— The stack

Generate a TypeScript client from FastAPI without hiding contract drift

See an executed OpenAPI-to-TypeScript field-rename experiment, with pinned generation, actual TS2339 drift detection and runtime validation limits.

4 min readUpdated 2026-11-05By AptixLabs studio
OpenAPI artifact: Versioned schema and IDs; Pinned generator: Deterministic declarations; Consumer compile: Breaking field detected; Runtime validation: Unknown JSON still needs checksAptixLabs · 2026-11-05

Original explanatory figure of a synthetic or proposed design; not a deployed product screenshot. View full-size diagram

Generate TypeScript contracts from a versioned FastAPI OpenAPI artifact, pin the generator and fail the build when regeneration exposes incompatible consumer code. Keep generated files separate from hand-written authentication and runtime validation. Types help only when the schema is current and the consumer uses them; they do not verify an arbitrary JSON response at runtime.

Freeze the input before generating

Export OpenAPI from the intended application revision in a controlled build or test process. Record the schema digest, framework version, generator version and generation command. Production documentation can be disabled without preventing a controlled schema export; do not enable a public schema endpoint solely for code generation. Review stable unique operation IDs so routine route refactoring does not unexpectedly rename client methods.

FastAPI's client-generation guide explains how OpenAPI and operation IDs feed TypeScript generation. Choose a generator according to the needed output: some produce types, others produce runtime SDK code. The executed experiment here uses openapi-typescript for runtime-free declarations, not a full generated HTTP SDK. Authentication injection and response validation remain hand-written integration decisions.

FastAPI: OpenAPI client generation and operation IDs

openapi-typescript: generated declarations and CLI usage

Executed synthetic breaking change

Two local OpenAPI 3.1 schemas expose GET /profile with operationId getProfile. Version one requires displayName; version two replaces it with name. We generated declarations using openapi-typescript 7.13.0 on Node v22.16.0, then compiled the same old consumer expression against both schemas with TypeScript 5.9.3. The v1 consumer had no diagnostics. The v2 consumer failed with TS2339 because displayName no longer exists.

import type { operations } from './schema-v1';
type Profile = operations['getProfile']['responses'][200]
  ['content']['application/json'];
declare const result: Profile;
const label: string = result.displayName;
// Switch to schema-v2: TS2339 for displayName.

The snippet illustrates the generated indexed type; the saved consumer files contain the exact compilable formatting. The experiment used synthetic local schemas rather than importing the private application's FastAPI process. It proves the generator-and-consumer drift mechanism for one field rename, not live endpoint behavior or compatibility with every installed app version.

Actual isolated generation and compilation result
InputConsumer expressionObserved result
v1: displayName requiredresult.displayNameNo diagnostics
v2: name replaces displayNameresult.displayNameTS2339
Unchecked runtime v2 JSONpayload.displayNameundefined
Execution boundaryLocal schemas and declarationsNo live FastAPI or HTTP SDK

Regenerate in a reviewable gate

Keep schema generation deterministic and run it with the lockfile's tools. Compare the regenerated output with the committed artifact, then type-check consumers. Do not hand-edit a generated field to make the diff disappear; fix the server contract, regenerate or intentionally migrate the caller. Review changes in requiredness, nullability, enums, status codes and security definitions as well as field names.

If the service supports older clients, compilation of the newest frontend is insufficient. Maintain representative old-consumer fixtures or a compatibility contract. A server can emit a new shape while a previously released mobile client still expects the old one. Versioning and deprecation decisions remain product responsibilities even when the generator faithfully reflects the newest schema.

Validate unknown runtime data separately

The local experiment also parsed a synthetic v2 payload in JavaScript and read the old displayName field without validation; the result was undefined. No runtime type check appeared simply because declarations existed. TypeScript documents that its type annotations are erased. Validate at the response boundary when the application needs runtime assurance, and apply domain checks for identifiers and business rules after shape validation.

TypeScript: types are erased from runtime JavaScript

Keep authentication, retries, cancellation and base-URL configuration in reviewed wrapper code or the generated SDK's supported extension points. Avoid storing credentials in generated fixtures. The retained result includes the actual compiler diagnostic, generator version and both schemas. It deliberately does not claim a network request, live FastAPI export or full SDK generation. Add those integration checks for the selected endpoint before presenting the generated client as a verified release boundary.

Mobile app API backward compatibility

Observability for small teams: start with the failed customer task

Bring one API or data workflow and a sanitized failure example for a bounded implementation assessment.

AptixLabs saas development

How should you generate a TypeScript client contract from FastAPI?

Freeze the OpenAPI artifact, pin generation tools and operation IDs, regenerate in CI and type-check consumers while testing runtime responses separately.

Should generated output be edited by hand?

No. Change the source contract or supported generator configuration, regenerate and review the resulting consumer migration.

Do generated TypeScript declarations validate HTTP JSON?

No. Runtime response validation and domain checks remain separate; type annotations are erased.

Need help with b2b saas development for connected customer workflows?

Tell us about your product, existing stack and the problem you need to solve. We can discuss integration scope, implementation tradeoffs and a practical first milestone.

Start a conversation