Generating TypeScript types — cratebase typegen
Cratebase ships TypeScript typegen as a built-in feature of the server binary — no separate
package, no Node/Bun toolchain required. It turns your collections into a single .d.ts file with
one <Name>Record type per non-system collection (plus <Name>Create/<Name>Update input types),
and a Schema/SchemaCreate/SchemaUpdate export shaped for
@cratebase/client’s createClient<Schema>(). Three
surfaces all call the same generator, so they can never disagree:
cratebase typegen— a one-shot CLI command, reading the local data directory’s collections the same waycratebase schema pulldoes;GET /api/typegen— a superuser-only REST endpoint, and a “Download TypeScript types” button in the dashboard’s API docs tab that calls it;- the
--devwatch — when the server runs with--devandCB_TYPEGEN_OUTis set, the file at that path is rewritten every time a collection is created, updated or deleted.
CLI: cratebase typegen
Section titled “CLI: cratebase typegen”cratebase typegen # writes ./cratebase-types.d.tscratebase typegen -o src/cratebase-types.d.tscratebase typegen -o - # write to stdout instead of a filecratebase typegen --dir ./pb_data -o types.d.tsSee the CLI reference for the full flag list.
REST: GET /api/typegen
Section titled “REST: GET /api/typegen”Superuser-only, returns the generated .d.ts as application/typescript with a
Content-Disposition: attachment filename, so a plain authenticated fetch (or the dashboard’s
download button) can save it directly:
curl -H "Authorization: Bearer <superuser-jwt>" \ http://localhost:8090/api/typegen -o cratebase-types.d.ts--dev watch: CB_TYPEGEN_OUT
Section titled “--dev watch: CB_TYPEGEN_OUT”Set CB_TYPEGEN_OUT (in .env or the environment) and run with --dev, and Cratebase rewrites
that path after every collection create/update/delete — including through cratebase schema push
and POST /api/schema/apply, since they apply changes through the same code path. A write failure
only logs a warning; it never fails the schema change itself.
CB_DEV=trueCB_TYPEGEN_OUT=./src/cratebase-types.d.tsField type mapping
Section titled “Field type mapping”Given a posts collection with a required title, a multi-select tags, and a relation to
authors:
{ "name": "posts", "type": "base", "fields": [ { "name": "title", "type": "text", "required": true }, { "name": "tags", "type": "select", "values": ["news", "release"], "maxSelect": 5 }, { "name": "author", "type": "relation", "collectionId": "<authors id>", "maxSelect": 1, "required": true } ]}cratebase typegen writes:
export type PostsRecord = { id: string; title: string; tags?: ("news" | "release")[]; author: string; expand?: { author?: AuthorsRecord; }; readonly created?: string; readonly updated?: string;};
export type PostsCreate = { title: string; tags?: ("news" | "release")[]; author: string;};
export type PostsUpdate = Partial<PostsCreate>;selectfields become a string literal union ("news" | "release"), an array of the union whenmaxSelect > 1.relationfields are the related id(s) (astringorstring[]bymaxSelect), plus anexpand?field typed to the target collection’s own<Name>Record— falling back toRecord<string, unknown>if the target collection isn’t in the generated set (e.g. a relation to a system collection).filefields arestring/string[](the stored filename(s));jsonisunknownunless a narrower type is more useful to hand-edit in;geoPointis{ lon: number; lat: number };vectorisnumber[];autodatefields (created/updated) are markedreadonly.passwordfields are excluded from the record type entirely — they never round-trip out of a record read.- Every other kind maps straight to its TypeScript scalar (
number→number,bool→boolean, …).
<Name>Create and <Name>Update exclude system and autodate fields (the server fills those in);
Create respects each field’s required flag, and Update is Partial<Create>. An auth
collection’s Create type also gets password/passwordConfirm (required strings), and its
record type includes email/emailVisibility/verified. A view collection only gets a record
type — it has no Create/Update, since it can’t be written to.
Schema, SchemaCreate, SchemaUpdate — for createClient<Schema>()
Section titled “Schema, SchemaCreate, SchemaUpdate — for createClient<Schema>()”The generated file also exports Schema (every non-system collection, keyed by name, typed to its
own <Name>Record), type Collections = keyof Schema, and SchemaCreate/SchemaUpdate (the same
keys, typed to each collection’s <Name>Create/<Name>Update; a view collection maps to never):
export type Schema = { posts: PostsRecord;};
export type Collections = keyof Schema;
export type SchemaCreate = { posts: PostsCreate;};
export type SchemaUpdate = { posts: PostsUpdate;};Pass Schema alone to createClient<Schema>() for a fully typed client.collection(name) — an
unknown collection name is a compile error, and reads resolve to the right <Name>Record with no
per-call type argument needed. Pass SchemaCreate/SchemaUpdate too for create()/update()
typed to each collection’s actual input shape (required fields respected) instead of
Partial<Record>:
import { createClient } from "@cratebase/client";import type { Schema, SchemaCreate, SchemaUpdate } from "./cratebase-types.js";
const cb = createClient<Schema, SchemaCreate, SchemaUpdate>("http://localhost:8090");const posts = await cb.collection("posts").list(); // typed PostsRecord[]await cb.collection("posts").create({ title: "hello", author: "abc123" }); // title/author requiredThe standalone @cratebase/schema-codegen package
Section titled “The standalone @cratebase/schema-codegen package”Before cratebase typegen existed, tools/schema-codegen
was a separate, Node/Bun-based CLI that did the same job from a checked-in
schema-as-code JSON file. It’s superseded by cratebase typegen
for any new project, but still works and isn’t going away — useful if you want typegen as a pure-JS
build step with no Cratebase binary on hand. See its own README for usage.