This document explains how the frontend and backend stay in sync using OpenAPI and auto-generated TypeScript types.
The API contract is defined in backend/openapi.yaml, which is the single source of truth for all API endpoints, request/response schemas, and authentication.
Frontend TypeScript types are auto-generated from this spec, ensuring perfect alignment between what the frontend expects and what the backend provides.
backend/openapi.yaml (Single Source of Truth)
↓
openapi-typescript
↓
frontend/src/types/api.ts (Auto-generated, Do Not Edit)
↓
frontend/src/services/api.ts (Uses generated types)
↓
React Components (Always use correct types)
-
Update the OpenAPI spec:
# Edit backend/openapi.yaml # Add your new endpoint to the paths section
-
Regenerate frontend types:
cd frontend npm run generate:api-types -
The TypeScript compiler will:
- ✅ Flag any breaking changes
- ✅ Auto-complete endpoints in the IDE
- ✅ Validate request/response shapes
-
Add to
backend/openapi.yaml:/api/companies/{id}/: get: summary: Get company details tags: - Companies parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Company details content: application/json: schema: $ref: '#/components/schemas/Company'
-
Regenerate types:
npm run generate:api-types
-
Update
frontend/src/services/api.ts:export const companiesApi = { get: (id: number) => api.get<Company>(`/api/companies/${id}/`), };
-
Use in components - TypeScript autocomplete will guide you:
const { data } = await companiesApi.get(1); // Type-safe!
The generated frontend/src/types/api.ts includes:
- Paths: All endpoint definitions (
/api/auth/login/,/api/applications/, etc.) - Schemas: Request/response models (User, Application, UserProfile, etc.)
- Operations: Typed request/response pairs for each endpoint
// ❌ Manual types (can drift from spec)
interface Application {
id: number;
title: string;
}// ✅ Use auto-generated types
import type { paths } from '../types/api';
type Application = paths['/api/applications/']['get']['responses']['200']['content']['application/json'][number];import type { paths } from '../types/api';
// Get request body type
type CreateApplicationRequest = paths['/api/applications/']['post']['requestBody']['content']['application/json'];
// Get response type
type CreateApplicationResponse = paths['/api/applications/']['post']['responses']['201']['content']['application/json'];npm run buildIf the spec changed and code doesn't match, TypeScript will error immediately.
We recommend adding contract tests with Dredd:
npm install -D dredd
dredd backend/openapi.yaml http://localhost:8000This validates:
- ✅ All endpoints exist
- ✅ Request parameters match spec
- ✅ Response schemas match spec
- ✅ Status codes are correct
- Never manually edit
src/types/api.ts- it's auto-generated - Update the OpenAPI spec first - then regenerate types
- Use TypeScript strict mode - catches type mismatches
- Test endpoints against spec - add Dredd to CI/CD
- Keep spec and code in same PR - spec + implementation together
import type { paths } from '../types/api';
import { applicationsApi } from '../services/api';
type Application = paths['/api/applications/']['get']['responses']['200']['content']['application/json'][number];
export function ApplicationList() {
const [apps, setApps] = useState<Application[]>([]);
// Types are enforced throughout
const handleFetch = async () => {
const { data } = await applicationsApi.list();
setApps(data); // Type-safe
};
}// ✅ Correct - types inferred from spec
const response = await applicationsApi.create({
company: { id: 1, name: 'Google' },
job_title: 'SWE', // Required
status: 'APPLIED', // Enum enforced
});
// ❌ Wrong - TypeScript will error
const response = await applicationsApi.create({
company: { id: 1 },
job_title: 'SWE',
status: 'INVALID_STATUS', // Error: not in enum
});Whenever the OpenAPI spec changes:
cd frontend
npm run generate:api-typesThis is fast (~50ms) and safe - it only overwrites the generated file.
Q: TypeScript says type doesn't exist
- Run
npm run generate:api-typesto regenerate - Check OpenAPI spec has the path defined
Q: IDE autocomplete not working
- Restart TypeScript server (Cmd+Shift+P → "Restart TS Server" in VS Code)
- Ensure
src/types/api.tsexists
Q: Breaking type changes in spec
- Run build:
npm run build - TypeScript will show exactly what broke
- Fix code to match new spec
| File | Purpose | Edit? |
|---|---|---|
backend/openapi.yaml |
API contract definition | ✅ Yes |
frontend/src/types/api.ts |
Auto-generated types | ❌ No |
frontend/package.json |
Has generate:api-types script | ✅ Update scripts |
frontend/src/services/api.ts |
API client using types | ✅ Yes |
-
Review the generated types:
cat frontend/src/types/api.ts | less -
Test the type system:
cd frontend npm run build -
Add contract testing (optional):
npm install -D dredd
-
Add to CI/CD (optional):
- Run
npm run generate:api-typesbefore build - Fail if types changed (ensuring spec is kept in sync)
- Run