How to Configure Desktop-Aware Fetch for Tauri Runtime (127.0.0.1:3210) in Routa
Routa's desktopAwareFetch helper automatically detects Tauri desktop mode and rewrites API calls to http://127.0.0.1:3210 when running inside the Tauri webview, while using standard HTTP endpoints for web deployments.
When building cross-platform applications with phodal/routa, your frontend must handle two distinct runtime environments: a traditional web app served over HTTP/HTTPS and a desktop application packaged with Tauri. Since Tauri serves the UI from the tauri:// scheme without an attached backend server, standard /api/* requests fail unless you configure desktop-aware fetch to route traffic to the embedded Rust server.
Understanding the Desktop-Aware Fetch Architecture
The desktopAwareFetch function in src/client/utils/diagnostics.ts serves as a transparent proxy that determines the correct API base URL based on the current runtime context.
Runtime Detection Mechanisms
Routa identifies Tauri environments through isTauriRuntime(), which checks for the presence of window.__TAURI__ or window.__TAURI_INTERNALS__ globals. Alternatively, you can force desktop mode by appending ?runtime=tauri to the URL, which persists a marker in localStorage via hasPersistedTauriMarker().
For static builds, isDesktopStaticRuntime() performs an additional protocol check. It returns true only when running inside Tauri and the page protocol is not http: or https: (confirming the tauri:// scheme). This distinction prevents false positives during regular web development.
Embedded Server Address Resolution
When isDesktopStaticRuntime() returns true, getDesktopApiBaseUrl() returns the default embedded server address http://127.0.0.1:3210. This port is defined by the constant DESKTOP_API_DEFAULT_PORT in src/client/utils/diagnostics.ts. If you supply a custom backend URL through configuration, that value overrides the default localhost endpoint.
Configuring the Backend URL
Routa supports three methods for overriding the default 127.0.0.1:3210 endpoint, checked in priority order by getConfiguredBackendBaseUrl() in src/client/config/backend.ts:
Query String Parameter
Append ?backend=https://my-api.example.com to the application URL. The helper reads this value once, stores it in localStorage, and uses it for the remainder of the session.
Local Storage Programmatically set the backend URL for persistent configuration:
localStorage.setItem('routa.backendBaseUrl', 'https://api.production.com');
Environment Variable
For static builds, set NEXT_PUBLIC_ROUTA_BACKEND_BASE_URL at build time. The readFromEnv() function reads this during initialization.
The resolution logic follows this cascade: query parameter → localStorage → environment variable → default localhost:3210.
Implementing Desktop-Aware Fetch in Your Code
The desktopAwareFetch function in src/client/utils/diagnostics.ts automatically handles URL resolution and request building:
// src/client/utils/diagnostics.ts
export function desktopAwareFetch(
path: string,
options?: RequestInit,
): Promise<Response> {
const base = getDesktopApiBaseUrl(); // "" for web, or http://127.0.0.1:3210
return fetch(resolveApiPath(path, base), options);
}
The resolveApiPath helper (from src/client/config/backend.ts) ensures the final URL always includes the /api prefix. When base is an empty string (standard web mode), requests target the current origin. In Tauri static mode, requests route to http://127.0.0.1:3210/api/... or your configured custom URL.
Basic Usage Example
import { desktopAwareFetch } from '@/client/utils/diagnostics';
export async function loadWorkspaceData(workspaceId: string) {
// Works identically in browser and Tauri desktop
const resp = await desktopAwareFetch(`/api/workspaces/${workspaceId}`);
if (!resp.ok) throw new Error('Failed to load workspace');
return resp.json();
}
Environment-Based Configuration
Configure a remote API for your desktop build without code changes:
# .env.local
NEXT_PUBLIC_ROUTA_BACKEND_BASE_URL=https://api.mycompany.com
// Automatically uses the environment variable
const resp = await desktopAwareFetch('/api/tasks');
Dynamic Backend Override
For one-off testing against staging servers, launch the Tauri app with a query parameter:
tauri://localhost?backend=https://staging.api.example.com
Subsequent API calls will target the staging environment:
await desktopAwareFetch('/api/projects');
// Resolves to: https://staging.api.example.com/api/projects
Rust Server and Error Handling
On the backend, crates/routa-server/src/lib.rs starts the embedded HTTP server on port 3210 with appropriate CORS settings to accept requests from the Tauri webview.
If the frontend attempts API calls while in static desktop mode but getDesktopApiBaseUrl() returns an empty string (indicating the Rust server failed to start), desktopStaticApiError() throws a descriptive error. This safeguard prevents silent failures when the desktop backend is unavailable during development or production.
Enable debug logging by setting window.__ROUTA_DEBUG__ = true or adding routa.debug=1 to localStorage to trace runtime detection logic via the logRuntime utility.
Summary
desktopAwareFetchinsrc/client/utils/diagnostics.tstransparently routes API calls tohttp://127.0.0.1:3210when running inside Tauri'stauri://scheme.- Runtime detection relies on
window.__TAURI__globals or the?runtime=tauriquery parameter withlocalStoragepersistence. - Backend overrides support three sources: query strings,
localStorage(routa.backendBaseUrl), and theNEXT_PUBLIC_ROUTA_BACKEND_BASE_URLenvironment variable. - Path resolution via
resolveApiPathinsrc/client/config/backend.tsguarantees consistent/apiprefixing regardless of runtime. - Error handling prevents API calls when the desktop server is unreachable, protecting against misconfiguration in packaged builds.
Frequently Asked Questions
How does Routa detect whether it's running inside Tauri?
Routa's isTauriRuntime() function checks for window.__TAURI__ or window.__TAURI_INTERNALS__ globals. Additionally, if you load the app with ?runtime=tauri, the system stores a persistent marker in localStorage via hasPersistedTauriMarker(), ensuring desktop mode persists across reloads even if the Tauri globals are temporarily unavailable.
Can I use a custom API URL instead of localhost:3210 for the desktop app?
Yes. You can set NEXT_PUBLIC_ROUTA_BACKEND_BASE_URL during build time, use localStorage.setItem('routa.backendBaseUrl', 'https://api.example.com'), or append ?backend=https://api.example.com to the URL. The getConfiguredBackendBaseUrl() function reads these in that priority order, allowing the desktop app to communicate with remote APIs instead of the embedded Rust server.
What happens if the Rust server on port 3210 isn't running?
If isDesktopStaticRuntime() returns true but getDesktopApiBaseUrl() returns an empty string (indicating no backend URL is configured or the default server is unreachable), the desktopStaticApiError() function throws an error with a helpful message. This prevents the frontend from attempting requests to non-existent endpoints and alerts you that the desktop backend needs to be started.
Do I need to change my fetch calls when building for web vs. desktop?
No. The desktopAwareFetch utility abstracts runtime differences. Use it consistently across your codebase—it automatically uses the current origin for web deployments and switches to http://127.0.0.1:3210 (or your configured URL) when running inside Tauri. The helper is used throughout the codebase, including in utilities like src/client/utils/repo-validation.ts.
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 →