Skip to content

Generating TypeScript types — @cratebase/schema-codegen

@cratebase/schema-codegen is a standalone build-time CLI, versioned independently of the server, that turns a schema-as-code JSON document into a single .d.ts file with one TypeScript interface per non-system collection. It has no dependency on the server or admin dashboard — run it from your own project’s build pipeline wherever a PostsRecord-shaped type is more useful than hand-written ones.

Terminal window
npx @cratebase/schema-codegen ./schema.json -o src/cratebase-types.d.ts

The input is exactly what GET /api/collections returns (an { items: [...] } page), what PUT /api/collections/import and POST /api/schema/apply accept ({ collections: [...] }), a bare array of collection objects, or a live server URL to fetch that page from directly:

Terminal window
npx @cratebase/schema-codegen https://api.example.com --token <superuser-jwt> -o types.d.ts

Given this schema (a base collection plus a select and a relation field):

schema.json
{
"items": [
{
"name": "posts",
"type": "base",
"fields": [
{ "name": "title", "type": "text", "required": true },
{ "name": "tags", "type": "select", "maxSelect": 5 },
{ "name": "author", "type": "relation", "maxSelect": 1 }
]
}
]
}

npx @cratebase/schema-codegen ./schema.json -o cratebase-types.d.ts writes:

cratebase-types.d.ts
// Generated by @cratebase/schema-codegen. Do not edit by hand;
// re-run the codegen script instead.
/** `posts` (base). Generated by @cratebase/schema-codegen — do not edit by hand. */
export interface PostsRecord {
title: string;
tags?: string[];
author?: string;
}

Field types follow FieldKind’s variants in crates/core/src/field.rs one-for-one: select, file and relation fields become an array when their maxSelect is greater than 1 and a bare string otherwise, and every other kind maps straight to its TypeScript scalar (numbernumber, boolboolean, jsonunknown, geoPoint{ lon: number; lat: number }, …). System collections (_superusers, _admins, and the like) are skipped, since a project’s own code has no business constructing rows in those directly.

A CI pipeline typically checks the generated .d.ts into the repo alongside the checked-in schema.json from Schema as code, regenerating both whenever the schema changes so pb.collection("posts").getOne<PostsRecord>(id) stays in sync with what the server actually serves.