SQL to Drizzle Schema

Generate a Drizzle ORM schema from a SQL CREATE TABLE statement.

SQL DDL Input
Drizzle Schema Output
Drizzle Schema output appears here

Related Tools

Documentation

What is SQL to Drizzle?

SQL to Drizzle turns CREATE TABLE statements into a Drizzle ORM schema file using drizzle-orm/pg-core — one pgTable() call per table, built from column builder functions chained with modifiers like .primaryKey() and .notNull().

How it works

Each column's SQL type picks a pg-core builder function — INTEGER/SERIALinteger() (or serial() when auto-incrementing), VARCHAR(n)varchar('col', { length: n }), TIMESTAMPtimestamp(), UUIDuuid(), and so on — then chains .primaryKey(), .notNull(), and .unique() based on the parsed constraints. Postgres's SERIAL/BIGSERIAL auto-increment is expressed purely through the column type itself (serial()/bigserial()), not a separate flag, matching how Postgres actually implements it as a sequence-backed default. Only literal defaults and CURRENT_TIMESTAMP/NOW() (translated to .default(sql`now()`), importing sql from drizzle-orm only when needed) are converted; anything else becomes a trailing comment. The import line at the top of the file is assembled dynamically from exactly the builder functions your schema actually used.

Features

  • One pgTable() export per CREATE TABLE, named in camelCase
  • Type-aware builder selection, including varchar length and numeric precision/scale
  • Auto-generated import list scoped to only the builders actually used
  • Literal and now() defaults translated; everything else left as a comment
  • Copy or download the generated .ts schema file

Example

Input:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  external_id UUID DEFAULT uuid_generate_v4(),
  name VARCHAR(255) NOT NULL,
  is_active BOOLEAN DEFAULT true
);

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  user_id INTEGER NOT NULL REFERENCES users(id),
  title VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Output:

import { sql } from 'drizzle-orm';
import { boolean, integer, pgTable, serial, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  externalId: uuid('external_id'), // SQL default not translated: uuid_generate_v4()
  name: varchar('name', { length: 255 }).notNull(),
  isActive: boolean('is_active').default(true),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  userId: integer('user_id').notNull(), // FK -> users.id
  title: varchar('title', { length: 255 }).notNull(),
  createdAt: timestamp('created_at').default(sql`now()`),
});

Common errors

A DEFAULT the tool can't confidently translate, like uuid_generate_v4(), is never guessed at — it's left as a plain // SQL default not translated: uuid_generate_v4() comment next to the column so you can add .default(sql`gen_random_uuid()`) or the equivalent yourself. Foreign keys appear only as // FK -> table.column comments — Drizzle's .references(() => otherTable.column) needs a live reference to the other table's exported object, which the generator can't safely construct across separate schema files, so that wiring is left to you. If you're targeting MySQL or SQLite instead of Postgres, remember the output is pg-core-specific — some builder names and options differ between dialects.

Best practices

After pasting the output into your schema file, grep for not translated and FK -> to find every column needing manual follow-up, then run drizzle-kit generate to confirm the schema compiles and produces the migration you expect before applying it.

Frequently Asked Questions

Why pg-core specifically?

Postgres is the most common target for new Drizzle projects, so the generated import comes from drizzle-orm/pg-core. If you're on MySQL or SQLite, swap the import and a few column builder names (e.g. varchar stays similar, but some types differ) — the column options (notNull, unique, default) carry over conceptually.

Are foreign keys wired up with .references()?

Not automatically — a foreign key column gets a plain column definition plus a comment noting the target table/column. Drizzle's .references() needs a direct reference to the other table's exported column object, which requires both schema files to exist together; wiring that up is left for you to connect.

How does it decide between integer, bigint, and smallint?

Directly from the SQL type: INT/INTEGER/SERIAL become integer(), BIGINT/BIGSERIAL become bigint() with mode: 'number', and SMALLINT/SMALLSERIAL become smallint() — matching Postgres's own type hierarchy.

What about VARCHAR length or DECIMAL precision?

Both are preserved — VARCHAR(255) becomes varchar('col', { length: 255 }) and DECIMAL(10,2) becomes numeric('col', { precision: 10, scale: 2 }).