How Sushi SaaS Routes Requests
See what the middleware does for users and operators, why organization selection stays visible in the URL, and which routing choices you can change.
Sushi SaaS middleware handles request-level context, not authentication or business authorization. Its three jobs—locale routing, request correlation, and organization transport—produce user-visible behavior that you should choose deliberately.
What users experience
- A page resolves to the appropriate localized route through
next-intl. - Two tabs can stay on two different workspaces because the selected organization is represented by
?org=<slug>, not only by session state. - A shared account link shows which workspace it targets.
Operators receive an x-request-id on every response, allowing a support report to be matched to structured logs. API requests skip locale negotiation but receive the same request and organization context.
The implementation is src/middleware.ts at 2a1a04a.
What the middleware actually trusts
For a page request, organization context comes from the URL query parameter. A caller-supplied organization header is removed. For an API request, the shared client may send x-organization-slug; a URL value takes precedence when both exist.
Neither value authorizes access. src/services/authz.ts still proves that the signed-in user belongs to the requested organization and refuses ambiguous multi-organization API requests. Middleware only validates that context is safe to transport.
Incoming request IDs are accepted only when they match a bounded, log-safe format; otherwise the application creates a new UUID. The ID is forwarded to the route and copied to the response.
Choices you can make
Locale routing
Keep next-intl middleware when locale-prefixed pages and detection are part of the product. For a single-language product, remove the middleware branch together with localized routes, message catalogs, and tests. Leaving half of the locale contract usually produces redirects and links that disagree.
Workspace selection
The shipped query-string model is tab-local and easy to add to existing account URLs. Path-based tenancy such as /acme/account gives the workspace stronger URL hierarchy but requires changing link builders, callbacks, matchers, and context resolution. Session-only selection looks simpler, but two tabs can overwrite one shared active organization; use it only if that behavior is acceptable.
Request tracing
Keep correlation IDs for almost every deployed product. If your proxy or observability provider owns trace IDs, map its trusted value into the logger contract or carry both values. Do not accept unbounded caller text into logs.
API context
Requiring an explicit organization for multi-workspace API users prevents an operation from silently landing in the last active tenant. You can simplify this only if the API is provably single-tenant.
When to add middleware logic
Add a concern here only when it must run before routing and can remain fast and edge-compatible. Put membership checks, database-dependent authorization, billing decisions, and other business rules in services instead.
After changing the boundary, test localized pages, /api routes, static assets, request-ID forwarding, malformed context, and two-tab workspace switching. Then review Architecture and Error Contracts and Organizations and Teams.