Generate
Read .cerial schema files and produce a fully typed TypeScript client.
The generate command reads your .cerial schema files and produces a fully typed TypeScript client.
bunx cerial generate [options]Options
| Flag | Alias | Description | Default |
|---|---|---|---|
--schema <path> | -s | Path to schema file or directory | ./schemas |
--output <path> | -o | Output directory for generated client | - |
--config <path> | -C | Path to config file | - |
--name <name> | -n | Target a specific schema by name (multi-schema) | - |
--clean | -c | Delete entire output directory before generating | false |
--watch | -w | Watch for schema changes and regenerate | false |
--verbose | -v | Verbose output showing generation details | false |
--log <level> | -l | Log output level: minimal, medium, or full | minimal |
--format | Run the formatter on schema files during generation | false | |
--help | -h | Show help message | - |
When using a config file, the -o flag is optional since output paths come from the config. Without a config, -o is required.
Examples
Generate with a config file
If you have a cerial.config.ts or cerial.config.json in your project root, just run:
bunx cerial generateCerial auto-discovers the config and uses its schema paths and output directories. See Configuration for setup details.
Specify a config file path
Point to a config file in a non-default location:
bunx cerial generate -C ./config/cerial.config.tsSpecify paths directly
Skip the config and pass schema and output paths on the command line:
bunx cerial generate -s ./schemas -o ./db-clientSingle schema file
Generate from a single .cerial file instead of a directory:
bunx cerial generate -s ./schema.cerial -o ./generatedTarget a specific schema
In a multi-schema setup, regenerate just one schema by name:
bunx cerial generate -n authThe name matches a key in your config's schemas map. Only that schema is regenerated; the rest stay untouched. This flag requires a config file.
Watch mode
Automatically regenerate the client whenever schema files change:
bunx cerial generate --watchWatch mode monitors your schema files and triggers regeneration each time a .cerial file is created, modified, or deleted. Changes are debounced (300ms) to coalesce rapid edits into a single rebuild.
With a multi-schema config, each schema is watched independently. A change to one schema only regenerates that schema's client.
Combine -n with --watch to focus on a single schema:
bunx cerial generate -n auth --watchGenerate with formatting
Run the .cerial formatter alongside generation:
bunx cerial generate --watch --formatClean output
Delete the entire output directory before generating, ensuring a completely fresh output with no leftover files:
bunx cerial generate -s ./schemas -o ./db-client --cleanWithout --clean, stale files from previous generations (types for renamed or removed models, for example) are automatically detected and removed after generating. The --clean flag is useful when you want a guaranteed clean slate, such as after major schema restructuring.
Verbose output
See detailed information about what the generator is doing at each step:
bunx cerial generate --verboseWhat Happens During Generation
The generation process follows a nine-step pipeline.
1. Schema Discovery
The generator resolves schema files through a priority chain (see Configuration for full details):
- CLI flags (
-s), the given path is used directly - Config file, schema paths from
cerial.config.tsorcerial.config.json - Folder-level config, a
cerial.config.tsplaced inside a schema folder - Convention markers, directories containing
schema.cerial,main.cerial, orindex.cerial - Legacy fallback, a
schemas/orschema/directory in the current working directory
Once resolved, each path can be a directory (all .cerial files are discovered and loaded) or a single file (just that one file is loaded).
2. Parsing
Each schema file is parsed into an AST. The parser recognizes:
model {}blocks with fields, decorators, and relationsobject {}blocks with embedded fieldstuple {}blocks with typed elementsliteral {}blocks with union variantsenum {}blocks with named string constants- Field types:
String,Email,Int,Float,Bool,Date,Record,Relation,Uuid,Duration,Decimal,Bytes,Geometry,Number,Any, and type references - Modifiers:
?(optional),[](array),@nullable - Decorators:
@id,@unique,@default(),@defaultAlways(),@now,@createdAt,@updatedAt,@field(),@model(),@onDelete(),@readonly,@flexible,@set,@uuid, and more
3. Validation
The parsed schema is validated for correctness:
- Every
Relationfield must reference an existing model - Forward relations must have a
@field()decorator pointing to a validRecordfield - Reverse relations must not have
@field() Recordfields (except@id) must have a correspondingRelationfield- Object, tuple, literal, and enum references must point to existing definitions
- Decorators are checked for valid combinations and field type compatibility
4. Type Generation
TypeScript types and interfaces are generated for each model, object, tuple, literal, and enum. See Generated Output for the full list of types per kind.
// For models: full set of types
export interface User { ... }
export interface UserCreate { ... }
export interface UserUpdate { ... }
export interface UserWhere { ... }
export interface UserSelect { ... }
export interface UserInclude { ... }
export interface UserOrderBy { ... }
// For objects: subset of types
export interface Address { ... }
export interface AddressInput { ... }
export interface AddressWhere { ... }5. Model Registry Generation
A runtime registry is generated containing metadata about every model:
- Field names, types, optionality, and array status
- Relation targets, directions, and field references
- Decorator information (id, unique, default values, onDelete behavior)
The query builder uses this registry at runtime to construct correct SurrealQL queries.
6. Migration Generation
SurrealQL migration statements are generated:
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD name ON TABLE user TYPE string;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE INDEX user_email_unique ON TABLE user FIELDS email UNIQUE;7. Client Generation
The CerialClient class is generated with:
connect()anddisconnect()methodsmigrate()for explicit migrationdbproxy with typed model access
8. Formatting
All generated files are formatted with Biome and written to the output directory. Existing files in the output directory are overwritten.
9. Stale File Cleanup
After writing all files, the generator scans the output directory for .ts files that weren't part of the current generation and removes them. This handles renamed or deleted models, objects, and tuples without requiring a full directory wipe. Empty directories left behind are also cleaned up.
If --clean was used, this step is skipped since the directory was already wiped before generating.
Typical Workflow
# 1. Define your schema
# schemas/schema.cerial
# 2. Set up a config (optional, but recommended)
bunx cerial init
# 3. Generate the client
bunx cerial generate
# 4. Import and use in your app
# import { CerialClient } from './client';
# 5. During development, use watch mode
bunx cerial generate --watchTroubleshooting
"No schema files found": Check that the schema path is correct and contains .cerial files. If you're using a config, verify the schema or schemas paths point to existing directories.
Validation errors: The generator reports which field or model has an issue. Common problems:
- A
Relationfield references a model that doesn't exist - A
Recordfield doesn't have a correspondingRelation(except@idfields) - A forward relation is missing
@field() - Decorator conflicts (e.g.,
@createdAtand@defaulton the same field)
Output directory not created: The generator creates the output directory if it doesn't exist. If you're getting permission errors, check your file system permissions.
Stale types after renaming: The automatic cleanup should handle this. If files linger, run with --clean to wipe the output directory and regenerate fresh.