Skip to content
Docs

Command palette

Jump to a page or view, or find a todo by name.

Changelog

oRPC conventions

Share Zod contracts and typed clients while keeping authorization on the server.

Markdown

Procedure selection

ProcedureUse
publicProcedurePublic boot or health data, with captured-scope checks when supplied
protectedProcedureAn authenticated User's personal data
orgProcedureShared data in the membership-checked Active Organization
orgProcedure.use(requireOrgRole(...))The same, limited to Members with one of the listed Org roles; available, not applied to any procedure yet
adminProcedureGlobal reads requiring the current platform admin role
adminWriteProcedureAdmin writes through Better Auth's audited capability

Each feature lives in packages/api/src/features/<feature>/: the leaf contract (<feature>.contract.ts), the procedures (<feature>.procedures.ts), the service and its errors. Register the procedures in platform/rpc/app-router.ts. Browser and mobile clients use the type-only api/client export and the feature's contract subpath (api/<feature>/contract). The backend conventions give the full shape.

Add an operation

  1. Define input and output schemas, including bounded pagination and text limits.
  2. Choose the procedure according to the resource owner.
  3. Apply Organization scope in every relevant query and mutation.
  4. Keep the mutation and Audit Event in one transaction when the operation is audited.
  5. Add any cross-package schema export explicitly, then consume it through a hook.

Pagination

Each list endpoint uses one strategy. Choose it by what the interface needs:

UseWhenShapeExamples
OffsetThe interface shows a total, page numbers or facet counts over a bounded, slowly changing setpage from 1 and pageSize capped at 100; the output carries total and pageCounttodo.list, the admin audit table and user list
CursorThe list grows without bound or changes while it is read (feeds, timelines, "load more")an opaque cursor of the stable sort key, such as { createdAt, id }, plus limit capped at 100; the output carries the next cursorActivity Timeline, integration delivery activity, Document Text pages
Bounded windowThe server caps the result by designa plain array or a fixed window, no pagingBoard lanes (limitPerLane), the Calendar date window, document search

Rules for every list:

  • Every paginated query has a total ORDER BY with a unique tie-breaker (id), so equal sort values cannot repeat or skip rows.
  • Every limit has a maximum in the contract. No endpoint returns an unbounded page.
  • A cursor is opaque to clients. A malformed cursor is INPUT_VALIDATION (HTTP 400), never a 500.
  • One endpoint offers one strategy; it does not accept both page and cursor.

The HTTP endpoint is /api/rpc. The session-authenticated OpenAPI reference is at /api/rpc/api-reference, with its specification at /api/rpc/api-reference/spec.json.

Client state

TanStack Query owns server data, query keys and invalidation. Forms use TanStack Form. URL state expresses Todo views, filters and sorting. Components render the hook's state; they do not recreate authentication or authorization decisions.

Validate external payloads at the boundary. Use the shared error vocabulary and translated resolver rather than returning internal exceptions. Do not create a parallel REST API for a domain operation already represented by oRPC.

Key files

Edit on GitHubDocumentation is in English.