# How to Debug Tauri Desktop App Routes and Backend Fallback Mapping in Routa

> Debug Tauri desktop app routes and backend fallback mapping in Routa. Verify server logs, static_dir path, and workspace ID rewriting for seamless SPA fallback.

- Repository: [Fengda Huang/routa](https://github.com/phodal/routa)
- Tags: how-to-guide
- Published: 2026-05-26

---

**To debug Tauri desktop app routes and backend fallback mapping in Routa, verify that the embedded Rust/Axum server logs show the SPA fallback resolution, confirm the `static_dir` path points to the Next.js static export containing `__placeholder__` files, and test that workspace IDs are correctly rewritten when serving HTML responses.**

Routa utilizes a dual-backend architecture where a Next.js static export runs alongside an embedded Rust HTTP server inside a Tauri desktop container. When you debug Tauri desktop app routes and backend fallback mapping in Routa, you are essentially tracing how the Rust server intercepts requests and rewrites placeholder files to serve dynamic content, since Tauri's native protocol lacks built-in SPA fallback support.

## Architecture Overview: Why SPA Fallback is Necessary

Unlike web deployments, the Tauri desktop shell does not provide native SPA fallback handling for client-side routing. Instead, **Routa** delegates this responsibility to an embedded Axum server that starts alongside the desktop application.

The fallback flow operates as follows:

1. Tauri launches the Rust server via `start_server` (defined in [`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs)).
2. The server loads the `static_dir` (typically `out/desktop` from the Next.js export) and constructs an Axum router.
3. When a request misses API routes, the `fallback_service` triggers.
4. The `resolve_static_target` function maps URLs to placeholder files (e.g., `/workspace/default/kanban` → [`workspace/__placeholder__/kanban.html`](https://github.com/phodal/routa/blob/main/workspace/__placeholder__/kanban.html)).
5. If the URL contains a concrete workspace ID, the server rewrites the `__placeholder__` token to the actual ID before returning the response.
6. For React Server Components (RSC), the server sets the content-type to `text/x-component; charset=utf-8`.

## Step-by-Step Debugging Guide

### 1. Check Server Startup Logs

Verify the server initialized the fallback service correctly. In [`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs) (lines 326-332), the server logs the static directory path and fallback configuration on startup.

Look for log entries resembling:

```text
SPA fallback: /workspace/default -> /path/to/out/desktop/workspace/__placeholder__.html (rsc=false)

```

If these logs are absent, the server may not have received the `static_dir` configuration.

### 2. Validate the static_dir Configuration

In [`apps/desktop/src-tauri/src/main.rs`](https://github.com/phodal/routa/blob/main/apps/desktop/src-tauri/src/main.rs), inspect the server initialization around line 1156. Ensure the `ServerConfig` struct includes the `static_dir` field pointing to the Next.js export:

```rust
let config = ServerConfig {
    host: "127.0.0.1".into(),
    port: 3210,
    db_path: "routa.db".into(),
    static_dir: Some("out/desktop".into()), // Critical for SPA fallback
};

let _addr = routa_server::start_server(config).await?;

```

If `static_dir` is `None`, the fallback service is disabled and all non-file requests will return 404.

### 3. Inspect Placeholder Files

Verify that the Next.js static export generated the expected placeholder files. The server expects to find files like:

- [`out/desktop/workspace/__placeholder__.html`](https://github.com/phodal/routa/blob/main/out/desktop/workspace/__placeholder__.html)
- [`out/desktop/workspace/__placeholder__/kanban.html`](https://github.com/phodal/routa/blob/main/out/desktop/workspace/__placeholder__/kanban.html)
- [`out/desktop/workspace/__placeholder__/sessions/__placeholder__.html`](https://github.com/phodal/routa/blob/main/out/desktop/workspace/__placeholder__/sessions/__placeholder__.html)

If these files are missing, rerun the Next.js static export (`npm run build && npm run export`) before building the Tauri app.

### 4. Reproduce Requests Manually

Test the fallback logic directly using `curl` against the local server port (default `3210`):

```bash
curl http://127.0.0.1:3210/workspace/your-workspace-id/kanban

```

Examine the response HTML. It should contain your actual workspace ID, not the string `__placeholder__`. If you receive a 404, the placeholder file is likely missing or the `static_dir` path is incorrect.

### 5. Check the Rewrite Logic

If the placeholder file is served but the workspace ID is not injected, inspect the `fallback_service` implementation in [`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs) (lines 345-355). The code extracts `workspace_segments` and conditionally rewrites content based on `should_rewrite_workspace_placeholder`.

Add temporary debug logging to verify the extracted ID:

```rust
tracing::debug!("Rewriting placeholder with actual workspace ID: {}", actual_workspace_id);

```

Note that rewrite is skipped for session-specific routes where the placeholder is already per-session.

### 6. Run the E2E Regression Suite

Routa includes end-to-end tests specifically for desktop routing. Execute:

```bash
pnpm test:e2e e2e/tauri-backend-check.spec.ts

```

This test validates that all SPA routes return HTML containing the correct workspace ID, serving as the official regression guard for the fallback mapping.

### 7. Verify Client-Side Request Routing

Ensure the frontend is actually calling the Rust server and not attempting to load files via Tauri's asset protocol. All frontend code should use `desktopAwareFetch` from [`src/client/utils/diagnostics.ts`](https://github.com/phodal/routa/blob/main/src/client/utils/diagnostics.ts) (lines 143-156):

```typescript
export function desktopAwareFetch(input: RequestInfo, init?: RequestInit) {
  const url = typeof input === "string" ? input : input.url;
  const full = isDesktop ? `http://127.0.0.1:3210${url}` : url;
  return fetch(full, init);
}

```

If `isDesktop` evaluates incorrectly or the port is wrong, requests will bypass the fallback logic entirely.

## Common Pitfalls

- **Missing static export**: Building the Tauri app without first generating `out/desktop` results in a server that cannot serve placeholders. Always export the Next.js static site before the desktop build.
- **Incorrect static_dir path**: The path must be relative to the Tauri binary's working directory. Absolute paths or incorrect relative paths prevent the fallback service from locating placeholder files.
- **Workspace rewrite disabled**: The `should_rewrite_workspace_placeholder` flag is `false` for session-specific routes. If debugging a session route, expect the placeholder to remain unchanged in the filename (although the content should still load).

## Key Source Files

- **[`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs)**: Contains `start_server`, `resolve_static_target`, and the `fallback_service` logic (lines 326-355).
- **[`apps/desktop/src-tauri/src/main.rs`](https://github.com/phodal/routa/blob/main/apps/desktop/src-tauri/src/main.rs)**: Desktop entry point that configures `static_dir` and starts the embedded server (line 1156).
- **[`src/client/utils/diagnostics.ts`](https://github.com/phodal/routa/blob/main/src/client/utils/diagnostics.ts)**: Frontend utility `desktopAwareFetch` that routes requests to the local HTTP server (lines 143-156).
- **[`e2e/tauri-backend-check.spec.ts`](https://github.com/phodal/routa/blob/main/e2e/tauri-backend-check.spec.ts)**: End-to-end test suite validating the fallback mapping for all desktop routes.

## Summary

- **Routa** handles SPA fallback through an embedded Rust/Axum server, not Tauri's native protocol.
- Debug by checking server logs for "SPA fallback" entries in [`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs).
- Ensure `static_dir` in [`apps/desktop/src-tauri/src/main.rs`](https://github.com/phodal/routa/blob/main/apps/desktop/src-tauri/src/main.rs) points to the Next.js `out/desktop` export containing `__placeholder__` files.
- Use `curl` to test manual requests and verify workspace ID rewriting in the HTML response.
- Validate the complete flow using [`e2e/tauri-backend-check.spec.ts`](https://github.com/phodal/routa/blob/main/e2e/tauri-backend-check.spec.ts).
- Confirm frontend requests route through `desktopAwareFetch` to hit the local server at `127.0.0.1:3210`.

## Frequently Asked Questions

### Why does my Tauri app show "Not found" for dynamic routes?

This occurs when the embedded Rust server cannot locate the placeholder files. Verify that `static_dir` is configured in [`apps/desktop/src-tauri/src/main.rs`](https://github.com/phodal/routa/blob/main/apps/desktop/src-tauri/src/main.rs) and that the `out/desktop` directory exists with the expected `__placeholder__` subdirectory structure. Without these files, the `fallback_service` cannot generate a valid response.

### How do I verify the placeholder rewrite is working correctly?

Send a manual HTTP request to `http://127.0.0.1:3210/workspace/<your-id>/kanban` using curl. Inspect the returned HTML for your specific workspace ID. If the response contains `__placeholder__` instead of your ID, or if you receive a 404, the rewrite logic in [`crates/routa-server/src/lib.rs`](https://github.com/phodal/routa/blob/main/crates/routa-server/src/lib.rs) (lines 345-355) is not executing properly.

### What is the purpose of the `__placeholder__` token in Routa?

The `__placeholder__` token allows the Next.js static export to generate generic HTML files for dynamic routes at build time. At runtime, the Rust server replaces this token with the actual workspace or session ID from the request URL, enabling a static export to serve dynamic content without server-side rendering.

### Where can I find the E2E tests for desktop routing?

The end-to-end tests are located at [`e2e/tauri-backend-check.spec.ts`](https://github.com/phodal/routa/blob/main/e2e/tauri-backend-check.spec.ts) in the repository root. These tests programmatically verify that all SPA routes return the correct HTML with proper workspace ID injection, providing an automated way to confirm the fallback mapping works after build changes.