What is JSON to Drizzle Schema Generator?
Drizzle ORM is a TypeScript-first, SQL-close ORM where your schema is a plain TypeScript file — no separate SDL, no separate schema.prisma. You call builder functions like pgTable(), integer(), varchar(), and jsonb() to describe each column, and Drizzle infers TypeScript types directly from those definitions using $inferSelect and $inferInsert. This tool reads a sample JSON object and produces a ready-to-use Drizzle table definition. It infers the most appropriate column type for each field, adds .notNull() where the value is not null, detects id fields and makes them serial().primaryKey(), spots ISO 8601 dates and maps them to timestamp(), and optionally converts camelCase property names to snake_case SQL column names (the Drizzle convention). The most important limitation to understand: Drizzle infers types from a single sample. A field that is populated in your sample will not get nullable treatment — if that field can be null in production, remove .notNull() manually. For the same reason, paste a sample that is representative of your actual data, including any fields that might be missing or null.
How to Use
- Enter the PascalCase table name (e.g. "Post", "OrderItem") — Drizzle conventionally names the exported variable in camelCase plural (e.g. "posts", "orderItems")
- Pick your database provider — this affects which import package is used (drizzle-orm/pg-core, mysql-core, or sqlite-core) and which column builders are available
- Paste a representative JSON object. Include fields that might be null in real data by setting them to null in the sample
- Click Generate Schema, then copy and paste the output into your schema.ts file
- Replace any jsonb() columns that represent relational data with a proper foreign key and a related table. Run npx drizzle-kit generate to create the migration
Why Use This Tool?
Tips & Best Practices
- Drizzle column names in the builder are the SQL column names, not the JavaScript property names. If you turn off snake_case, the SQL column will match your camelCase JSON key — that is valid but unconventional
- $inferSelect reflects every column, including those with .default(). $inferInsert makes columns with defaults optional and required columns required — this is how TypeScript catches missing INSERT arguments
- jsonb() stores arbitrary JSON and supports path queries in PostgreSQL (via the ->> and @> operators in raw SQL). If you query inner fields frequently, promote them to typed columns instead
- For UUID primary keys on PostgreSQL, use uuid().primaryKey().defaultRandom() — this tool detects UUID-shaped strings and will output the right column type
- After generating, add @@index manually on columns you filter or sort by — Drizzle does not infer indexes from data
Frequently Asked Questions
What is the complete JSON-to-Drizzle type mapping?
JSON integer → integer() or serial() for IDs. JSON float → numeric({ precision, scale }) on PG/MySQL, real() on SQLite. JSON boolean → boolean() on PG, tinyint({ mode: "boolean" }) on MySQL, integer({ mode: "boolean" }) on SQLite. JSON string → varchar({ length: 255 }). ISO date string → timestamp() / datetime() / text({ mode: "timestamp" }). UUID string → uuid() on PG, varchar({ length: 36 }) elsewhere. JSON object or array → jsonb() on PG, json() on MySQL/SQLite. JSON null → field has no .notNull().
When should I use jsonb() versus a separate related table?
Use jsonb() for genuinely unstructured data you treat as an opaque blob: user preferences, third-party webhook payloads, audit snapshots. Use a separate table with a foreign key when the nested data has a stable shape, needs to be filtered or aggregated, or needs its own constraints. Arrays of objects in JSON (e.g. order line items) almost always belong in a separate related table, not a jsonb column.
What is the difference between $inferSelect and $inferInsert?
$inferSelect is the TypeScript type of a complete row as returned by SELECT — every column, including those with defaults. $inferInsert is the type for INSERT — columns with .default() or .defaultNow() become optional because Drizzle fills them in. This means TypeScript will error if you try to insert without providing a required column, but will not require you to supply createdAt or id on every insert.
How do I run migrations after generating the schema?
Install drizzle-kit (npm install --save-dev drizzle-kit), add a drizzle.config.ts file pointing to your schema file and database URL, then run npx drizzle-kit generate to create a SQL migration file and npx drizzle-kit migrate (or push for prototyping) to apply it.
Is my data sent to a server?
No. All schema generation runs entirely in your browser. Your JSON never leaves your device.
Real-world Examples
User table from a signup API response
A POST /auth/signup response returns the created user. The id becomes serial().primaryKey(), email gets .unique(), the null bio becomes a nullable varchar, score is a numeric float, and audit timestamps are added automatically.
{
"id": 1,
"email": "[email protected]",
"name": "Ada Lovelace",
"bio": null,
"score": 4.85,
"isVerified": true,
"createdAt": "2024-03-01T12:00:00Z"
}import { boolean, numeric, pgTable, serial, text, timestamp, varchar } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
bio: text('bio'),
score: numeric('score', { precision: 10, scale: 2 }).notNull(),
isVerified: boolean('is_verified').notNull().default(true),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;Product catalog item — nested object becomes jsonb
A product has structured fields plus a specs object that is genuinely unstructured (different per category). The structured fields get typed columns; specs maps to jsonb(). This is the canonical pattern for semi-structured data.
{
"id": "prod_abc123",
"sku": "TS-RED-M",
"name": "Red T-Shirt Medium",
"price": 19.99,
"stock": 142,
"inStock": true,
"specs": { "color": "red", "size": "M", "weight_g": 180 },
"createdAt": "2024-06-01T09:00:00Z"
}import { boolean, integer, jsonb, numeric, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core';
export const products = pgTable('products', {
id: varchar('id', { length: 255 }).notNull().primaryKey(),
sku: varchar('sku', { length: 255 }).notNull(),
name: varchar('name', { length: 255 }).notNull(),
price: numeric('price', { precision: 10, scale: 2 }).notNull(),
stock: integer('stock').notNull(),
inStock: boolean('in_stock').notNull().default(true),
specs: jsonb('specs').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull(),
});
export type Product = typeof products.$inferSelect;
export type NewProduct = typeof products.$inferInsert;