TrademarkTrademark
Ctrl k
Search docs...
Guides

Error handling

One error contract across every API route, and how the UI reads it.
Every route handler in this template answers failures with the same shape: A single shape means the client never has to guess how a given endpoint reports trouble, and messages can be surfaced to users without a per-route translation layer. src/lib/errors.ts exports ApiError and the helpers around it. Throw ApiError for anything the caller should see: handleApiError returns the ApiError verbatim. Anything else becomes a 500 with a generic message, and the original is logged server-side — an unexpected exception must never leak a stack trace or a query fragment to the browser. On the client, apiErrorMessage in src/lib/api-error-message.ts pulls the message out of a parsed body, falling back to a readable default when the body is missing or malformed. It also accepts the older { error: string } shape, so a route that has not been migrated yet still produces something readable: src/app/error.tsx catches render-time errors for the whole app and src/app/not-found.tsx handles unknown routes. Both are shell files, so every composition has them. When the Upstash variables are set, src/proxy.ts rate limits the mutating routes and answers over-limit requests with the same error shape, code rate_limited. When they are not set, the proxy passes requests through. Callers cannot tell the difference from the contract's point of view, which is what lets the same UI code run in both modes.
Last updated August 27, 2026
Edit this page on GitHub