Skip to main content

Add a Backend Route

This guide shows how to add a backend route to your module manifest.

Use this when a backend endpoint needs explicit manifest metadata, schema references, or backend-inspect visibility. For the default full app path, keep document pages in src/frontend/pages/** and keep form-handling logic in src/backend/module.ts, then add manifest-backed route entries as those backend surfaces stabilize.

Prerequisites

  • A Webstir workspace initialized via webstir init ....
  • Backend source at src/backend/.

Steps

  1. Add a route with defaults:
    • webstir add-route users --workspace "$PWD"
    • Writes GET /api/users to webstir.moduleManifest.routes in package.json.
  2. Specify method and path explicitly when needed:
    • webstir add-route users --workspace "$PWD" --method POST --path /api/users
  3. Attach metadata so documentation and tooling stay in sync:
    • webstir add-route accounts --workspace "$PWD" --summary "List accounts" --description "Returns the current tenant accounts" --tags accounts,api
    • Schema references follow the kind:name@source format described in the CLI reference. Example:
      webstir add-route accounts --workspace "$PWD" --params-schema zod:AccountParams@src/shared/contracts/accounts.ts --response-schema zod:AccountList@src/shared/contracts/accounts.ts
  4. Declare HTML-first route primitives when the route is a server-handled form or fragment update:
    • webstir add-route sign-in --workspace "$PWD" --method POST --path /api/sign-in --interaction mutation --session required --session-write --form-urlencoded --csrf
    • webstir add-route account-panel --workspace "$PWD" --method POST --path /api/account/panel --interaction mutation --fragment-target account-panel --fragment-mode replace

Wire the handler

After writing the manifest entry, implement the handler in src/backend/module.ts. The template already exports a manifest and route list—extend it with your logic:

// src/backend/module.ts
import { createDatabaseClient } from './db/connection';
// RouteContext is already defined earlier in the scaffold file.

const routes = [
{
definition: {
name: 'listAccounts',
method: 'GET',
path: '/api/accounts',
summary: 'Return the current accounts',
description: 'Demonstrates auth + db helpers'
},
handler: async (ctx: RouteContext) => {
if (!ctx.auth?.userId) {
return { status: 401, errors: [{ code: 'auth', message: 'Sign in required' }] };
}

const db = await createDatabaseClient();
const accounts = await db.query('select id, email from accounts where owner_id = ?', [ctx.auth.userId]);
await db.close();

return { status: 200, body: { accounts } };
}
}
];

export const module = {
manifest: {
contractVersion: '1.0.0',
name: '@demo/backend',
version: '0.1.0',
kind: 'backend',
capabilities: ['http'],
routes: routes.map((route) => route.definition)
},
routes
};
  • The scaffolded RouteContext exposes params, query, body, auth, env, logger, and now() helpers.
  • The backend provider auto-loads build/backend/module.js, logs the manifest summary, and mounts every exported route. No manual registration is required when you edit src/backend/module.ts.

Verify the manifest

  • Run webstir build --workspace "$PWD" (or webstir watch --workspace "$PWD" in an api or full workspace).
  • Print the manifest summary without starting the dev service:
    webstir backend-inspect --workspace "$PWD"
  • The inspect command rebuilds the backend and lists capabilities, routes, and jobs so you can verify manifest metadata before sharing it with collaborators or publishing packages.

Notes

  • The CLI prevents duplicate entries for the same method+path.
  • The backend provider also validates the manifest and emits diagnostics on duplicates.
  • Route handler scaffolding is optional and intended as a starting point; adapt it to your server style.
  • --session required is the current manifest-level way to mark auth-gated routes in the default server-first lane.
  • --form-urlencoded, --csrf, and --fragment-* only declare the contract. Your handler in src/backend/module.ts still needs to implement the actual form, redirect, or fragment behavior.
  • Schema references can point at Zod files (zod:Type@path/to/file.ts), JSON schema, or ts-rest routers. They only record metadata; the backend provider enforces the manifest at build time.

See Also

  • CLI reference: ../reference/cli.md#add-route
  • Backend provider: ../explanations/solution.md (manifest ingestion)