Skip to main content

@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 typeAllowed?Reason
Add new field✅ YesBackward compatible (new field = ignored by old consumers)
Add new message✅ YesNo impact on existing messages
Rename field❌ NoBreaks consumers using JSON-encoded proto
Delete field❌ NoBreaks consumers expecting the field
Change field type❌ NoWire format incompatibility

Violations → CI red → PR merge blocked. Config: buf.yaml in the platform monorepo root with breaking.use: [FILE].