API Contracts
@wavemap/api-contracts is the shared application boundary between Wavemap apps. It prevents the front end, back end,
i18n package, and tests from each inventing their own names for the same route, response, query shape, permission, or
domain code.
Use this page when adding or changing API routes, DTOs, API envelopes, query controls, page query presets, frontend route constants, roles, permissions, or domain-code surfaces that cross an app boundary.
Package Boundary
Section titled “Package Boundary”The package exports one public entrypoint:
export * from "./constants"export * from "./types"export * from "./utils"Its job is to publish shared contracts, not to implement app behavior.
| Surface | Examples |
|---|---|
| Constants | Backend route constants, frontend route constants, role codes, permission codes, pagination limits. |
| DTOs and types | Zod request/response schemas, inferred T... types, API envelopes, entity summary DTOs. |
| Query contracts | Filter groups, filter clauses, sort instructions, endpoint capabilities, saved-view state schemas. |
| Utilities | Permission derivation and query helper utilities that are contract-shaped rather than app runtime. |
The package depends on @wavemap/shared-utils for lower-level shared primitives. Apps depend on @wavemap/api-contracts
when they need the same route, data, or identity surface.
Route Contracts
Section titled “Route Contracts”Route identity is split into composable layers:
constants/routing-common.tsowns shared path segments, parameter tokens, and unversioned backend route shapes.constants/api/routes.v1.tsowns the versioned/api/v1/...route constants consumed by backend routes and frontend API callers.constants/frontend/routes.tsowns frontend route constants such as artist, event, auth, profile, and dashboard paths.
Use route constants instead of handwritten strings in app code when a route is shared or repeated. Parameter tokens such
as :artistID, :eventID, :mediaID, and :userID should come from the contract layer so path-building and tests do
not drift independently.
Public entity routes should treat route identifiers as public IDs at the app boundary, even when a historical parameter
name still says :artistID or similar. Backend handlers translate public IDs to internal database IDs before joins,
authorization checks, and mutations. See Public Entity Identifiers
for the durable publicId and slug convention.
AVAILABLE_BACKEND_ROUTES_V1 is the current route inventory for v1 backend routes. Add to it when a new route becomes
part of the shared backend surface.
DTOs And Envelopes
Section titled “DTOs And Envelopes”DTO files use Zod schemas for runtime validation and exported T... types for TypeScript consumers.
Public DTOs should not expose internal database IDs by default. Public entity responses should use publicId and, where
helpful for canonical links, slug. Keep internal IDs inside server-side persistence, authorization, joins, audit
targets, and explicitly internal/operational endpoints.
API responses should use shared envelopes:
APIEnvelopeOk(schema)for successful responses.APIErrorEnvelopefor structured API errors.TAPIEnvelopeOk<TData>andTAPIEnvelopeErrorfor static typing.
Error envelopes preserve both display fallback and stable identity:
messageremains a fallback display string.messageKeyidentifies localized backend user-facing copy when present.messageInterpolationValuescarries primitive ICU interpolation values for backend-localized errors.
When route behavior changes, keep the DTO, backend response validation, frontend parsing expectations, and tests aligned. Do not widen a DTO silently to match an accidental response shape.
Query And Saved-View Contracts
Section titled “Query And Saved-View Contracts”Queryable collection pages should share applied query shapes through @wavemap/api-contracts.
The core query DTOs are:
QueryFilterGroupDTOQueryFilterClauseDTOQuerySortInstructionDTO
Endpoint capabilities describe what a page or route can expose:
- Supported criteria.
- Optional query keys.
- Label keys for localized UI.
- Sort defaults.
- Filter data types and operations.
- Pagination defaults, allowed limits, and max limit.
The frontend can hold richer draft state while a user edits filters, sorts, search, or saved views. The apply boundary should normalize that draft state into the shared contract before it reaches URL state, API calls, or persisted page query presets.
Saved-view contracts live in the same package because they cross frontend state, backend persistence, and schema-version compatibility. Page-specific preset DTOs should pin the page key and state schema so each page can evolve deliberately.
Roles And Permissions
Section titled “Roles And Permissions”Role and permission identity belongs in @wavemap/api-contracts.
The package owns:
- Role code constants.
- Permission code constants.
rolesPermissionsMap.- Role and permission types derived from the available constants.
The back end uses this contract for route authorization, and the front end uses it for permission-aware rendering. Only role assignments are persisted; permissions are derived from roles at runtime.
Do not manually ask whether a user is an admin or root inside feature code when the question is really whether the
current user has a permission. Use the shared permission vocabulary and the established auth helpers.
When adding permissions, prefer the narrowest stable action vocabulary that matches the user intent. Avoid collapsing association, media, profile, and ownership/control semantics into one broad update permission just because the same role receives every permission today.
i18n And Seed Coupling
Section titled “i18n And Seed Coupling”Some API contract constants also feed i18n and database seed contracts.
Examples include:
- Domain codes that need localized labels.
- Page query preset page keys.
- Filter, sort, operation, and data-type codes.
- Link destination, status, and type code arrays.
When a source code value needs a localized label, keep the source array in code and let translation sheets render it. Translation files should not become the source of truth for allowed domain states.
When a code set also has backend base seeds, make sure seed files map over the canonical source array instead of
hardcoding row values. pnpm verify:i18n checks the registered i18n domain contracts that are stable enough for
continuous verification.
What Does Not Belong Here
Section titled “What Does Not Belong Here”Keep these outside @wavemap/api-contracts:
- Backend database queries, handlers, middleware, storage adapters, and email-sending behavior.
- Frontend React components, page workflow state, TanStack Query hooks, and UI rendering.
- Wavemap translation resources and runtime i18n loading.
- Generic helpers that do not describe an API, route, query, permission, or domain-code contract.
- Cloud resource names, runtime config, deploy workflow profiles, or operational command implementation.
If a helper is useful but not contract-shaped, consider @wavemap/shared-utils, an app-owned utility module, or
@wavemap/operations depending on the audience.
Change Checklist
Section titled “Change Checklist”When adding or changing a contract:
- Add or update constants and
as constarrays before deriving union types. - Add or update the Zod DTO when runtime validation matters.
- Export the contract through the relevant
index.tspath. - Update backend handlers, frontend API callers, and tests together.
- Update i18n domain contracts when a code value needs localized display.
- Update seed-source contracts when a backend base seed depends on the code set.
- Add tests for route inventories, DTO constraints, preset schema behavior, or helper behavior when the contract is non-obvious.
Prefer exact, deliberate changes. A shared contract can spread quickly through both apps, so avoid broad “while here” reshaping.
Verification
Section titled “Verification”Useful commands:
pnpm -F @wavemap/api-contracts testpnpm -F @wavemap/api-contracts typecheckpnpm verify:i18nUse targeted backend, frontend, or docs checks as needed when a contract change affects app behavior or documentation.
Related Pages
Section titled “Related Pages”- Monorepo Map for where
@wavemap/api-contractssits in the workspace. - Feature Slice Workflow for moving a cross-app change through contracts, apps, tests, and docs.
- Query Controls And Browsing State for query-control patterns.
- Domain Relationships for relationship patterns.
- Authentication And Authorization for role and permission usage.
- Public Entity Identifiers for public ID, slug, URL, and DTO identity conventions.
- i18n Guardrails for domain-code translation and seed-source checks.