1. Project Overview & Quickstart (drizzle-team/drizzle-orm)
File: README.md
What's Drizzle?
Drizzle is a modern TypeScript ORM developers wanna use in their next project.
It is lightweight at only ~7.4kb minified+gzipped, and it's tree shakeable with exactly 0 dependencies.
Drizzle supports every PostgreSQL, MySQL and SQLite database, including serverless ones like Turso, Neon, Xata, PlanetScale, Cloudflare D1, FlyIO LiteFS, Vercel Postgres, Supabase and AWS Data API. No bells and whistles, no Rust binaries, no serverless adapters, everything just works out of the box.
Drizzle is serverless-ready by design. It works in every major JavaScript runtime like NodeJS, Bun, Deno, Cloudflare Workers, Supabase functions, any Edge runtime, and even in browsers.
With Drizzle you can be fast out of the box and save time and costs while never introducing any data proxies into your infrastructure.
While you can use Drizzle as a JavaScript library, it shines with TypeScript. It lets you declare SQL schemas and build both relational and SQL-like queries, while keeping the balance between type-safety and extensibility for toolmakers to build on top.
Ecosystem
While Drizzle ORM remains a thin typed layer on top of SQL, we made a set of tools for people to have best possible developer experience.
Drizzle comes with a powerful Drizzle Kit CLI companion for you to have hassle-free migrations. It can generate SQL migration files for you or apply schema changes directly to the database.
We also have Drizzle Studio for you to effortlessly browse and manipulate data in your database of choice.
Documentation
Check out the full documentation on the website.
Our sponsors โค๏ธ
File: changelogs/README.md
Release flow
- Push feature branch
- GitHub workflow publishes new feature tag to NPM
- Bump package versions manually
- (Optional) Create and merge PR to beta
- (Optional) GitHub workflow publishes new beta version to NPM
- Create PR to main
- TODO: GitHub workflow checks if changelog is present for every package version
- Resolve all conflicts, bump versions if necessary
- Merge PR
- GitHub workflow publishes new latest version to NPM and removes feature tag from NPM
File: docs/custom-types.lite.md
Common way of defining custom types
[!NOTE]
For more advanced documentation about defining custom data types in PostgreSQL and MySQL, please checkcustom-types.md.
Examples
Best way to see, how customType definition is working - is to check how existing data types in postgres and mysql could be defined using customType function from Drizzle ORM
Postgres Data Types using `node-postgres` driver
**Serial**
const customSerial = customType<{ data: number; notNull: true; default: true }>(
{
dataType() {
return 'serial';
},
},
);**Text**
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});**Boolean**
const customBoolean = customType<{ data: boolean }>({
dataType() {
return 'boolean';
},
});**Jsonb**
const customJsonb = (name: string) =>
customType<{ data: TData; driverData: string }>({
dataType() {
return 'jsonb';
},
toDriver(value: TData): string {
return JSON.stringify(value);
},
})(name);**Timestamp**
const customTimestamp = customType<
{
data: Date;
driverData: string;
config: { withTimezone: boolean; precision?: number };
}
>({
dataType(config) {
const precision = typeof config.precision !== 'undefined'
? ` (${config.precision})`
: '';
return `timestamp${precision}${
config.withTimezone ? ' with time zone' : ''
}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
});Usage for all types will be same as defined functions in Drizzle ORM
const usersTable = pgTable('users', {
id: customSerial('id').primaryKey(),
name: customText('name').notNull(),
verified: customBoolean('verified').notNull().default(false),
jsonb: customJsonb<string[]>('jsonb'),
createdAt: customTimestamp('created_at', { withTimezone: true }).notNull()
.default(sql`now()`),
});MySql Data Types using `mysql2` driver
**Serial**
const customSerial = customType<{ data: number; notNull: true; default: true }>(
{
dataType() {
return 'serial';
},
},
);**Text**
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});**Boolean**
const customBoolean = customType<{ data: boolean }>({
dataType() {
return 'boolean';
},
fromDriver(value) {
if (typeof value === 'boolean') {
return value;
}
return value === 1;
},
});**Json**
const customJson = (name: string) =>
customType<{ data: TData; driverData: string }>({
dataType() {
return 'json';
},
toDriver(value: TData): string {
return JSON.stringify(value);
},
})(name);**Timestamp**
const customTimestamp = customType<
{ data: Date; driverData: string; config: { fsp: number } }
>({
dataType(config) {
const precision = typeof config.fsp !== 'undefined'
? ` (${config.fsp})`
: '';
return `timestamp${precision}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
});Usage for all types will be same as defined functions in Drizzle ORM
const usersTable = mysqlTable('userstest', {
id: customSerial('id').primaryKey(),
name: customText('name').notNull(),
verified: customBoolean('verified').notNull().default(false),
jsonb: customJson<string[]>('jsonb'),
createdAt: customTimestamp('created_at', { fsp: 2 }).notNull().default(
sql`now()`,
),
});You can check ts-doc for types and param definition
export type CustomTypeValues = {
/**
* Required type for custom column, that will infer proper type model
*
* Examples:
*
* If you want your column to be `string` type after selecting/or on inserting - use `data: string`. Like `text`, `varchar`
*
* If you want your column to be `number` type after selecting/or on inserting - use `data: number`. Like `integer`
*/
data: unknown;
/**
* Type helper, that represents what type database driver is accepting for specific database data type
*/
driverData?: unknown;
/**
* What config type should be used for {@link CustomTypeParams} `dataType` generation
*/
config?: unknown;
/**
* Whether the config argument should be required or not
* @default false
*/
configRequired?: boolean;
/**
* If your custom data type should be notNull by default you can use `notNull: true`
*
* @example
* const customSerial = customType<{ data: number, notNull: true, default: true }>({
* dataType() {
* return 'serial';
* },
* });
*/
notNull?: boolean;
/**
* If your custom data type has default you can use `default: true`
*
* @example
* const customSerial = customType<{ data: number, notNull: true, default: true }>({
* dataType() {
* return 'serial';
* },
* });
*/
default?: boolean;
};
export interface CustomTypeParams<T extends Partial<CustomTypeValues>> {
/**
* Database data type string representation, that is used for migrations
* @example
* ```
* `jsonb`, `text`
* ```
*
* If database data type needs additional params you can use them from `config` param
* @example
* ```
* `varchar(256)`, `numeric(2,3)`
* ```
*
* To make `config` be of specific type please use config generic in {@link CustomTypeValues}
*
* @example
* Usage example
* ```
* dataType() {
* return 'boolean';
* },
* ```
* Or
* ```
* dataType(config) {
* return typeof config.length !== 'undefined' ? `varchar(${config.length})` : `varchar`;
* }
* ```
*/
dataType: (config: T['config'] | (Equal<T['configRequired'], true> extends true ? never : undefined)) => string;
/**
* Optional mapping function, between user input and driver
* @example
* For example, when using jsonb we need to map JS/TS object to string before writing to database
* ```
* toDriver(value: TData): string {
* return JSON.stringify(value);
* }
* ```
*/
toDriver?: (value: T['data']) => T['driverData'] | SQL;
/**
* Optional mapping function, that is responsible for data mapping from database to JS/TS code
* @example
* For example, when using timestamp we need to map string Date representation to JS Date
* ```
* fromDriver(value: string): Date {
* return new Date(value);
* },
* ```
*/
fromDriver?: (value: T['driverData']) => T['data'];
}File: docs/custom-types.md
How to define custom types
Drizzle ORM has a big set of predefined column types for different SQL databases. But still there are additional types that are not supported by Drizzle ORM (yet). That could be native pg types or extension types
Here are some instructions on how to create and use your own types with Drizzle ORM
Abstract view on column builder pattern in Drizzle ORM
Each type creation should use 2 classes:
ColumnBuilder- class, that is responsible for generating whole set of needed fields for column creationColumn- class, that is representing Columns itself, that is used in query generation, migration mapping, etc.Each module has it's own class, representing
ColumnBuilderorColumn:For
pg->PgColumnBuilderandPgColumnFor
mysql->MySqlColumnBuilderandMySqlColumnFor
sqlite->SQLiteColumnBuilderandSQLiteColumn
Builder class explanation - (postgresql text data type example)
Builder class is responsible for storing TS return type for specific database datatype and override build function to return ready to use column in table
TData- extends return type for column. Current example will infer string type for current datatype used in schema definition
export class PgTextBuilder
extends PgColumnBuilder<
ColumnBuilderConfig<{ data: TData; driverParam: string }>
>
{
build<TTableName extends string>(
table: AnyPgTable<{ name: TTableName }>,
): PgText<TTableName, TData> {
return new PgText(table, this.config);
}
}[!WARNING]
$pgColumnBuilderBrandshould be changed and be equal to class name for new data type builder
Column class explanation - (postgresql text data type example)
Column class has set of types/functions, that could be overridden to get needed behavior for custom type
TData- extends return type for column. Current example will infer string type for current datatype used in schema definitiongetSQLType()- function, that shows datatype name in database and will be used in migration generationmapFromDriverValue()- interceptor between database and select query execution. If you want to modify/map/change value for specific data type, it could be done here
Usage example for jsonb type
override mapToDriverValue(value: TData): string {
return JSON.stringify(value);
}mapToDriverValue- interceptor between user input for insert/update queries and database query. If you want to modify/map/change value for specific data type, it could be done here
Usage example for int type
override mapFromDriverValue(value: number | string): number {
if (typeof value === 'string') {
return parseInt(value);
}
return value;
}Column class example
export class PgText<TTableName extends string, TData extends string>
extends PgColumn<ColumnConfig<{ tableName: TTableName; data: TData; driverParam: string }>> {
constructor(table: AnyPgTable<{ name: TTableName }>, builder: PgTextBuilder['config']) {
super(table, builder);
}
getSQLType(): string {
return 'text';
}
override mapFromDriverValue(value: string): TData {
return value as TData
}
override mapToDriverValue(value: TData): string {
return value
}
}[!WARNING]
$pgColumnBrandshould be changed and be equal to class name for new data type
Full text data type for PostgreSQL example
For more postgres data type examples you could check here
import { ColumnConfig, ColumnBuilderConfig } from 'drizzle-orm';
import { AnyPgTable } from 'drizzle-orm/pg-core';
import { PgColumn, PgColumnBuilder } from './common';
export class PgTextBuilder
extends PgColumnBuilder<
ColumnBuilderConfig<{ data: TData; driverParam: string }>
>
{
build<TTableName extends string>(
table: AnyPgTable<{ name: TTableName }>,
): PgText<TTableName, TData> {
return new PgText(table, this.config);
}
}
export class PgText<TTableName extends string, TData extends string>
extends PgColumn<
ColumnConfig<{ tableName: TTableName; data: TData; driverParam: string }>
>
{
constructor(
table: AnyPgTable<{ name: TTableName }>,
builder: PgTextBuilder['config'],
) {
super(table, builder);
}
getSQLType(): string {
return 'text';
}
}
export function text<T extends string = string>(
name: string,
): PgTextBuilder<T> {
return new PgTextBuilder(name);
}Custom data type example
[!NOTE]
We will check example on pg module, but current pattern applies to all dialects, that are currently supported by Drizzle ORM
Setting up CITEXT datatype
[!NOTE]
This type is available only with extensions and used for example, just to show how you could setup any data type you want. Extension support will come soon
CITEXT data type example
export class PgCITextBuilder extends PgColumnBuilder<
PgColumnBuilderHKT,
ColumnBuilderConfig<{ data: TData; driverParam: string }>
> {
protected $pgColumnBuilderBrand: string = 'PgCITextBuilder';
build<TTableName extends string>(table: AnyPgTable<{ name: TTableName }>): PgCIText<TTableName, TData> {
return new PgCIText(table, this.config);
}
}
export class PgCIText<TTableName extends string, TData extends string>
extends PgColumn>
{
constructor(table: AnyPgTable<{ name: TTableName }>, builder: PgCITextBuilder['config']) {
super(table, builder);
}
getSQLType(): string {
return 'citext';
}
}
export function citext<T extends string = string>(name: string): PgCITextBuilder<T> {
return new PgCITextBuilder(name);
}Usage example
const table = pgTable('table', {
id: integer('id').primaryKey(),
ciname: citext('ciname')
})Contributing by adding new custom types in Drizzle ORM
You could add your created custom data types to Drizzle ORM, so everyone can use it.
Each data type should be placed in separate file in columns folder and PR open with tag new-data-type:pg | new-data-type:sqlite | new-data-type:mysql
For more Contribution information - please check CONTRIBUTING.md
File: docs/joins.md
Drizzle ORM - Joins
As with other parts of Drizzle ORM, the joins syntax is a balance between the SQL-likeness and type safety.
Here's an example of how a common "one-to-many" relationship can be modelled.
const users = pgTable('users', {
id: serial('id').primaryKey(),
firstName: text('first_name').notNull(),
lastName: text('last_name'),
cityId: int('city_id').references(() => cities.id),
});
const cities = pgTable('cities', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});Now, let's select all cities with all users that live in that city.
This is how you'd write it in raw SQL:
select
cities.id as city_id,
cities.name as city_name,
users.id as user_id,
users.first_name,
users.last_name
from cities
left join users on users.city_id = cities.idAnd here's how to do the same with Drizzle ORM:
const rows = await db
.select({
cityId: cities.id,
cityName: cities.name,
userId: users.id,
firstName: users.firstName,
lastName: users.lastName,
})
.from(cities)
.leftJoin(users, eq(users.cityId, cities.id));rows will have the following type:
{
cityId: number;
cityName: string;
userId: number | null;
firstName: string | null;
lastName: string | null;
}[]As you can see, all the joined columns have been nullified. This might do the trick if you're using joins to form a single row of results, but in our case we have two separate entities in our row - a city and a user.
It might not be very convenient to check every field for nullability separately (or, even worse, just add an ! after every field to "make compiler happy"). It would be much more useful if you could somehow run a single check
to verify that the user was joined and all of its fields are available.
To achieve that, you can group the fields of a certain table in a nested object inside of .select():
const rows = await db
.select({
cityId: cities.id,
cityName: cities.name,
user: {
id: users.id,
firstName: users.firstName,
lastName: users.lastName,
},
})
.from(cities)
.leftJoin(users, eq(users.cityId, cities.id));In that case, the ORM will use dark TypeScript magic (as if it wasn't already) and figure out that you have a nested object where all the fields belong to the same table. So, the rows type will now look like this:
{
cityId: number;
cityName: string;
user: {
id: number;
firstName: string;
lastName: string | null;
} | null;
}This is much more convenient! Now, you can just do a single check for row.user !== null, and all the user fields will become available.
Note that you can group any fields in a nested object however you like, but the single check optimization will only be applied to a certain nested object if all its fields belong to the same table.
So, for example, you can group the city fields, too:
.select({
city: {
id: cities.id,
name: cities.name,
},
user: {
id: users.id,
firstName: users.firstName,
lastName: users.lastName,
},
})And the result type will look like this:
{
city: {
id: number;
name: string;
};
user: {
id: number;
firstName: string;
lastName: string | null;
} | null;
}If you just need all the fields from all the tables you're selecting and joining, you can simply omit the argument of the .select() method altogether:
const rows = await db.select().from(cities).leftJoin(users, eq(users.cityId, cities.id));[!NOTE]
In this case, the Drizzle table/column names will be used as the keys in the result object.
{
cities: {
id: number;
name: string;
};
users: {
id: number;
firstName: string;
lastName: string | null;
cityId: number | null;
} | null;
}[]There are cases where you'd want to select all the fields from one table, but pick fields from others. In that case, instead of listing all the table fields, you can just pass a table:
.select({
cities, // shorthand for "cities: cities", the key can be anything
user: {
firstName: users.firstName,
},
}){
cities: {
id: number;
name: string;
};
user: {
firstName: string;
} | null;
}But what happens if you group columns from multiple tables in the same nested object? Nothing, really - they will still be all individually nullable, just grouped under the same object (as you might expect!):
.select({
id: cities.id,
cityAndUser: {
cityName: cities.name,
userId: users.id,
firstName: users.firstName,
lastName: users.lastName,
}
}){
id: number;
cityAndUser: {
cityName: string;
userId: number | null;
firstName: string | null;
lastName: string | null;
};
}Aggregating results
OK, so you have obtained all the cities and the users for every city. But what you really wanted is a list of users for every city, and what you currently have is an array of city-user? combinations. So, how do you transform it?
That's the neat part - you can do that however you'd like! No hand-holding here.
For example, one of the ways to do that would be Array.reduce():
import { InferModel } from 'drizzle-orm';
type User = InferModel<typeof users>;
type City = InferModel<typeof cities>;
const rows = await db
.select({
city: cities,
user: users,
})
.from(cities)
.leftJoin(users, eq(users.cityId, cities.id));
const result = rows.reduce<Record<number, { city: City; users: User[] }>>(
(acc, row) => {
const city = row.city;
const user = row.user;
if (!acc[city.id]) {
acc[city.id] = { city, users: [] };
}
if (user) {
acc[city.id].users.push(user);
}
return acc;
},
{},
);File: docs/table-introspect-api.md
Table introspect API
Get table information
import { pgTable, getTableConfig } from 'drizzle-orm/pg-core';
const table = pgTable(...);
const {
columns,
indexes,
foreignKeys,
checks,
primaryKeys,
name,
schema,
} = getTableConfig(table);Get table columns map
import { pgTable, getTableColumns } from 'drizzle-orm/pg-core';
const table = pgTable('table', {
id: integer('id').primaryKey(),
name: text('name'),
});
const columns/*: { id: ..., name: ... } */ = getTableColumns(table);File: drizzle-arktype/README.md
drizzle-arktype is a plugin for Drizzle ORM that allows you to generate arktype schemas from Drizzle ORM schemas.
Features
- Create a select schema for tables, views and enums.
- Create insert and update schemas for tables.
- Supports all dialects: PostgreSQL, MySQL and SQLite.
Usage
import { pgEnum, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema } from 'drizzle-arktype';
import { type } from 'arktype';
const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
role: text('role', { enum: ['admin', 'user'] }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// Schema for inserting a user - can be used to validate API requests
const insertUserSchema = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const updateUserSchema = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const selectUserSchema = createSelectSchema(users);
// Overriding the fields
const insertUserSchema = createInsertSchema(users, {
role: type('string'),
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const insertUserSchema = createInsertSchema(users, {
id: (schema) => schema.atLeast(1),
role: type('string'),
});
// Usage
const isUserValid = parse(insertUserSchema, {
name: 'John Doe',
email: '[email protected]',
role: 'admin',
});File: drizzle-kit/README.md
Drizzle Kit
Drizzle Kit is a CLI migrator tool for Drizzle ORM. It is probably the one and only tool that lets you completely automatically generate SQL migrations and covers ~95% of the common cases like deletions and renames by prompting user input.
https://github.com/drizzle-team/drizzle-kit-mirror - is a mirror repository for issues.
Documentation
Check the full documentation on the website.
How it works
Drizzle Kit traverses a schema module and generates a snapshot to compare with the previous version, if there is one.
Based on the difference, it will generate all needed SQL migrations. If there are any cases that can't be resolved automatically, such as renames, it will prompt the user for input.
For example, for this schema module:
// src/db/schema.ts
import { integer, pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
const users = pgTable("users", {
id: serial("id").primaryKey(),
fullName: varchar("full_name", { length: 256 }),
}, (table) => ({
nameIdx: index("name_idx", table.fullName),
})
);
export const authOtp = pgTable("auth_otp", {
id: serial("id").primaryKey(),
phone: varchar("phone", { length: 256 }),
userId: integer("user_id").references(() => users.id),
});It will generate:
CREATE TABLE IF NOT EXISTS auth_otp (
"id" SERIAL PRIMARY KEY,
"phone" character varying(256),
"user_id" INT
);
CREATE TABLE IF NOT EXISTS users (
"id" SERIAL PRIMARY KEY,
"full_name" character varying(256)
);
DO $$ BEGIN
ALTER TABLE auth_otp ADD CONSTRAINT auth_otp_user_id_fkey FOREIGN KEY ("user_id") REFERENCES users(id);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
CREATE INDEX IF NOT EXISTS users_full_name_index ON users (full_name);Installation & configuration
npm install -D drizzle-kitRunning with CLI options:
// package.json
{
"scripts": {
"generate": "drizzle-kit generate --out migrations-folder --schema src/db/schema.ts"
}
}npm run generateFile: drizzle-orm/src/knex/README.md
Drizzle ORM + Knex.js
This is a toolchain for integrating Drizzle with Knex.js.
Using Knex as a query builder
You can define you DB schema using Drizzle and use Knex as the query builder. With this approach you benefit from schema definition and automated migrations provided by Drizzle, and you can use Knex to build your queries. This might be helpful if you have an existing Knex.js project and you want to add Drizzle features to it.
This integration is based on official Knex TypeScript guide.
import Knex from 'knex';
import { pgTable, serial, text } from 'drizzle-orm/pg-core';
// This line is important - it allows you to use the Knexify type
import 'drizzle-orm/knex';
const test = pgTable('test', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
declare module 'knex/types/tables' {
interface Tables {
test: Knexify<typeof test>;
}
}
const db = Knex({});
const result/*: { id: number, name: string }[] */ = db('test').select();Wrapping Knex connection with Drizzle
Coming soon!
File: drizzle-orm/src/kysely/README.md
Drizzle ORM + Kysely
This is a toolchain for integrating Drizzle with Kysely.
Using Kysely as a query builder
You can define you DB schema using Drizzle and use Kysely as the query builder. With this approach you benefit from schema definition and automated migrations provided by Drizzle, and you can use Kysely to build your queries. This might be helpful if you have an existing Kysely project and you want to add Drizzle features to it.
import { Kysely, PostgresDialect } from 'kysely';
import { Pool } from 'pg';
import { Kyselify } from 'drizzle-orm/kysely';
import { pgTable, serial, text } from 'drizzle-orm/pg-core';
const test = pgTable('test', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
interface Database {
test: Kyselify<typeof test>;
}
const db = new Kysely<Database>({
dialect: new PostgresDialect({
pool: new Pool(),
}),
});
const result/*: { id: number, name: string }[] */ = db.selectFrom('test').selectAll().execute();2. Official Technical Reference & Guides (drizzle-team/drizzle-orm-docs)
File: README.md
๐ Project Structure
MDX files are located in this folder:
โโโ src/
โ โโโ content/
โ โ โโโ docsAnnouncements markdown files:
โโโ src/
โ โโโdata/
โ โ โโโ announcementsRoadmap markdown file:
โโโ src/
โ โโโdata/
โ โ โโโ roadmap.mdShipping section yaml file:
โโโ src/
โ โโโdata/
โ โ โโโ shipping.yamlprogress: number
weeks:
- date:
start: "YYYY-MM-DD"
details:
- string๐ง Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
|---|---|
pnpm install |
Installs dependencies |
pnpm run dev |
Starts local dev server at localhost:4321 |
pnpm run build |
Build your production site to ./dist/ |
pnpm run preview |
Preview your build locally, before deploying |
pnpm run astro ... |
Run CLI commands like astro add, astro check |
pnpm run astro -- --help |
Get help using the Astro CLI |
File: src/content/docs/cockroach/aliases.mdx
import Section from '@mdx/Section.astro';
Aliases
Drizzle supports aliasing for tables, columns, subqueries and CTEs โ mapping directly to the SQL AS keyword.
Table aliasing
You can alias tables using the alias function. This is useful when you need to join the same table multiple times,
for example in self-referencing relationships like employees and their managers:
const employees = cockroachTable("employees", {
id: int4().primaryKey(),
name: string(),
managerId: int4("manager_id"),
});
const manager = alias(employees, "manager");
await db.select({
employeeName: employees.name,
managerName: manager.name,
})
.from(employees)
.leftJoin(manager, eq(employees.managerId, manager.id));
```sql
select "employees"."name", "manager"."name"
from "employees"
left join "employees" as "manager" on "employees"."manager_id" = "manager"."id";Column aliasing
You can alias columns using the .as() method on columns and sql expressions.
This maps directly to the SQL AS keyword and lets you control the name of a column in the query result:
You can also use .as() with sql expressions:
Subquery aliasing
When using a subquery as a data source, you must provide an alias using .as().
This lets you reference the subquery's columns in the outer query:
const result = await db.select().from(sq);
```sql
select "id", "name", "age" from (select "id", "name", "age" from "users" where "users"."id" = 42) "sq";Subqueries can also be used in joins:
const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id));
```sql
select "users"."id", "users"."name", "users"."age", "sq"."id", "sq"."name", "sq"."age"
from "users"
left join (select "id", "name", "age" from "users" where "users"."id" = 42) "sq"
on "users"."id" = "sq"."id";CTE aliasing
Common table expressions (CTEs) are aliased using db.$with('alias').
The alias name is used to reference the CTE in the main query:
const result = await db.with(sq).select().from(sq);
```sql
with "sq" as (select "id", "name", "age" from "users" where "users"."id" = 42)
select "id", "name", "age" from "sq";When using sql expressions inside a CTE, you need to alias them with .as() to be able to reference them in the outer query:
const result = await db.with(sq).select({ name: sq.name }).from(sq);
```sql
with "sq" as (select upper("name") as "name" from "users")
select "name" from "sq";File: src/content/docs/cockroach/arktype.mdx
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
arktype
Install the dependencies
Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
import { int4, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import type { ArkErrors } from 'arktype';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: ArkErrors | { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: ArkErrors | { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Will parse successfullyViews and enums are also supported.
import { cockroachEnum, cockroachView } from 'drizzle-orm/cockroach-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/arktype';
import type { ArkErrors } from 'arktype';
const roles = cockroachEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: 'admin' | 'basic' = rolesSchema(...);
const usersView = cockroachView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: ArkErrors | { id: number; name: string; age: number } = usersViewSchema(...);Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
import { int4, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createInsertSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from "arktype";
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: ArkErrors | { name: string, age: number } = userInsertSchema(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: ArkErrors | { name: string, age: number } = userInsertSchema(user); // Will parse successfully
if (parsed instanceof ArkErrors) {
console.error(parsed.summary);
process.exit(1);
}
await db.insert(users).values(parsed);Update schema
Defines the shape of data to be updated in the database - can be used to validate API requests.
import { int4, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { eq } from "drizzle-orm";
import { ArkErrors } from 'arktype';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: ArkErrors | { name?: string | undefined, age?: number | undefined } = userUpdateSchema(user); // Will parse successfully
if (parsed instanceof ArkErrors) {
console.error(parsed.summary);
process.exit(1);
}
await db.update(users).set(parsed).where(eq(users.name, 'Jane'));Refinements
Each create schema function accepts an additional optional parameter that you can used to extend, modify or completely overwite a field's schema. Defining a callback function will extend or modify while providing a arktype schema will overwrite it.
import { int4, jsonb, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors, type } from 'arktype';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
bio: text(),
preferences: jsonb()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => schema.atMostLength(20), // Extends schema
bio: () => type.string.atMostLength(2000), // Extends schema before becoming nullable/optional
preferences: type({ theme: 'string' }) // Overwrites the field, including its nullability
});
const parsed: ArkErrors | {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = userSelectSchema(...);Data type reference
CockroachDB data type mappings follow the CockroachDB column builders. See the CockroachDB data types page for the full column reference.
File: src/content/docs/cockroach/cache.mdx
import Callout from '@mdx/Callout.astro';
import Npm from '@mdx/Npm.astro';
Cache
Drizzle sends every query straight to your database by default. There are no hidden actions, no automatic caching
or invalidation - you'll always see exactly what runs. If you want caching, you must opt in.
By default, Drizzle uses a explicit caching strategy (i.e. global: false), so nothing is ever cached unless you ask.
This prevents surprises or hidden performance traps in your application.
Alternatively, you can flip on all caching (global: true) so that every select will look in cache first.
Quickstart
Upstash integration
Drizzle provides an upstashCache() helper out of the box. By default, this uses Upstash Redis with automatic configuration if environment variables are set.
import { upstashCache } from "drizzle-orm/cache/upstash";
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DB_URL!, {
cache: upstashCache({ token: process.env.UPSTASH_TOKEN, url: process.env.UPSTASH_URL }),
});You can also explicitly define your Upstash credentials, enable global caching for all queries by default or pass custom caching options:
import { upstashCache } from "drizzle-orm/cache/upstash";
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DB_URL!, {
cache: upstashCache({
// ๐ Redis credentials (optional โ can also be pulled from env vars)
url: '',
token: '',
// ๐ Enable caching for all queries by default (optional)
global: true,
// ๐ Default cache behavior (optional)
config: { ex: 60 }
})
});Cache config reference
Drizzle supports the following cache config options for Upstash:
export type CacheConfig = {
/**
* Expiration in seconds (positive integer)
*/
ex?: number;
/**
* Set an expiration (TTL or time to live) on one or more fields of a given hash key.
* Used for HEXPIRE command
*/
hexOptions?: "NX" | "nx" | "XX" | "xx" | "GT" | "gt" | "LT" | "lt";
};Cache usage examples
Once you've configured caching, here's how the cache behaves:
Case 1: Drizzle with global: false (default, opt-in caching)
import { upstashCache } from "drizzle-orm/cache/upstash";
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DB_URL!, {
// ๐ `global: true` is not passed, false by default
cache: upstashCache({ url: "", token: "" }),
});In this case, the following query won't read from cache
const res = await db.select().from(users);
// Any mutate operation will still trigger the cache's onMutate handler
// and attempt to invalidate any cached queries that involved the affected tables
await db.insert(users).value({ email: "[email protected]" });To make this query read from the cache, call .$withCache()
const res = await db.select().from(users).$withCache();.$withCache has a set of options you can use to manage and configure this specific query strategy
// rewrite the config for this specific query
.$withCache({ config: {} })
// give this query a custom cache key (instead of hashing query+params under the hood)
.$withCache({ tag: 'custom_key' })
// turn off auto-invalidation for this query
// note: this leads to eventual consistency (explained below)
.$withCache({ autoInvalidate: false })This example is only relevant if you manually set autoInvalidate: false. By default, autoInvalidate is enabled.
You might want to turn off autoInvalidate if:
- your data doesn't change often, and slight staleness is acceptable (e.g. product listings, blog posts)
- you handle cache invalidation manually
In those cases, turning it off can reduce unnecessary cache invalidation. However, in most cases, we recommend keeping the default enabled.
Example: Imagine you cache the following query on usersTable with a 3-second TTL:
const recent = await db
.select().from(usersTable)
.$withCache({ config: { ex: 3 }, autoInvalidate: false });If someone runs db.insert(usersTable)... the cache won't be invalidated immediately. For up to 3 seconds, you'll keep seeing the old data until it eventually becomes consistent.
Case 2: Drizzle with global: true option
import { upstashCache } from "drizzle-orm/cache/upstash";
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DB_URL!, {
cache: upstashCache({ url: "", token: "", global: true }),
});In this case, the following query will read from cache
const res = await db.select().from(users);If you want to disable cache for this specific query, call .$withCache(false)
// disable cache for this query
const res = await db.select().from(users).$withCache(false);You can also use cache instance from a db to invalidate specific tables or tags
// Invalidate all queries that use the `users` table. You can do this with the Drizzle instance.
await db.$cache.invalidate({ tables: users });
// or
await db.$cache.invalidate({ tables: [users, posts] });
// Invalidate all queries that use the `usersTable`. You can do this by using just the table name.
await db.$cache.invalidate({ tables: "usersTable" });
// or
await db.$cache.invalidate({ tables: ["usersTable", "postsTable"] });
// You can also invalidate custom tags defined in any previously executed select queries.
await db.$cache.invalidate({ tags: "custom_key" });
// or
await db.$cache.invalidate({ tags: ["custom_key", "custom_key1"] });Custom cache
This example shows how to plug in a custom cache in Drizzle: you provide functions to fetch data from the cache, store results back into cache, and invalidate entries whenever a mutation runs.
Cache extension provides this set of config options
export type CacheConfig = {
/** expire time, in seconds */
ex?: number;
/** expire time, in milliseconds */
px?: number;
/** Unix time (sec) at which the key will expire */
exat?: number;
/** Unix time (ms) at which the key will expire */
pxat?: number;
/** retain existing TTL when updating a key */
keepTtl?: boolean;
/** options for HEXPIRE (hash-field TTL) */
hexOptions?: 'NX' | 'XX' | 'GT' | 'LT' | 'nx' | 'xx' | 'gt' | 'lt';
};const db = drizzle(process.env.DB_URL!, { cache: new TestGlobalCache() });/* Detailed source-code truncated for AI context efficiency. */Limitations
Queries that won't be handled by the `cache` extension:
- Using cache with raw queries, such as:
db.execute(sql`select 1`);- Using cache in transactions
```ts
await db.transaction(async (tx) => {
await tx.update(accounts).set(...).where(...);
await tx.update...
});Limitations that are temporary and will be handled soon:
- Using cache with Drizzle Relational Queries
await db._query.users.findMany();- Using cache with views
File: src/content/docs/cockroach/column-types.mdx
title: CockroachDB column types
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Npm from '@mdx/Npm.astro';
We have native support for all of them, yet if that's not enough for you, feel free to create custom types.
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
bigint
int int8 int64 integer
Signed 8-byte integer
If you're expecting values above 2^31 but below 2^53, you can utilize mode: 'number' and deal with javascript number as opposed to bigint.
import { int8, bigint, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
int8: int8({ mode: 'number' })
bigint: bigint({ mode: 'number' })
});
// will be inferred as `number`
int8: int8({ mode: 'number' })
bigint: bigint({ mode: 'number' })
// will be inferred as `bigint`
int8: int8({ mode: 'bigint' })
bigint: bigint({ mode: 'bigint' })CREATE TABLE "table" (
"int8" int8,
"bigint" int8
);export const table = cockroachTable('table', {
bigint1: bigint().default(10),
bigint2: int8().default(10),
});
```sql
CREATE TABLE "table" (
"bigint1" bigint DEFAULT 10,
"bigint2" int8 DEFAULT 10
);smallint
smallint int2
Small-range signed 2-byte integer
export const table = cockroachTable('table', {
int2: int2(),
smallint: smallint()
});
```sql
CREATE TABLE "table" (
"int2" int2,
"smallint" smallint
);export const table = cockroachTable('table', {
smallint1: int2().default(10),
smallint2: smallint().default(10)
});
```sql
CREATE TABLE "table" (
"smallint1" int2 DEFAULT 10,
"smallint2" int2 DEFAULT 10
);int4
Signed 4-byte integer
export const table = cockroachTable('table', {
int4: int4()
});
```sql
CREATE TABLE "table" (
"int4" int4
);export const table = cockroachTable('table', {
int1: int4().default(10)
});
```sql
CREATE TABLE "table" (
"int1" int4 DEFAULT 10
);int8
An alias of bigint.
int2
An alias of smallint.
---
bool
Cockroach provides the standard SQL type bool.
For more info please refer to the official Cockroach docs.
export const table = cockroachTable('table', {
boolean: bool()
});
```sql
CREATE TABLE "table" (
"boolean" bool
);---
string
text varchar, char
The STRING data type stores a string of Unicode characters.
VARCHAR (and alias CHARACTER VARYING)CHAR (and alias CHARACTER)NAME
For more info please refer to the official CockroachDB docs.
You can define { enum: ["value1", "value2"] } config to infer insert and select types, it won't check runtime values.
import { string, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
stringColumn: string(), // equivalent to `text` PostgreSQL type
stringColumn1: string({ length: 256 }), // equivalent to `varchar(256)` PostgreSQL type
});
// will be inferred as text: "value1" | "value2" | null
stringColumn: string({ enum: ["value1", "value2"] })CREATE TABLE "table" (
"stringColumn" string,
"stringColumn1" string(256),
);text
CockroachDB alias for STRING:
For more info please refer to the official CockroachDB docs.
You can define { enum: ["value1", "value2"] } config to infer insert and select types, it won't check runtime values.
import { text, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
text: text()
});
// will be inferred as text: "value1" | "value2" | null
text: text({ enum: ["value1", "value2"] })CREATE TABLE "table" (
"text" string
);varchar
character varying(n) varchar(n)
STRING alias used to stay compatible with PostgreSQL
For more info please refer to the official CockroachDB docs.
You can define { enum: ["value1", "value2"] } config to infer insert and select types, it won't check runtime values.
The length parameter is optional for CockroachDB string types.
import { varchar, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
varchar1: varchar(),
varchar2: varchar({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
varchar: varchar({ enum: ["value1", "value2"] }),CREATE TABLE "table" (
"varchar1" varchar,
"varchar2" varchar(256)
);char
character(n) char(n)
STRING alias used to stay compatible with PostgreSQL
For more info please refer to the official CockroachDB docs.
You can define { enum: ["value1", "value2"] } config to infer insert and select types, it won't check runtime values.
The length parameter is optional for CockroachDB string types.
import { char, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
char1: char(),
char2: char({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
char: char({ enum: ["value1", "value2"] }),CREATE TABLE "table" (
"char1" char,
"char2" char(256)
);---
https://www.cockroachlabs.com/docs/stable/float
decimal
numeric decimal dec
The DECIMAL data type stores exact, fixed-point numbers. This type is used when it is important to preserve exact precision, for example, with monetary data.
For more info please refer to the official CockroachDB docs.
export const table = cockroachTable('table', {
decimal1: decimal(),
decimal2: decimal({ precision: 100 }),
decimal3: decimal({ precision: 100, scale: 20 }),
decimalNum: decimal({ mode: 'number' }),
decimalBig: decimal({ mode: 'bigint' }),
});
```sql
CREATE TABLE "table" (
"decimal1" decimal,
"decimal2" decimal(100),
"decimal3" decimal(100, 20),
"decimalNum" decimal,
"decimalBig" decimal
);numeric
An alias of decimal.
float
float float8 double precision
For more info please refer to the official CockroachDB docs.
const table = cockroachTable('table', {
float1: float(),
float2: float().default(10.10),
float3: float().default(sql'10.10'::float),
});
```sql
CREATE TABLE "table" (
"float1" float,
"float2" float default 10.10,
"float3" float default '10.10'::float
);real
real float4
Single precision floating-point number (4 bytes)
For more info please refer to the official CockroachDB docs.
const table = cockroachTable('table', {
real1: real(),
real2: real().default(10.10),
real3: real().default(sql'10.10'::real),
});
```sql
CREATE TABLE "table" (
"real1" real,
"real2" real default 10.10,
"real3" real default '10.10'::real
);double precision
An alias of float.
---
jsonb
jsonb
The JSONB data type stores JSON (JavaScript Object Notation) data as a binary representation of the JSONB value, which eliminates whitespace, duplicate keys, and key ordering
For more info please refer to the official CockroachDB docs.
import { jsonb, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
jsonb1: jsonb(),
jsonb2: jsonb().default({ foo: "bar" }),
jsonb3: jsonb().default(sql`'{foo: "bar"}'::jsonb`),
});CREATE TABLE "table" (
"jsonb1" jsonb,
"jsonb2" jsonb default '{"foo": "bar"}'::jsonb,
"jsonb3" jsonb default '{"foo": "bar"}'::jsonb
);You can specify .$type<..>() for json object inference, it won't check runtime values.
It provides compile time protection for default values, insert and select schemas.
// will be inferred as { foo: string }
jsonb: jsonb().$type<{ foo: string }>();
// will be inferred as string[]
jsonb: jsonb().$type<string[]>();
// won't compile
jsonb: jsonb().$type<string[]>().default({});---
bit
bit
The BIT data types store bit arrays. With BIT, the length is fixed.
Size
The number of bits in a BIT value is determined as follows:
| Type declaration | Logical size |
|:------------------ |:-------------- |
| BIT | 1 bit |
| BIT(N) | N bits |
For more info please refer to the official CockroachDB docs.
import { sql } from "drizzle-orm";
import { cockroachTable, bit } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
bit1: bit(),
bit2: bit({ length: 15 }),
bit3: bit({ length: 15 }).default('10011'),
bit4: bit({ length: 15 }).default(sql`'10011'`)
});CREATE TABLE "table" (
"bit1" bit,
"bit2" bit(15),
"bit3" bit(15) DEFAULT '10011',
"bit4" bit(15) DEFAULT '10011'
);varbit
varbit
The VARBIT data types store bit arrays. With VARBIT, the length can be variable.
Size
The number of bits in a VARBIT value is determined as follows:
| Type declaration | Logical size |
|:------------------ |:-------------- |
| VARBIT | variable with no maximum |
| VARBIT(N) | variable with a maximum of N bits |
For more info please refer to the official CockroachDB docs.
import { sql } from "drizzle-orm";
import { cockroachTable, bit } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
varbit1: varbit(),
varbit2: varbit({ length: 15 }),
varbit3: varbit({ length: 15 }).default('10011'),
varbit4: varbit({ length: 15 }).default(sql`'10011'`)
});CREATE TABLE "table" (
"varbit1" varbit,
"varbit2" varbit(15),
"varbit3" varbit(15) DEFAULT '10011',
"varbit4" varbit(15) DEFAULT '10011'
);---
uuid
uuid
The UUID (Universally Unique Identifier) data type stores a 128-bit value that is unique across both space and time.
For more info please refer to the official CockroachDB docs.
import { uuid, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
uuid1: uuid(),
uuid2: uuid().defaultRandom(),
uuid3: uuid().default('a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11')
});CREATE TABLE "table" (
"uuid1" uuid,
"uuid2" uuid default gen_random_uuid(),
"uuid3" uuid default 'a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11'
);---
time
time timetz time with timezone time without timezone
The TIME data type stores the time of day in UTC.
The TIMETZ data type stores a time of day with a time zone offset from UTC.
For more info please refer to the official CockroachDB docs.
const table = cockroachTable('table', {
time1: time(),
time2: time({ withTimezone: true }),
time3: time({ precision: 6 }),
time4: time({ precision: 6, withTimezone: true })
});
```sql
CREATE TABLE "table" (
"time1" time,
"time2" time with timezone,
"time3" time(6),
"time4" time(6) with timezone
);timestamp
timestamp timestamptz timestamp with time zone timestamp without time zone
The TIMESTAMP and TIMESTAMPTZ data types store a date and time pair in UTC.
For more info please refer to the official CockroachDB docs.
import { sql } from "drizzle-orm";
import { timestamp, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
timestamp1: timestamp(),
timestamp2: timestamp({ precision: 6, withTimezone: true }),
timestamp3: timestamp().defaultNow(),
timestamp4: timestamp().default(sql`now()`),
});CREATE TABLE "table" (
"timestamp1" timestamp,
"timestamp2" timestamp (6) with time zone,
"timestamp3" timestamp default now(),
"timestamp4" timestamp default now()
);You can specify either date or string infer modes:
// will infer as date
timestamp: timestamp({ mode: "date" }),
// will infer as string
timestamp: timestamp({ mode: "string" }),The
stringmode does not perform any mappings for you. This mode was added to Drizzle ORM to provide developers
with the possibility to handle dates and date mappings themselves, depending on their needs.
Drizzle will pass raw dates as stringstoandfromthe database, so the behavior should be as predictable as possible
and aligned 100% with the database behavior
The
datemode is the regular way to work with dates. Drizzle will take care of all mappings between the database and the JS Date object
date
date
The DATE data type stores a year, month, and day.
For more info please refer to the official CockroachDB docs.
import { date, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
date: date(),
});CREATE TABLE "table" (
"date" date
);// will infer as string
date: date({ mode: "string" }),
### interval
`interval`
Stores a value that represents a span of time.
For more info please refer to the official CockroachDB **[docs.](https://www.cockroachlabs.com/docs/stable/interval)**
<Section>
```typescript
import { interval, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
interval1: interval(),
interval2: interval({ fields: 'day' }),
interval3: interval({ fields: 'month' , precision: 6 }),
});CREATE TABLE "table" (
"interval1" interval,
"interval2" interval day,
"interval3" interval(6) month
);---
enum
enum enumerated types
Enumerated (enum) types are data types that comprise a static, ordered set of values.
They are equivalent to the enum types supported in a number of programming languages.
An example of an enum type might be the days of the week, or a set of status values for a piece of data.
For more info please refer to the official CockroachDB docs.
import { cockroachEnum, cockroachTable } from "drizzle-orm/cockroach-core";
export const moodEnum = cockroachEnum('mood', ['sad', 'ok', 'happy']);
export const table = cockroachTable('table', {
mood: moodEnum(),
});CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE "table" (
"mood" mood
);---
geometry
geometry
The GEOMETRY data type stores 2D spatial data
For more info please refer to the official CockroachDB docs.
export const table = cockroachTable('table', {
geo1: geometry({ type: 'point' }),
geo2: geometry({ type: 'point', mode: 'xy' }),
geo3: geometry({ type: 'point', srid: 4326 }),
});
// will be inferred as [number, number]
geo1: geometry({ type: 'point' })
// will be inferred as { x: number, y: number }
geo2: geometry({ type: 'point', mode: 'xy' })
```sql
CREATE TABLE "table" (
"geo1" geometry(point),
"geo2" geometry(point),
"geo3" geometry(point,4326)
);inet
inet
The INET data type stores an IPv4 or IPv6 address
For more info please refer to the official CockroachDB docs.
export const table = cockroachTable('table', {
inet: inet(),
});
```sql
CREATE TABLE "table" (
"inet" inet
);vector
vector
The VECTOR data type stores fixed-length arrays of floating-point numbers, which represent data points in multi-dimensional space
You must specify the number of dimensions for the vector.
export const table = cockroachTable('table', {
embedding: vector({ dimensions: 3 }),
});
```sql
CREATE TABLE "table" (
"embedding" vector(3)
);---
Customizing data type
Every column builder has a .$type() method, which allows you to customize the data type of the column.
This is useful, for example, with unknown or branded types:
type UserId = number & { __brand: 'user_id' };
type Data = {
foo: string;
bar: number;
};
const users = cockroachTable('users', {
id: int4().$type().primaryKey(),
jsonbField: jsonb().$type<Data>(),
});Identity Columns
PostgreSQL and CockroachDB supports identity columns as a way to automatically generate unique int4 values for a column. These values are generated using sequences and can be defined using the GENERATED AS IDENTITY clause.
Types of Identity Columns
GENERATED ALWAYS AS IDENTITY: The database always generates a value for the column. Manual insertion or updates to this column are not allowed unless the OVERRIDING SYSTEM VALUE clause is used.GENERATED BY DEFAULT AS IDENTITY: The database generates a value by default, but manual values can also be inserted or updated. If a manual value is provided, it will be used instead of the system-generated value.
Usage example
import { cockroachTable, int4, text } from 'drizzle-orm/cockroach-core'
export const ingredients = cockroachTable("ingredients", {
id: int4().primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }),
name: text().notNull(),
});You can specify all properties available for sequences in the .generatedAlwaysAsIdentity() function. Additionally, you can specify custom names for these sequences
CockroachDB docs reference.
Default value
The DEFAULT clause specifies a default value to use for the column if no value
is explicitly provided by the user when doing an INSERT.
If there is no explicit DEFAULT clause attached to a column definition,
then the default value of the column is NULL.
An explicit DEFAULT clause may specify that the default value is NULL,
a string constant, a blob constant, a signed-number, or any constant expression enclosed in parentheses.
export const table = cockroachTable('table', {
int: int4().default(42),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sqlgen_random_uuid()),
});
```sql
CREATE TABLE IF NOT EXISTS "table" (
"int" int4 DEFAULT 42,
"uuid1" uuid DEFAULT gen_random_uuid(),
"uuid2" uuid DEFAULT gen_random_uuid()
);When using $default() or $defaultFn(), which are simply different aliases for the same function,
you can generate defaults at runtime and use these values in all insert queries.
These functions can assist you in utilizing various implementations such as uuid, cuid, cuid2, and many more.
import { text, cockroachTable } from "drizzle-orm/cockroach-core";
import { createId } from '@paralleldrive/cuid2';
export const table = cockroachTable('table', {
id: text().$defaultFn(() => createId()),
});When using $onUpdate() or $onUpdateFn(), which are simply different aliases for the same function,
you can generate defaults at runtime and use these values in all update queries.
Adds a dynamic update value to the column. The function will be called when the row is updated,
and the returned value will be used as the column value if none is provided.
If no default (or $defaultFn) value is provided, the function will be called
when the row is inserted as well, and the returned value will be used as the column value.
import { int4, timestamp, text, cockroachTable } from "drizzle-orm/cockroach-core";
const table = cockroachTable('table', {
updateCounter: int4().default(sql`1`).$onUpdateFn((): SQL => sql`${table.update_counter} + 1`),
updatedAt: timestamp({ mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text().$type<string | null>().$onUpdate(() => null),
});Not null
NOT NULL constraint dictates that the associated column may not contain a NULL value.
const table = cockroachTable('table', {
int4: int4().notNull(),
});
```sql
CREATE TABLE IF NOT EXISTS "table" (
"int4" int4 NOT NULL
);Primary key
A primary key constraint indicates that a column, or group of columns, can be used as a unique identifier for rows in the table.
This requires that the values be both unique and not null.
import { int4, cockroachTable } from 'drizzle-orm/cockroach-core';
export const table = cockroachTable('table', {
id: int4().primaryKey(),
});CREATE TABLE IF NOT EXISTS "table" (
"id" int4 PRIMARY KEY
);File: src/content/docs/cockroach/connect-overview.mdx
Database connection with Drizzle
Use these CockroachDB drivers and providers with Drizzle ORM.
CockroachDB connection docs
File: src/content/docs/cockroach/custom-types.mdx
import Callout from "@mdx/Callout.astro";
Custom types
Custom types let you define your own column types with custom SQL data types and JS/DB value transforms
Cockroach exposes customType from drizzle-orm/cockroach-core.
Examples
The best way to see how customType definition is working is to check how existing data types could be defined using customType function from Drizzle ORM.
Integer
import { customType, cockroachTable } from 'drizzle-orm/cockroach-core';
const customInteger = customType<{ data: number; }>(
{
dataType() {
return 'int4';
},
},
);
export const users = cockroachTable("users", {
id: customInteger(),
});Text
import { customType, cockroachTable } from 'drizzle-orm/cockroach-core';
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});
export const users = cockroachTable('users', {
name: customText(),
});Timestamp
import { customType, cockroachTable } from 'drizzle-orm/cockroach-core';
const customTimestamp = customType<{
data: Date;
driverData: string;
config: { withTimezone: boolean; precision?: number };
configRequired: false;
}>({
dataType(config) {
const precision = typeof config?.precision !== 'undefined' ? ` (${config.precision})` : '';
return `timestamp${config?.withTimezone ? 'tz' : ''}${precision}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
});
export const users = cockroachTable('users', {
createdAt: customTimestamp('created_at'),
});Usage for all types will be same as defined functions in Drizzle ORM. For example:
const usersTable = cockroachTable('users', {
id: customInteger().primaryKey(),
name: customText().notNull(),
createdAt: customTimestamp('created_at', { withTimezone: true }).notNull()
.default(sql`now()`),
});TS-doc for type definitions
/* Detailed source-code truncated for AI context efficiency. */* `jsonb`, `text`
* ```
*
* If database data type needs additional params you can use them from `config` param
* @example
* ```
* `varchar(256)`, `numeric(2,3)`
* ```
*
* To make `config` be of specific type please use config generic in {@link CustomTypeValues}
*
* @example
* Usage example
* ```
* dataType() {
* return 'boolean';
* },
* ```
* Or
* ```
* dataType(config) {
* return typeof config.length !== 'undefined' ? `varchar(${config.length})` : `varchar`;
* }
* ```
*/
dataType: (config: T['config'] | (Equal<T['configRequired'], true> extends true ? never : undefined)) => string;
/**
* Optional mapping function, that is used to transform inputs from desired to be used in code format to one suitable for driver
* @example
* For example, when using jsonb we need to map JS/TS object to string before writing to database
* ```
* toDriver(value: TData): string {
* return JSON.stringify(value);
* }
* ```
*/
toDriver?: (value: T['data']) => T['driverData'] | SQL;
/**
* Optional mapping function, that is used for transforming data returned by driver to desired column's output format
* @example
* For example, when using timestamp we need to map string Date representation to JS Date
* ```
* fromDriver(value: string): Date {
* return new Date(value);
* }
* ```
*
* It'll cause the returned data to change from:
* ```
* {
* customField: "2025-04-07T03:25:16.635Z";
* }
* ```
* to:
* ```
* {
* customField: new Date("2025-04-07T03:25:16.635Z");
* }
* ```
*/
fromDriver?: (value: 'driverOutput' extends keyof T ? T['driverOutput'] : T['driverData']) => T['data'];
/**
* Optional mapping function, that is used for transforming data returned by transofmed to JSON in database data to desired format
*
* Used by [relational queries](https://orm.drizzle.team/docs/rqb)
*
* Defaults to {@link fromDriver} function
* @example
* For example, when querying bigint column via [RQB](https://orm.drizzle.team/docs/rqb) or [JSON functions](https://orm.drizzle.team/docs/json-functions), the result field will be returned as it's string representation, as opposed to bigint from regular query
* To handle that, we need a separate function to handle such field's mapping:
* ```
* fromJson(value: string): bigint {
* return BigInt(value);
* },
* ```
*
* It'll cause the returned data to change from:
* ```
* {
* customField: "5044565289845416380";
* }
* ```
* to:
* ```
* {
* customField: 5044565289845416380n;
* }
* ```
*/
fromJson?: (value: T['jsonData']) => T['data'];
/**
* Optional selection modifier function, that is used for modifying selection of column inside [JSON functions](https://orm.drizzle.team/docs/json-functions)
*
* Additional mapping that could be required for such scenarios can be handled using {@link fromJson} function
*
* Used by [relational queries](https://orm.drizzle.team/docs/rqb)
*
* Following types are being casted to text by default: `bytea`, `geometry`, `timestamp`, `numeric`, `bigint`
* @example
* For example, when using bigint we need to cast field to text to preserve data integrity
* ```
* forJsonSelect(identifier: SQL, sql: SQLGenerator, arrayDimensions?: number): SQL {
* return sql`${identifier}::text`
* },
* ```
*
* This will change query from:
* ```
* SELECT
* row_to_json("t".*)
* FROM
* (
* SELECT
* "table"."custom_bigint" AS "bigint"
* FROM
* "table"
* ) AS "t"
* ```
* to:
* ```
* SELECT
* row_to_json("t".*)
* FROM
* (
* SELECT
* "table"."custom_bigint"::text AS "bigint"
* FROM
* "table"
* ) AS "t"
* ```
*
* Returned by query object will change from:
* ```
* {
* bigint: 5044565289845416000; // Partial data loss due to direct conversion to JSON format
* }
* ```
* to:
* ```
* {
* bigint: "5044565289845416380"; // Data is preserved due to conversion of field to text before JSON-ification
* }
* ```
*/
forJsonSelect?: (identifier: SQL, sql: SQLGenerator, arrayDimensions?: number) => SQL;}
---
## File: src/content/docs/cockroach/data-querying.mdx
import Callout from '@mdx/Callout.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from '@mdx/Section.astro';
import Flex from "@mdx/Flex.astro"
import LinksList from "@mdx/LinksList.astro"
import Prerequisites from "@mdx/Prerequisites.astro";
# Drizzle Queries + CRUD
- How to define your schema - [Schema Fundamentals](/docs/cockroach/sql-schema-declaration)
- How to connect to the database - [Connection Fundamentals](/docs/cockroach/connect-overview)
Drizzle gives you a few ways for querying your database and it's up to you to decide which one you'll need in your next project.
It can be either SQL-like syntax or Relational Syntax. Let's check them:
## Why SQL-like?
\
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate from or abstract away SQL, leading to a double learning curve: you need to learn both SQL and the framework's API.
Drizzle is the opposite.
We embrace SQL and built Drizzle to be SQL-like at its core, so you have little to no learning curve and full access to the power of SQL.
<Section>
```typescript copy
// Access your data
await db
.select()
.from(posts)
.leftJoin(comments, eq(posts.id, comments.post_id))
.where(eq(posts.id, 10))SELECT *
FROM "posts"
LEFT JOIN "comments" ON "posts"."id" = "comments"."post_id"
WHERE "posts"."id" = 10With SQL-like syntax, you can replicate much of what you can do with pure SQL and know
exactly what Drizzle will do and what query will be generated. You can perform a wide range of queries,
including select, insert, update, delete, as well as using aliases, WITH clauses, subqueries, prepared statements,
and more. Let's look at more examples
<CodeTabs items={['insert', 'update', 'delete']}>
await db.insert(users).values({ email: '[email protected]' })INSERT INTO "users" ("email") VALUES ('[email protected]')Why not SQL-like?
We're always striving for a perfectly balanced solution. While SQL-like queries cover 100% of your needs,
there are certain common scenarios where data can be queried more efficiently.
We've built the Queries API so you can fetch relational, nested data from the database in the most convenient
and performant way, without worrying about joins or data mapping.
Drizzle always outputs exactly one SQL query. Feel free to use it with serverless databases,
and never worry about performance or roundtrip costs!
Advanced
With Drizzle, queries can be composed and partitioned in any way you want. You can compose filters
independently from the main query, separate subqueries or conditional statements, and much more.
Let's check a few advanced examples:
Compose a WHERE statement and then use it in a query
async function getProductsBy({
name,
category,
maxPrice,
}: {
name?: string;
category?: string;
maxPrice?: number;
}) {
const filters: SQL[] = [];
if (name) filters.push(ilike(products.name, name));
if (category) filters.push(eq(products.category, category));
if (maxPrice) filters.push(lte(products.price, maxPrice));
return db
.select()
.from(products)
.where(and(...filters));
}Separate subqueries into different variables, and then use them in the main query
const subquery = db
.select()
.from(internalStaff)
.leftJoin(customUser, eq(internalStaff.userId, customUser.id))
.as('internal_staff');
const mainQuery = await db
.select()
.from(ticket)
.leftJoin(subquery, eq(subquery.internal_staff.userId, ticket.staffId));What's next?
File: src/content/docs/cockroach/delete.mdx
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
SQL Delete
You can delete all rows in the table:
await db.delete(users);And you can delete with filters and conditions:
await db.delete(users).where(eq(users.name, 'Dan'));Returning
You can delete a row and get it back in PostgreSQL:
const deletedUser = await db.delete(users)
.where(eq(users.name, 'Dan'))
.returning();
// partial return
const deletedUserId = await db.delete(users)
.where(eq(users.name, "Dan"))
.returning({ deletedId: users.id });
// deletedUserId: { deletedId: number | null }[]delete from "users" where "users"."name" = 'Dan' returning "id", "name";
delete from "users" where "users"."name" = 'Dan' returning "id";WITH DELETE clause
Using the with clause can help you simplify complex queries by splitting them into smaller subqueries called common table expressions (CTEs):
const averageAmount = db.$with('average_amount').as(
db.select({ value: sql`avg(${orders.amount})`.as('value') }).from(orders)
);
const result = await db
.with(averageAmount)
.delete(orders)
.where(gt(orders.amount, sql`(select * from ${averageAmount})`))
.returning({
id: orders.id
});with "average_amount" as (select avg("amount") as "value" from "orders")
delete from "orders"
where "orders"."amount" > (select * from "average_amount")
returning "id"File: src/content/docs/cockroach/drizzle-config-file.mdx
import CodeTab from "@mdx/CodeTab.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
import Callout from "@mdx/Callout.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Prerequisites from "@mdx/Prerequisites.astro"
import Dialects from "@mdx/Dialects.mdx"
import Drivers from "@mdx/Drivers.mdx"
import DriversExamples from "@mdx/DriversExamples.mdx"
import Npx from "@mdx/Npx.astro"
Drizzle Kit configuration file
- Get started with Drizzle and
drizzle-kit- read here - Drizzle schema fundamentals - read here
- Database connection basics - read here
- Drizzle migrations fundamentals - read here
- Drizzle Kit overview and config file
Drizzle Kit lets you declare configuration options in TypeScript or JavaScript configuration files.
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
out: "./drizzle",
});
</CodeTab>
<CodeTab>
```js
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
out: "./drizzle",
});Example of an extended config file
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "./database/",
},
schemaFilter: "public",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "drizzle",
},
entities: {
roles: {
provider: '',
exclude: [],
include: []
}
},
breakpoints: true,
verbose: true,
});Multiple configuration files
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases or different databases on the same project:
drizzle-kit generate --config=drizzle-dev.config.ts
drizzle-kit generate --config=drizzle-prod.config.ts
๐ฆ
โ ๐ drizzle
โ ๐ src
โ ๐ .env
โ ๐ drizzle-dev.config.ts
โ ๐ drizzle-prod.config.ts
โ ๐ package.json
โ ๐ tsconfig.jsonMigrations folder
out param lets you define folder for your migrations, it's optional and drizzle by default.
It's very useful since you can have many separate schemas for different databases in the same project
and have different migration folders for them.
Migration folder contains folders with .sql migration files which is used by drizzle-kit
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema/*",
out: "./drizzle",
});
</Section>
## ---
### `dialect`
<rem025/>
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | <Dialects/> |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export` |
<rem025/>
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
});`schema`
glob
based path to drizzle schema file(s) or folder(s) contaning schema files.
| type | string string[] |
| default | -- |
| commands | generate, push, export, studio |
`out`
Defines output folder of your SQL migration files, json snapshots of your schema and schema.ts from drizzle-kit pull command.
| type | string string[] |
| default | drizzle |
| commands | generate, pull, migrate, check, up |
export default defineConfig({
out: "./drizzle",
});
## ---
### `dbCredentials`
<rem025/>
Database connection credentials in a form of `url`,
`user:password@host:port/db` params or exceptions drivers(<Drivers/>) specific connection options.
| | |
| :------------ | :----------------- |
| type | <Dialects/> |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
<rem025/>
<CodeTabs items={["Connection string", "Connection params"]}>
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "cockroach",
dbCredentials: {
url: "postgres://user:password@host:port/db",
}
});import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "cockroach",
dbCredentials: {
host: "host",
port: 26257,
user: "user",
password: "password",
database: "dbname",
ssl: true, // can be boolean | "require" | "allow" | "prefer" | "verify-full" | options from node:tls
}
});`migrations`
When running drizzle-kit migrate - drizzle will records about
successfully applied migrations in your database in log table named __drizzle_migrations in drizzle schema.
migrations config options lets you change both migrations log table name and schema.
| type | { table: string, schema: string } |
| default | { table: "__drizzle_migrations", schema: "drizzle" } |
| commands | migrate, push, pull |
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
schema: 'my_schema', // `drizzle` by default
},
});`introspect`
Configuration for drizzle-kit pull command.
casing is responsible for in-code column keys casing
| type | { casing: "preserve" | "camel" } |
| default | { casing: "camel" } |
| commands | pull |
<CodeTabs items={["camel", "preserve"]}>
import * as p from "drizzle-orm/cockroach-core"
export const users = p.cockroachTable("users", {
id: p.int4(),
firstName: p.string("first-name"),
lastName: p.string("LastName"),
email: p.string(),
phoneNumber: p.string("phone_number"),
});SELECT column_name as column_name, lower(crdb_sql_type) as data_type FROM information_schema.columns WHERE table_name = 'users';column_name | data_type
---------------+------------------------
id | int4
first-name | string
LastName | string
email | string
phone_number | stringexport const users = p.cockroachTable("users", {
id: p.int4(),
"first-name": p.string("first-name"),
LastName: p.string("LastName"),
email: p.string(),
phone_number: p.string("phone_number"),
});
```sql
SELECT column_name as column_name, lower(crdb_sql_type) as data_type FROM information_schema.columns WHERE table_name = 'users';column_name | data_type
---------------+------------------------
id | int4
first-name | string
LastName | string
email | string
phone_number | string---
`tablesFilter`
tablesFilter option lets you specify glob
based table names filter, e.g. ["users", "user_info"] or "user*"
| type | string string[] |
| default | -- |
| commands | push pull |
export default defineConfig({
dialect: "cockroach",
tablesFilter: ["users", "posts", "project1_*"],
});
### `schemaFilter`
<Callout>
If you want to run multiple projects with one database - check out [our guide](/docs/cockroach/goodies#multi-project-schema).
</Callout>
`drizzle-kit push` and `drizzle-kit pull` will by default manage all schemas.
`schemaFilter` option lets you specify [`glob`](https://www.digitalocean.com/community/tools/glob?comments=true&glob=/**/*.js&matches=false&tests=//%20This%20will%20match%20as%20it%20ends%20with%20'.js'&tests=/hello/world.js&tests=//%20This%20won't%20match!&tests=/test/some/globs)
based schema names filter, e.g. `["public", "auth"]` or `"tenant_*"`
| | |
| :------------ | :----------------- |
| type | `string[]` |
| commands | `push` `pull` |
<rem025/>
```ts
export default defineConfig({
dialect: "cockroach",
schemaFilter: ["public", "schema1", "schema2"],
});---
`entities`
This configuration is created to set up management settings for specific entities in the database.
For now, it only includes roles, but eventually all database entities will migrate here, such as tables, schemas, extensions, functions, triggers, etc
`roles`
If you are using Drizzle Kit to manage your schema and especially the defined roles, there may be situations where you have some roles that are not defined in the Drizzle schema.
In such cases, you may want Drizzle Kit to skip those roles without the need to write each role in your Drizzle schema and mark it with .existing().
The roles option lets you:
- Enable or disable role management with Drizzle Kit.
- Exclude specific roles from management by Drizzle Kit.
- Include specific roles for management by Drizzle Kit.
- Enable modes for providers like
NeonandSupabase, which do not manage their specific roles. - Combine all the options above
| type | boolean | { provider: "neon" | "supabase", include: string[], exclude: string[] } |
| default | false |
| commands | push pull |
By default, drizzle-kit won't manage roles for you, so you will need to enable that. in drizzle.config.ts
export default defineConfig({
dialect: "cockroach",
entities: {
roles: true
}
});You have a role admin and want to exclude it from the list of manageable roles
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
exclude: ['admin']
}
}
});You have a role admin and want to include to the list of manageable roles
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
include: ['admin']
}
}
});If you are using Neon and want to exclude roles defined by Neon, you can use the provider option
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'neon'
}
}
});If you are using Supabase and want to exclude roles defined by Supabase, you can use the provider option
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'supabase'
}
}
});// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'supabase',
exclude: ['new_supabase_role']
}
}
});---
`verbose`
Print all SQL statements during drizzle-kit push command.
| type | boolean |
| default | false |
| commands | pull |
export default defineConfig({
dialect: "cockroach",
verbose: false,
});`breakpoints`
Drizzle Kit will automatically embed --> statement-breakpoint into generated SQL migration files,
that's necessary for databases that do not support multiple DDL alternation statements in one transaction(MySQL and SQLite).
breakpoints option flag lets you switch it on and off
| type | boolean |
| default | true |
| commands | generate pull |
export default defineConfig({
dialect: "cockroach",
breakpoints: false,
});