One schema. Four outputs. Zero drift.
Define your data model once in JSON or YAML — schema-cast generates TypeScript types, Zod validators, Mongoose models, and PostgreSQL SQL, all kept in sync automatically.
Getting Started · CLI Reference · Field Types · Contributing
In a typical fullstack project, the same data shape is written four separate times:
User interface → TypeScript
Request validation → Zod
Database model → Mongoose
Table definition → SQL
They drift. They conflict. You update one and forget the rest. schema-cast solves this with a single source of truth.
Given one .schema.json file, schema-cast produces:
| Output | File | Description |
|---|---|---|
| TypeScript | user.types.ts |
Interfaces and type definitions |
| Zod | user.zod.ts |
Runtime validation schema |
| Mongoose | user.model.ts |
MongoDB model with schema |
| PostgreSQL | user.sql |
CREATE TABLE statement |
# Global install (recommended for CLI use)
npm install -g schema-cast
# Or use without installing
npx schema-cast generate --input ./schemas/user.schema.json --out ./generatedCreate a user.schema.json file:
{
"name": "User",
"fields": [
{ "name": "id", "type": "uuid", "required": true, "primary": true },
{ "name": "email", "type": "email", "required": true, "unique": true },
{ "name": "age", "type": "number", "required": false },
{ "name": "role", "type": "enum", "values": ["admin", "user", "guest"], "default": "user" },
{ "name": "createdAt", "type": "date", "required": true },
{ "name": "profile", "type": "object", "fields": [
{ "name": "bio", "type": "string" },
{ "name": "avatar", "type": "url" }
]}
]
}schema-cast generate --input ./schemas/user.schema.json --out ./generatedFour files land in ./generated/:
user.types.ts — TypeScript interface
export interface User {
id: string;
email: string;
age?: number;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
profile?: {
bio?: string;
avatar?: string;
};
}user.zod.ts — Zod validation schema
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().optional(),
role: z.enum(['admin', 'user', 'guest']).default('user'),
createdAt: z.date(),
profile: z.object({
bio: z.string().optional(),
avatar: z.string().url().optional(),
}).optional(),
});
export type User = z.infer<typeof UserSchema>;user.model.ts — Mongoose model
import { Schema, model } from 'mongoose';
const UserSchema = new Schema({
email: { type: String, required: true, unique: true },
age: { type: Number },
role: { type: String, enum: ['admin', 'user', 'guest'], default: 'user' },
createdAt: { type: Date, required: true },
profile: {
bio: { type: String },
avatar: { type: String },
},
});
export const UserModel = model('User', UserSchema);user.sql — PostgreSQL CREATE TABLE
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR NOT NULL UNIQUE,
age INTEGER,
role VARCHAR CHECK (role IN ('admin', 'user', 'guest')) DEFAULT 'user',
created_at TIMESTAMP NOT NULL,
profile JSONB
);# Single file
schema-cast generate --input ./schemas/user.schema.json --out ./generated
# Entire directory
schema-cast generate --all --input ./schemas/ --out ./generated| Flag | Description |
|---|---|
--input |
Path to a .schema.json / .schema.yaml file or directory |
--out |
Output directory for generated files |
--all |
Process all schema files in the input directory |
schema-cast watch --input ./schemas/Only regenerates files whose schema changed — fast and non-destructive.
| Type | TypeScript | Zod | Mongoose | PostgreSQL |
|---|---|---|---|---|
string |
string |
z.string() |
String |
VARCHAR |
number |
number |
z.number() |
Number |
NUMERIC |
boolean |
boolean |
z.boolean() |
Boolean |
BOOLEAN |
date |
Date |
z.date() |
Date |
TIMESTAMP |
uuid |
string |
z.string().uuid() |
String |
UUID |
email |
string |
z.string().email() |
String |
VARCHAR |
url |
string |
z.string().url() |
String |
VARCHAR |
enum |
'a' | 'b' |
z.enum([...]) |
{ enum: [...] } |
CHECK (col IN (...)) |
object |
{ ... } |
z.object({...}) |
Nested schema | JSONB |
array |
T[] |
z.array(...) |
[{ type: T }] |
JSONB |
| Option | Type | Description |
|---|---|---|
required |
boolean |
Whether the field must be present (default: false) |
unique |
boolean |
Enforce unique constraint (Mongoose + PostgreSQL) |
primary |
boolean |
Mark as primary key — _id in Mongoose, PRIMARY KEY in SQL |
default |
any |
Default value applied across all outputs |
values |
string[] |
Enum values — required when type is "enum" |
fields |
FieldDefinition[] |
Nested fields — required when type is "object" |
items |
FieldDefinition |
Item type — required when type is "array" |
YAML schemas are supported alongside JSON:
name: Post
fields:
- name: title
type: string
required: true
- name: tags
type: array
items:
type: string
- name: status
type: enum
values: [draft, published, archived]
default: draftschema-bridge/
├── src/
│ ├── index.ts # Public API entry point
│ ├── parser.ts # JSON / YAML schema parser
│ ├── generators/
│ │ ├── typescript.ts # TypeScript interface generator
│ │ ├── zod.ts # Zod schema generator
│ │ ├── mongoose.ts # Mongoose model generator
│ │ └── postgres.ts # SQL CREATE TABLE generator
│ ├── cli.ts # CLI entry point (commander.js)
│ └── watcher.ts # Watch mode (chokidar)
└── tests/
└── generators.test.ts
Contributions are welcome. DemonDie is an open source community — if you're building with React, Next.js, Node.js, or any modern web stack, this tool is for you.
git clone https://github.com/Demon-Die/schema-bridge.git
cd schema-bridge
npm install
npm run devTo run tests:
npm testPlease open an issue before submitting large PRs. See CONTRIBUTING.md for guidelines.
schema-cast is part of the DemonDie open source ecosystem — a community building developer tools, web applications, AI/ML solutions, and community platforms.
MIT © DemonDie