The generated API contract and the typed SDK that apps/web uses to talk to apps/backend.
A single, type-safe boundary between the frontend and the backend. It holds the artifacts generated from the backend (OpenAPI schema, Supabase database types) and a hand-written SDK wrapper over them. The full generation chain is described in docs/architecture/api-contract.md.
Entry point src/index.ts re-exports:
ApiError,PaginatedResponse,PaginatedRequestParams(from./types);getErrorMessage(from./utils/getErrorMessage);- the generated schema (
./schemas/api) and public database types (./database.types.public); - the SDK (
./sdk):createSdk,createApiClient.
| Path | What it is |
|---|---|
src/schemas/ |
openapi.json + api.ts — generated, never edit by hand |
src/sdk/ |
hand-written wrapper (createSdk, createApiClient) |
src/database.types* |
generated Supabase database types |
src/types.ts |
shared response shapes (ApiError, PaginatedResponse) |
src/utils/ |
getErrorMessage |
createSdk(supabase, options) attaches the caller's access token to every request. It defaults to a
relative /api base URL (which only resolves in the browser), so SSR callers must pass an absolute
baseUrl (see ApiClientOptions).
@supabase/supabase-js— the Supabase client.
apps/web— consumes the SDK for backend calls.@languages-learner/data-source— builds its queries on top of the SDK.
Produced from apps/backend (schema and type generation), but does not depend on it at runtime.
-
Do not edit the generated files by hand.
openapi.json,api.ts,database.types.tsare eslint-ignored;lint:fixwill not reformat them. -
Regeneration is run from the backend, not from here:
pnpm --filter app-backend generate:api-schemas # DTO → openapi.json → api.ts pnpm --filter app-backend generate-types # Supabase → database.types.ts
Commit the regenerated files together with the backend change — otherwise
apps/webtypechecks against a stale contract.
- Scripts and how to run: README
- The full contract: docs/architecture/api-contract.md
- Backend:
apps/backend/README.md