stdb-zod-gen
v0.1.0
Published
Auto-generate Zod validation schemas from SpacetimeDB TypeScript modules
Maintainers
Readme
@stdb-zod-gen/cli
Auto-generate Zod validation schemas from SpacetimeDB TypeScript modules.
Features
- Static Analysis — Parse modules without code execution (safe, fast)
- Complete Type Coverage — All SpacetimeDB types mapped with proper constraints
- Reducer Schemas — Generate validation for reducer arguments
- Create/Update Variants — Separate schemas for create vs update operations
- Watch Mode — Auto-regenerate on module changes
- Type Safety — Coerce frontend primitives to SpacetimeDB types
Installation
pnpm add -D @stdb-zod-gen/cli
pnpm add @stdb-zod-gen/runtimeQuick Start
# Generate schemas from module
stdb-zod-gen ./src/module.ts ./src/schemas.ts
# Watch mode
stdb-zod-gen --watch
# Use config file
stdb-zod-gen # reads .stdb-zod-gen.jsonConfiguration
.stdb-zod-gen.json:
{
"inputs": [
{
"module": "./modules/game/src/index.ts",
"output": "./apps/web/src/schemas/game.ts",
"include": ["tables", "reducers"],
"options": {
"generateCreateSchemas": true,
"generateUpdateSchemas": true,
"maxStringLength": 1000
}
}
]
}Usage
Input (modules/game/src/index.ts):
import { table, schema, t } from 'spacetimedb/server';
export const User = table(
{ name: 'User', public: true },
{
identity: t.identity().primaryKey(),
name: t.string().optional(),
age: t.u8(),
online: t.bool(),
}
);
const s = schema(User);
s.reducer('set_name', { name: t.string() }, (ctx, args) => {
// ...
});Generated Output (apps/web/src/schemas/game.ts):
import { z } from 'zod';
import { zodSpacetime } from '@stdb-zod-gen/runtime';
// Table schema (full)
export const UserSchema = z.object({
identity: zodSpacetime.identity(),
name: z.string().max(1000).optional(),
age: z.number().int().min(0).max(255),
online: z.boolean(),
});
export type User = z.infer<typeof UserSchema>;
// Create schema (omit auto-generated fields)
export const UserCreateSchema = UserSchema.omit({ identity: true });
export type UserCreate = z.infer<typeof UserCreateSchema>;
// Update schema (all optional except primary key)
export const UserUpdateSchema = UserSchema.partial().required({ identity: true });
export type UserUpdate = z.infer<typeof UserUpdateSchema>;
// Reducer argument schema
export const SetNameArgsSchema = z.object({
name: z.string().min(1).max(1000),
});
export type SetNameArgs = z.infer<typeof SetNameArgsSchema>;Frontend Validation:
import { UserCreateSchema, SetNameArgsSchema } from './schemas/game';
import { DbConnection } from 'spacetimedb';
function createUser(name: string, age: number) {
// Validate + coerce before sending to DB
const user = UserCreateSchema.parse({ name, age, online: true });
// user.identity is auto-generated by SpacetimeDB
}
function setUserName(name: string) {
// Validate reducer arguments
const args = SetNameArgsSchema.parse({ name });
conn.reducers.set_name(args);
}Type Coercion
Identity (hex string → Identity):
const schema = z.object({
identity: zodSpacetime.identity(),
});
// Accepts: Identity instance, hex string, bigint
schema.parse({ identity: '0x' + '0'.repeat(64) });
schema.parse({ identity: 123456789n });Timestamp (Date → Timestamp):
const schema = z.object({
createdAt: zodSpacetime.timestamp(),
});
// Accepts: Timestamp, Date, ISO string, Unix ms
schema.parse({ createdAt: new Date() });
schema.parse({ createdAt: '2025-10-18T12:00:00Z' });
schema.parse({ createdAt: 1729252800000 });Numeric Bounds
SpacetimeDB integer types are validated with proper bounds:
| Type | Range | Zod Schema |
|------|-------|------------|
| t.u8() | 0-255 | z.number().int().min(0).max(255) |
| t.u16() | 0-65535 | z.number().int().min(0).max(65535) |
| t.i8() | -128-127 | z.number().int().min(-128).max(127) |
| t.u64() | ≥0 | z.bigint().nonnegative() |
Watch Mode
# Auto-regenerate on module changes
stdb-zod-gen --watch
# Useful in package.json scripts
{
"scripts": {
"dev": "concurrently 'pnpm dev' 'stdb-zod-gen --watch'"
}
}package.json Integration
{
"scripts": {
"gen:schemas": "stdb-zod-gen",
"gen:schemas:watch": "stdb-zod-gen --watch",
"dev": "bun gen:schemas:watch & pnpm dev",
"build": "bun gen:schemas && bun build"
}
}Error Handling
Actionable error messages with source locations:
❌ Error parsing module at ./src/module.ts:15:3
13 | export const User = table(
14 | { name: 'User', public: true },
> 15 | { identity: t.unknownType() }
| ^^^^^^^^^^^^^^
Unsupported type builder: UnknownTypeBuilder
Supported types: t.string(), t.bool(), t.u8(), t.i32(), ...
Documentation: https://spacetimedb.com/docs/modules/typescriptRequirements
- Node.js ≥ 20
- SpacetimeDB SDK ≥ 1.6.0
- Zod ≥ 4.0.0
License
MIT
