@platform/sdk-codegen
@platform/sdk-codegen generates TypeScript types and API clients
from OpenAPI 3.x specifications and Protocol Buffer (.proto) schema
files. Module authors never write HTTP client boilerplate or
manually maintain type definitions — the generated code is always
in sync with the server API.
Installation
pnpm add -D @platform/sdk-codegen
CLI — platform-codegen
Run code generation from your module root:
# Generate TypeScript types from the Platform API (OpenAPI 3.x)
npx platform-codegen openapi \
--input https://api.septemcore.com/v1/openapi.json \
--output src/generated/api.ts \
--client fetch # 'fetch' | 'axios' | 'none' (types-only)
# Generate TypeScript types from Proto definitions
npx platform-codegen proto \
--input proto/crm.proto \
--output src/generated/crm.ts
# Watch mode — regenerate on spec change (development)
npx platform-codegen openapi --watch \
--input https://api.septemcore.com/v1/openapi.json \
--output src/generated/api.ts
OpenAPI → TypeScript Pipeline
The OpenAPI pipeline uses
openapi-typescript
v7 (April 2026) to generate schema types and
openapi-fetch
for a fully-typed fetch-based client:
// src/generated/api.ts — auto-generated, do not edit
export interface paths {
'/api/v1/contacts': {
get: operations['listContacts'];
post: operations['createContact'];
};
'/api/v1/contacts/{id}': {
get: operations['getContact'];
patch: operations['updateContact'];
delete: operations['deleteContact'];
};
}
export interface components {
schemas: {
Contact: {
id: string;
name: string;
email: string;
status: 'active' | 'inactive';
createdAt: string;
};
CreateContactRequest: {
name: string;
email: string;
};
};
}
Using the generated client:
import createClient from 'openapi-fetch';
import type { paths } from './generated/api';
import { kernel } from '@platform/sdk-core';
// Client is fully typed — parameters, body, and response are all typed
const apiClient = createClient<paths>({
baseUrl: 'https://api.septemcore.com',
headers: { Authorization: `Bearer ${await kernel.auth().getToken()}` },
});
// TypeScript enforces the correct request shape
const { data: contacts, error } = await apiClient.GET('/api/v1/contacts', {
params: { query: { status: 'active', limit: 20 } },
});
// TypeScript knows contacts is Contact[] | undefined
contacts?.data.forEach(c => console.log(c.name, c.email));
Generated File Structure
src/generated/
├── api.ts ← OpenAPI types + path definitions
├── api-client.ts ← Pre-configured openapi-fetch client instance
└── schemas.ts ← Re-exported Zod schemas for runtime validation (optional)
Proto → TypeScript Pipeline
For gRPC and Protobuf-typed Kafka events, use the proto command.
The pipeline uses ts-proto
(maintained, April 2026 latest):
// proto/crm.proto
syntax = "proto3";
package crm;
message Contact {
string id = 1;
string name = 2;
string email = 3;
string tenant_id = 4;
int64 created_at = 5;
}
message CreateContactRequest {
string name = 1;
string email = 2;
}
service ContactService {
rpc CreateContact (CreateContactRequest) returns (Contact);
rpc GetContact (GetContactRequest) returns (Contact);
}
Generated TypeScript:
// src/generated/crm.ts — auto-generated, do not edit
export interface Contact {
id: string;
name: string;
email: string;
tenantId: string; // camelCase conversion from snake_case
createdAt: number; // int64 → number (Long by config)
}
export interface CreateContactRequest {
name: string;
email: string;
}
export interface ContactServiceClient {
createContact(request: CreateContactRequest): Promise<Contact>;
getContact(request: GetContactRequest): Promise<Contact>;
}
generate() — Programmatic API
For advanced use cases (monorepo builds, custom pipelines), call
generate() directly from a build script:
// scripts/codegen.ts
import { generate } from '@platform/sdk-codegen';
await generate({
openapi: [
{
input: 'https://api.septemcore.com/v1/openapi.json',
output: 'src/generated/platform-api.ts',
client: 'fetch',
},
{
input: 'https://api.septemcore.com/v1/crm/openapi.json',
output: 'src/generated/crm-api.ts',
client: 'fetch',
},
],
proto: [
{
input: 'proto/events.proto',
output: 'src/generated/events.ts',
},
],
});
console.log('Code generation complete.');
CI/CD Integration
Add code generation as a CI step to verify the generated files are always up to date with the server spec:
# .github/workflows/codegen-check.yml
name: Codegen Check
on: [pull_request]
jobs:
check-generated:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 10 }
- run: pnpm install --frozen-lockfile
- name: Re-generate types
run: pnpm exec platform-codegen openapi
--input https://api.septemcore.com/v1/openapi.json
--output src/generated/api.ts
- name: Fail if generated files changed
run: |
git diff --exit-code src/generated/
# Exit code 1 = generated files are stale → PR blocked
Best practice: Commit generated files to source control. The CI job re-generates and fails the PR if the output differs. This prevents "works on my machine" type-drift where the spec changed but the module author forgot to regenerate.
Proto Schema Compatibility (Buf CLI)
All .proto changes in the Platform monorepo CI run
buf breaking
to enforce backward compatibility.
| Change type | Allowed? | Reason |
|---|---|---|
| Add new field | ✅ Yes | Backward compatible (new field = ignored by old consumers) |
| Add new message | ✅ Yes | No impact on existing messages |
| Rename field | ❌ No | Breaks consumers using JSON-encoded proto |
| Delete field | ❌ No | Breaks consumers expecting the field |
| Change field type | ❌ No | Wire format incompatibility |
Violations → CI red → PR merge blocked. Config: buf.yaml in the
platform monorepo root with breaking.use: [FILE].