Casing moves into your schema
In TypeScript, you name properties in camelCase: fullName, createdAt. Many databases name columns in snake_case: full_name, created_at. Casing is the rule that converts one into the other, so you don’t write every column name twice.
In 0.x (legacy), you set this rule twice: once in drizzle() for your queries, and once in drizzle.config.ts for drizzle-kit. v1 removes both. You now set it once, on the table, and both your queries and drizzle-kit use it.
Before · Legacy 0.x
After · rc4
// In db.tsconst db = drizzle({ client, casing: "snake_case",});
// In schema.tsexport const users = pgTable("users", { id: serial().primaryKey(), fullName: text().notNull(), createdAt: timestamp().defaultNow().notNull(),});// In db.tsconst db = drizzle({ client });
// In schema.tsexport const users = snakeCase.table("users", { id: serial().primaryKey(), fullName: text().notNull(), createdAt: timestamp().defaultNow().notNull(),});Your columns keep the same names: fullName is still stored as full_name, and createdAt as created_at. Delete casing from drizzle.config.ts as well. drizzle-kit v1 doesn’t read it anymore.
If you wrote every column name by hand, like text("full_name"), casing never changed anything for you. You only need to delete the casing option, because TypeScript now rejects it.
introspect.casing in drizzle.config.ts is a different option. It only controls how drizzle-kit pull names properties, and it still exists.
Don’t just delete casing
Without casing, Drizzle looks for a column called fullName. Your database has full_name. Two things break.
Your queries fail. A select, for example, stops with column "fullName" does not exist.
Your next generate thinks you changed the table. It asks you about each column:
Is users.fullName column created or renamed from another column?In CI, where nobody can answer, it stops with missing_hints and exit code 2.
You can only answer “renamed” or “created”, and both change your database:
- Renamed: drizzle-kit renames
full_nametofullNamein your database. Your data stays, but the column names change, so any other app, script or SQL query that still usesfull_namebreaks. - Created: the migration adds a new, empty
fullNamecolumn and deletesfull_name, with its data.
You want neither. Use snakeCase.table instead, and generate says No schema changes, nothing to migrate.
Leaving casing in drizzle() doesn’t save you either. TypeScript rejects it, and in a plain JavaScript file, where nothing checks types, v1 silently ignores it. Your queries fail the same way.
Each dialect has its own casing helpers, imported from its -core package. In drizzle-orm/pg-core, you can apply casing to a table, a view, a materialized view, or a whole schema:
// Every table in this schema gets snake_case columnsexport const app = snakeCase.schema("app");
export const teams = app.table("teams", { id: serial().primaryKey(), displayName: text(), // display_name});camelCase.table works the other way: it names columns in camelCase, so a display_name key is stored as displayName.
Generated columns only take SQL
A generated column is a column the database computes from other columns. In 0.x, you could pass .generatedAlwaysAs() a plain string. v1 only accepts an sql template, or a function that returns one, so a string is now a type error.
Before · Legacy 0.x
After · rc4
export const users = pgTable("users", { id: serial().primaryKey(), fullName: text().notNull(), searchName: text().generatedAlwaysAs("lower(full_name)"),});export const users = snakeCase.table("users", { id: serial().primaryKey(), fullName: text().notNull(), searchName: text().generatedAlwaysAs(sql`lower(full_name)`),});Postgres: multi-dimensional arrays
An array column stores a list of values, like integer[]. A two-dimensional array, integer[][], stores a list of lists, such as a grid of scores. In v1, you give the number of dimensions in one call:
Before · Legacy 0.x
After · rc4
export const boards = pgTable("boards", { id: serial().primaryKey(), grid: integer().array().array(),});export const boards = pgTable("boards", { id: serial().primaryKey(), grid: integer().array("[][]"),});Postgres: row-level security
Row-level security (RLS) lets Postgres decide which rows each user can read or change. .enableRLS() still works in rc4, but it’s deprecated. Turn RLS on when you declare the table instead:
Before · Legacy 0.x
After · rc4
export const posts = pgTable("posts", { id: serial().primaryKey(),}).enableRLS();export const posts = pgTable.withRLS("posts", { id: serial().primaryKey(),});Validators move into drizzle-orm
Validator packages turn your tables into validation schemas, for example to check a request body before you insert it. In v1, they’re part of drizzle-orm:
| 0.x package | v1 import |
|---|---|
drizzle-zod |
drizzle-orm/zod |
drizzle-valibot |
drizzle-orm/valibot |
drizzle-typebox (@sinclair/typebox) |
drizzle-orm/typebox-legacy |
none (typebox 1.x) |
drizzle-orm/typebox (new) |
drizzle-arktype |
drizzle-orm/arktype |
| none | drizzle-orm/effect-schema (new) |
The functions have the same names, so usually you only change the import. You still install the validation library itself, such as zod:
import { createInsertSchema } from "drizzle-orm/zod";
const userInsertSchema = createInsertSchema(users);
userInsertSchema.parse({ fullName: "Ada Lovelace" });The docs say you can keep the old packages, but new features only go into drizzle-orm. Keeping them isn’t smooth anyway: npm refuses to install them next to drizzle-orm@1.0.0-rc.4 unless you pass --legacy-peer-deps.
getColumns replaces getTableColumns
getTableColumns still works in rc4, but it’s deprecated. Rename it:
const { fullName, ...rest } = getColumns(users);Check your work
Once your schema compiles, run generate. None of the changes above should touch your database, so drizzle-kit should have nothing to do:
npx drizzle-kit generatepnpm drizzle-kit generateyarn drizzle-kit generatebunx drizzle-kit generateNo schema changes, nothing to migrate 😴If it asks about columns, or wants to create, rename or drop anything, stop. Compare your schema with your database before you answer.
Further reading
How we checked: we generated migrations with drizzle-kit@0.31.10, converted them with drizzle-kit up, then ran generate again with 1.0.0-rc.4, before and after the schema changes in this article. The query errors come from running the queries against PGlite, an in-memory PostgreSQL.
The v0 to v1 changes (Drizzle docs, opens in a new tab) list the breaking changes for each dialect, and the upgrade guide (Drizzle docs, opens in a new tab) gives the order. For migrations and relations, see Migrations in v1 and Relational Queries v2.