BXUP Track progress & test yourself — free
Frontend & Mobile · Playbook · Foundation · ~4 min read
FRONTEND & MOBILE · PLAYBOOK · FOUNDATION

Generate Typed API Clients and MSW Mocks from the Schema

One pass turns an OpenAPI or GraphQL schema into TypeScript types, typed fetch hooks, and MSW mock handlers — with a CI drift check so the frontend can never quietly disagree with the backend. Hand-written API layers are where hallucinated fields hide; generated ones make them impossible.

Tier: Foundation · Workflow area: Component development · Tools: openapi-typescript, GraphQL Code Generator, MSW, Copilot

The API boundary is the classic source of frontend "works on my machine" bugs: a field renamed server-side, an optional the client assumed required, a mock that drifted from reality months ago. Generation from schema kills the whole category — and gives every AI tool in your repo a typed surface it cannot hallucinate against.

When to use

  • The backend publishes a schemaOpenAPI/Swagger JSON, a GraphQL endpoint, or a proto/contract file you can export.
  • Components are developed against mocksMSW handlers derived from the same schema keep local dev and tests honest.
  • AI writes your data-fetching codegenerated types turn "the model guessed the response shape" into a compile error.
  • Contract churn is realthe backend ships weekly and the frontend finds out via runtime errors.

When NOT to use

  • No schema exists and none is cominggeneration needs a source of truth; either write a minimal OpenAPI stub yourself (worth it) or skip this playbook.
  • A stable, tiny APItwo endpoints that haven't changed in a year don't justify the toolchain; a hand-typed client with a comment suffices.
  • The schema is a lieif the published spec doesn't match the deployed API, fix that first; generating from fiction automates the bug.

The recipe

  1. Generate types straight from the schema. For REST/OpenAPI:

    npm i -D openapi-typescript openapi-fetch
    npx openapi-typescript https://api.acme.dev/openapi.json \
      -o src/api/schema.d.ts
    

    For GraphQL, use codegen with the client preset:

    npm i -D @graphql-codegen/cli @graphql-codegen/client-preset
    npx graphql-codegen --config codegen.ts
    

    Commit the generated output. Generated-and-committed beats generated-on-install: diffs become visible in PRs, which is half the value.

  2. Build the thin typed client — generated, not hand-rolled. With openapi-fetch, the client is three lines and every call is checked against the schema:

    import createClient from 'openapi-fetch';
    import type { paths } from './schema';
    export const api = createClient<paths>({ baseUrl: '/api' });
    

    Now api.GET('/orders/{id}', { params: { path: { id } } }) fails to compile on a wrong path, param, or response usage. Then have AI generate the hook layer on top:

    Generate TanStack Query hooks for the orders endpoints in
    src/api/schema.d.ts using our api client: useOrders(filters),
    useOrder(id), useArchiveOrders(). Query keys follow our
    ['orders', ...] convention in src/api/keys.ts. Types come ONLY from
    the generated paths — no hand-written interfaces, no `any`.
    

    Good output imports paths/component schemas for every signature and contains zero locally-declared response types.

  3. Derive MSW handlers from the same schema. Ask for handlers that satisfy the generated types — the compiler then guarantees mocks match the contract:

    Write MSW v2 handlers for GET /orders, GET /orders/{id}, and
    POST /orders/archive in src/mocks/handlers/orders.ts. Response bodies
    must be typed with the generated schema types (satisfies
    OrdersResponse). Include: happy path with 25 realistic orders, a 422
    variant for archive with field errors, a 500 variant, and a
    delayed-3s variant for loading states. Export scenario helpers:
    orderScenarios.error500(), orderScenarios.slow().
    
    npm i -D msw && npx msw init public/ --save
    

    The satisfies clause is the trick: when the schema changes, stale mocks stop compiling instead of silently lying to your tests and stories (pb-06 stories should consume these same handlers).

  4. Add the drift check to CI. Regenerate against the live spec and fail on diff:

    npx openapi-typescript $OPENAPI_URL -o /tmp/schema.d.ts
    diff -q /tmp/schema.d.ts src/api/schema.d.ts \
      || { echo 'API schema drift — regenerate types'; exit 1; }
    npx tsc --noEmit
    

    The pairing matters: the diff catches the contract change; tsc shows exactly which components care. A red drift-check build is the backend telling you about a breaking change before users do.

  5. Point every AI tool at the generated layer. One instruction-file line closes the loop (pb-02):

    All API calls go through src/api (generated client + hooks). Never
    hand-write fetch calls, response interfaces, or mock data shapes —
    import them. If an endpoint is missing, say so; do not invent one.
    
Warning

The generated layer is only as honest as its source. Pin the spec URL to the environment you actually deploy against, and treat "we'll update the OpenAPI doc later" as the contract violation it is — a stale spec plus generated types produces confidently wrong code with green checkmarks.

Make it yours

  • REST + Reactas written: openapi-typescript + openapi-fetch + TanStack Query + MSW; for Axios shops, openapi-typescript types with a typed wrapper still beats untyped Axios.
  • GraphQL (any framework)@graphql-codegen/client-preset gives typed documents and useFragment masking; MSW mocks GraphQL operations natively (graphql.query('Orders', ...)); drift check compares the introspection result.
  • Vue / Angularsame generation layer; swap the hook layer for TanStack Query Vue (useQuery composables) or Angular's HttpClient services generated with ng-openapi-gen; MSW works in both ecosystems unchanged.
  • React Nativeidentical toolchain (MSW v2 supports RN via msw/native); mocks matter even more where device network debugging is painful.

Quality check

  • Zero hand-written response typesgrep src/features for interface .*Response and ad-hoc fetch calls; the count should be zero.
  • Mocks compile against schema typesdeliberately rename a field in a handler and confirm tsc fails.
  • Drift check fires in CItest it by pointing at an older spec version once; the build must go red with a readable message.
  • Scenario helpers used by tests and storieserror/slow variants are one import away, and pb-04's unhappy paths use them.
  • Regeneration is one commanddocumented in the repo (npm run api:gen), runnable by any teammate or agent.
Key Takeaway:

Generate types, client, and mocks from the schema in one pass and let CI police drift — the API boundary becomes a compile-time concern, and every AI tool in the repo inherits a typed surface it cannot hallucinate against.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in