The contract is generated, not declared. You never write an interface for your endpoint.
mercurjs codegen reads the route’s handler and validators and emits the Routes type the client consumes, so the panel call site breaks at compile time the moment the backend changes. This loop is also what makes Mercur projects reliable targets for AI agents. See Building with AI.What you’ll build
AGET /vendor/sales-summary endpoint returns the seller’s order count. You call it from a custom vendor portal page via client.vendor.salesSummary.query() with inferred types.
Build the loop
1
Create the route
API routes follow Medusa’s file conventions inside your API package. The URL path mirrors the directory path.Routes under
packages/api/src/api/vendor/sales-summary/route.ts
src/api/vendor/* run behind the vendor authentication middleware, so req.auth_context identifies the calling seller. Use src/api/admin/* for operator endpoints and src/api/store/* for public storefront endpoints.2
Regenerate the route map
Run codegen to scan your route files.Codegen rewrites the generated This file ships with the starter template, so you don’t need to touch it. After codegen,
Terminal
Routes type that your panel apps already import:apps/vendor/src/lib/client.ts
client.vendor.salesSummary simply exists, typed.3
Call it from a panel page
Drop a page into the vendor app and call the endpoint through the client. Route segments map to camelCase properties, and the terminal call chooses the HTTP method:
query (GET), mutate (POST), or delete (DELETE).apps/vendor/src/routes/sales-summary/page.tsx
InferClientOutput extracts the response type straight from the client method. Change the route’s response shape, rerun codegen, and this component stops compiling until you update it.Verify
Start the project and log into the vendor portal.Terminal
- Sidebar entry: Sales summary appears in the sidebar (the
configexport registered it), and the page shows the order count. - Auth guard:
curl http://localhost:9000/vendor/sales-summarywithout a token returns an authentication error. The vendor middleware guards your route. - Generated contract: change the route to return
{ count: ... }instead of{ order_count: ... }, rerun codegen, and confirm the page fails to type-check. That is the generated contract doing its job. Revert after.
FAQ
How do path parameters work in the client?
How do path parameters work in the client?
Use
$-prefixed segments: a route at src/api/vendor/things/[id]/route.ts is called as client.vendor.things.$id.query({ $id: "thing_123" }). The $id key is threaded into the URL path, and everything else in the object becomes query params (GET) or the JSON body (POST).How do I handle errors from the client?
How do I handle errors from the client?
Failed requests throw
ClientError from @mercurjs/client, carrying status, statusText, and the backend’s message. Wrap calls in try/catch or let TanStack Query surface the error.Where do request validation schemas go?
Where do request validation schemas go?
Follow Medusa conventions: a
validators.ts next to the route with a Zod schema, wired through the route’s middleware. Codegen reads validators too, so the client’s input type reflects them.Next steps
API conventions
Authentication, pagination, field selection, and error shapes.
Extend a workflow
Put multi-step business logic behind your endpoint with rollback support.