How to Debug Tauri Desktop App Routes and Backend Fallback Mapping in Routa
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:
- Tauri launches the Rust server via
start_server(defined incrates/routa-server/src/lib.rs). - The server loads the
static_dir(typicallyout/desktopfrom the Next.js export) and constructs an Axum router. - When a request misses API routes, the
fallback_servicetriggers. - The
resolve_static_targetfunction maps URLs to placeholder files (e.g.,/workspace/default/kanban→workspace/__placeholder__/kanban.html). - If the URL contains a concrete workspace ID, the server rewrites the
__placeholder__token to the actual ID before returning the response. - 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 (lines 326-332), the server logs the static directory path and fallback configuration on startup.
Look for log entries resembling:
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, inspect the server initialization around line 1156. Ensure the ServerConfig struct includes the static_dir field pointing to the Next.js export:
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__.htmlout/desktop/workspace/__placeholder__/kanban.htmlout/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):
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 (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:
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:
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 (lines 143-156):
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/desktopresults 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_placeholderflag isfalsefor 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: Containsstart_server,resolve_static_target, and thefallback_servicelogic (lines 326-355).apps/desktop/src-tauri/src/main.rs: Desktop entry point that configuresstatic_dirand starts the embedded server (line 1156).src/client/utils/diagnostics.ts: Frontend utilitydesktopAwareFetchthat routes requests to the local HTTP server (lines 143-156).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. - Ensure
static_dirinapps/desktop/src-tauri/src/main.rspoints to the Next.jsout/desktopexport containing__placeholder__files. - Use
curlto test manual requests and verify workspace ID rewriting in the HTML response. - Validate the complete flow using
e2e/tauri-backend-check.spec.ts. - Confirm frontend requests route through
desktopAwareFetchto hit the local server at127.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 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 (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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →