[BUG]: Column name not being returned as snake_case
What version of drizzle-orm are you using?
0.34.1
What version of drizzle-kit are you using?
0.25.0
Describe the Bug
When using casing: 'snake_case' in the config and defining a column like so firstName: t.text().notNull()
And then when accessing the column name via users.firstName.name we get firstName instead of first_name
Same happens if we get the columns via getTableConfig(users).columns and getTableColumns(users)
Expected behavior
No response
Environment & setup
No response
I'm seeing the same issue (using Vercel Postgres)
You need to pass casing to drizzle() too, not only in the drizzle-kit config.
export const db = drizzle(client, { schema, casing: 'snake_case' })
Thanks for the tip @alexandernanberg, I added that and everything seems to be working now!
I went back to the docs to see if I missed something and realized that there is a broken link within this snippet from the PostgreSQL column types page:
All examples in this part of the documentation do not use database column name aliases, and column names are generated from TypeScript keys.
You can use database aliases in column names if you want, and you can also use the casing parameter to define a mapping strategy for Drizzle.
You can read more about it here
I think I had previously tried clicking on that link and closed the page when I saw it was broken (seems to be using localhost). It turns out that the intended link - https://orm.drizzle.team/docs/sql-schema-declaration#shape-your-data-schema - contains the relevant info about adding the casing option to the Drizzle DB declaration.
Looks like someone already opened an issue for the broken link (https://github.com/drizzle-team/drizzle-orm/issues/3107), so I'll see if I can open a PR to fix that.
You need to pass
casingtodrizzle()too, not only in the drizzle-kit config.export const db = drizzle(client, { schema, casing: 'snake_case' })
I guess it's to be expected, but this doesn't affect the column name mappings for tables outside the context of a query. I've written a convenience method for onConflictDoUpdate and I'm still getting the camelCase column name:
export const excluded = (column: PgColumn) => {
return sql.raw(`excluded.${column.name}`);
}; // excluded.firstName
Same issue as @kaelruland - getting camelCase when snake_case is needed for excluded. As a workaround, I added the snake_case column name to the column definition.
This is my current workaround. I've committed to snake_case for the project so not a huge deal, but would be nice to inherit casing from the config.
import { toSnakeCase } from 'drizzle-orm/casing';
export const excluded = (column: PgColumn) => {
return sql.raw(`excluded.${toSnakeCase(column.name)}`);
};
You need to pass
casingtodrizzle()too, not only in the drizzle-kit config.export const db = drizzle(client, { schema, casing: 'snake_case' })
The missing part for me was the opposite. I included casing: 'snake_case' parameter in drizzle() but not in drizzle-kit config. I was following this guide, but configuring drizzle-kit is not mentioned there. Shall we mention drizzle-kit there?
I've added casing: "snake_case" to the drizzle config:
export const db = drizzle(pool, { schema, casing: "snake_case" });
However, when I use getTableConfig, the column names are in camelCase.
import { posts } from "@/schema/posts";
import { getTableConfig } from "drizzle-orm/pg-core";
export default function Page() {
const postsConfig = getTableConfig(posts);
return (
<div>
{postsConfig.columns.map((col) => (
<div key={col.name}>
{col.name}
</div>
))}
</div>
);
}
The only workaround I found was add the column name in snake_case to the schema. For example publishedAt: timestamp("published_at"), instead of publishedAt: timestamp(), or to rename the property name like published_at: timestamp()
Would be ideal if there was a way to access the actual database column name. Like col.actualColumnName. In addition to the javascript property name col.name.
Currently using v0.37.0.
I can confirm getTableColumns(table) with the snake_casing settings turned on returns the columns incorrectly as camelCase.
Table was defined like shortId: varchar({ length: 21 })
shortId: <ref *3> PgVarchar {
name: 'shortId',
keyAsName: true,
primary: false,
notNull: true,
default: undefined,
defaultFn: [Function (anonymous)],
onUpdateFn: undefined,
hasDefault: true,
isUnique: false,
uniqueName: 'deals_shortId_unique',
uniqueType: undefined,
dataType: 'string',
columnType: 'PgVarchar',
enumValues: undefined,
generated: undefined,
generatedIdentity: undefined,
config: {
name: 'shortId',
keyAsName: true,
notNull: true,
default: undefined,
hasDefault: true,
primaryKey: false,
isUnique: false,
uniqueName: 'deals_shortId_unique',
uniqueType: undefined,
dataType: 'string',
columnType: 'PgVarchar',
generated: undefined,
length: 21,
enumValues: undefined,
defaultFn: [Function (anonymous)]
},
table: PgTable {
id: [PgUUID],
shortId: [Circular *3],
name: [PgVarchar],
description: [PgVarchar],
createdBy: [PgUUID],
createdAt: [PgTimestamp],
updatedAt: [PgTimestamp],
deletedAt: [PgTimestamp],
enableRLS: [Function: enableRLS],
[Symbol(drizzle:Name)]: 'deals',
[Symbol(drizzle:OriginalName)]: 'deals',
[Symbol(drizzle:Schema)]: undefined,
[Symbol(drizzle:Columns)]: [Circular *2],
[Symbol(drizzle:ExtraConfigColumns)]: [Object],
[Symbol(drizzle:BaseName)]: 'deals',
[Symbol(drizzle:IsAlias)]: false,
[Symbol(drizzle:IsDrizzleTable)]: true,
[Symbol(drizzle:ExtraConfigBuilder)]: undefined,
[Symbol(drizzle:PgInlineForeignKeys)]: [Array],
[Symbol(drizzle:EnableRLS)]: false
},
length: 21
},
It should be
shortId: <ref *3> PgVarchar {
name: 'short_id',
keyAsName: false,
primary: false,
notNull: true,
default: undefined,
defaultFn: [Function (anonymous)],
onUpdateFn: undefined,
hasDefault: true,
isUnique: false,
uniqueName: 'deals_short_id_unique',
uniqueType: undefined,
dataType: 'string',
columnType: 'PgVarchar',
enumValues: undefined,
generated: undefined,
generatedIdentity: undefined,
config: {
name: 'short_id',
keyAsName: false,
notNull: true,
default: undefined,
hasDefault: true,
primaryKey: false,
isUnique: false,
uniqueName: 'deals_short_id_unique',
I'm guessing this is also related here.
When running drizzle-kit export the table names are also camelCased instead of snake_cased. drizzle-kit push works correctly.
I'm encountering this problem. Not sure if this helps but for me it seems to be with columns defined as enums.
export const fitnessLevel = pgEnum("fitness_level", [
"beginner",
"intermediate",
"advanced",
]);
export const user = pgTable("user", {
id: serial("id").primaryKey(),
name: varchar({ length: 256 }).notNull(),
email: varchar({ length: 256 }).notNull().unique(),
weight: real(),
height: real(),
fitnessLevel: fitnessLevel("fitness_level"), // without setting the name here, the column is created as fitnessLevel
...timestamps,
});
my db config is configured to use snake_case
const config = { schema, casing: "snake_case" } as DrizzleConfig<typeof schema>;
const pool = new Pool({ connectionString: env.DATABASE_URL });
export const db = dbWebsocket(pool, config);
You need to pass
casingtodrizzle()too, not only in the drizzle-kit config.export const db = drizzle(client, { schema, casing: 'snake_case' })
This doesn't work for me where do you get schema from? I tried to now import it from my schema.ts
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import { config } from "dotenv";
import * as schema from "./schema";
// Try loading from .env.local first, then .env
config({ path: ".env.local" });
config({ path: ".env" });
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema, casing: "snake_case" });
++ snake case not working for me.
"drizzle-orm": "^0.39.3"
"drizzle-kit": "^0.30.4"
export const connection = postgres(...
export const db = drizzle(connection, {
schema,
casing: 'snake_case'
});
getTableColumns() returns columns in camelCase instead of snake_case in migrations
I was able to get my snake_case in the db right by specifying the casing in both drizzle.config.ts and /src/db/index.ts, I believe this should be specified in the docs as I have spent hours making this work
I have recently set up a fresh Supabase database (PostgreSQL)
my current setup is as follow
// package.json
"dependencies": {
"drizzle-orm": "^0.42.0", // drizzle-orm
"next": "15.3.1",
"postgres": "^3.4.5", // postgres
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "^0.31.0", // drizzle-kit
"eslint": "^9",
"eslint-config-next": "15.3.1",
"tailwindcss": "^4",
"typescript": "^5"
}
my drizzle.config.ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit"
export default defineConfig({
out: "./drizzle",
schema: "./src/db/schema.ts",
dialect: "postgresql",
casing: "snake_case", // snake_case specified here
dbCredentials: {
url: process.env.DATABASE_URL!,
},
})
my /src/db/index.ts
// /src/db/index.ts
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
const client = postgres(process.env.DATABASE_URL!, { prepare: false })
export const db = drizzle({ client, casing: "snake_case" }) // snake_case specified here
my /src/db/schema.ts
// /src/db/schema.ts
import { pgTable, uuid, text, integer, timestamp } from "drizzle-orm/pg-core"
export const accountsTable = pgTable("accounts", {
id: uuid().primaryKey().defaultRandom(),
fullName: text().notNull(),
age: integer(),
createdAt: timestamp().defaultNow().notNull(),
updatedAt: timestamp().defaultNow().notNull(),
})
// notice the properties are in camelCase
executing npx drizzle-kit generate resulted in this migration file
# /drizzle/0000_grey_psynapse.sql
CREATE TABLE "accounts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"full_name" text NOT NULL,
"age" integer,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
# the columns have been converted to snake_case successfully
Thanks @salimdellali !! the part where I was missing is that I needed to put `casing: 'snake_case' in 2 places.
One at drizzle.config.ts
import { env } from '@/env'
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
schema: 'src/infra/db/schema/*',
out: 'src/infra/db/migrations',
dialect: 'postgresql',
dbCredentials: { url: env.DATABASE_URL },
casing: 'snake_case',
migrations: {
prefix: 'timestamp',
},
})
The other at the database drizzle client instance:
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import { env } from '@/env'
import { schema } from './schema'
export const client = postgres(env.DATABASE_URL)
export const db = drizzle(client, { schema, casing: 'snake_case' })
Adding casing: 'snake_case' to both drizzle client and drizzle config does fix creating tables in snake case but it doesn't return it in snake case when used with utils (e.g. getTableColumns) and with views which may lead drizzle studio to break
I had the same problem with fkey.getName() which is returning camel case in the middle of the key which is incorrect. Ironically I was going to use this getName method to create a structural test in an attempt to avoid this limitation.
Additionally unique keys are not snake cased either, but I think there's another issue for that specifically.
Hey everyone!
I've created this message to send in a batch to all opened issues we have, just because there are a lot of them and I want to update all of you with our current work, why issues are not responded to, and the amount of work that has been done by our team over ~8 months.
I saw a lot of issues with suggestions on how to fix something while we were not responding – so thanks everyone. Also, thanks to everyone patiently waiting for a response from us and continuing to use Drizzle!
We currently have 4 major branches with a lot of work done. Each branch was handled by different devs and teams to make sure we could make all the changes in parallel.
First branch is drizzle-kit rewrite
All of the work can be found on the alternation-engine branch. Here is a PR with the work done: https://github.com/drizzle-team/drizzle-orm/pull/4439
As you can see, it has 167k added lines of code and 67k removed, which means we've completely rewritten the drizzle-kit alternation engine, the way we handle diffs for each dialect, together with expanding our test suite from 600 tests to ~9k test units for all different types of actions you can do with kit. More importantly, we changed the migration folder structure and made commutative migrations, so you won't face complex conflicts on migrations when working in a team.
What's left here:
- We are finishing handling defaults for Postgres, the last being geometry (yes, we fixed the
sridissue here as well). - We are finishing commutative migrations for all dialects.
- We are finishing up the command, so the migration flow will be as simple as
drizzle-kit upfor you.
Where it brings us:
- We are getting drizzle-kit into a new good shape where we can call it
[email protected]!
Timeline:
- We need ~2 weeks to finish all of the above and send this branch to beta for testing.
Second big branch is a complex one with several HUGE updates
- Bringing Relational Queries v2 finally live. We've done a lot of work here to actually make it faster than RQBv1 and much better from a DX point of view. But in implementing it, we had to make another big rewrite, so we completely rewrote the drizzle-orm type system, which made it much simpler and improved type performance by ~21.4x:
(types instantiations for 3300 lines production drizzle schema + 990 lines relations)
TS v5.8.3: 728.8k -> 34.1k
TS v5.9.2: 553.7k -> 25.4k
You can read more about it here.
What's left here:
- We have 1 issue with TS that is already in progress of being fixed. The issue and Post about fixing.
Where it brings us:
- We are getting drizzle-orm into a new good shape where we can call it
[email protected]!
Breaking changes:
- We will have them, but we will have open channels for everyone building on top of drizzle types, so we can guide you through all the changes.
Third branch is adding support for CockroachDB and MSSQL dialects
Support for them is already in the alternation-engine branch and will be available together with the drizzle-kit rewrite.
Summary
All of the work we are doing is crucial and should be done sooner rather than later. We've received a lot of feedback and worked really hard to find the best strategies and decisions for API, DX, architecture, etc., so we can confidently mark it as v1 and be sure we can improve it and remain flexible for all the features you are asking for, while becoming even better for everyone building on top of the drizzle API as well.
We didn't want to stay with some legacy decisions and solutions we had, and instead wanted to shape Drizzle in a way that will be best looking ahead to 2025–2026 trends (v1 will get proper effect support, etc.).
We believe that all of the effort we've put in will boost Drizzle and benefit everyone using it.
Thanks everyone, as we said, we are here to stay for a long time to build a great tool together!
Timelines
We are hoping to get v1 for drizzle in beta this fall and same timeline for latest. Right after that we can go through all of the issues and PRs and resond everyone. v1 for drizzle should close ~70% of all the bug tickets we have, so on beta release we will start marking them as closed!