drizzle-orm Documentation

TypeScript bunjs & mysql repository by @drizzle-team. Model Context Protocol (MCP) server & tools with 35k stars.

35,456 stars TypeScript TypeScript 1 file ยท ~24,401 tokens #bunjs#mysql#nodejs#orm#postgres#postgresql#sql#sqlite
RAW Doc

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 check custom-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**

typescript
const customSerial = customType<{ data: number; notNull: true; default: true }>(
  {
    dataType() {
      return 'serial';
    },
  },
);

**Text**

typescript
const customText = customType<{ data: string }>({
  dataType() {
    return 'text';
  },
});

**Boolean**

typescript
const customBoolean = customType<{ data: boolean }>({
  dataType() {
    return 'boolean';
  },
});

**Jsonb**

typescript
const customJsonb = (name: string) =>
  customType<{ data: TData; driverData: string }>({
    dataType() {
      return 'jsonb';
    },
    toDriver(value: TData): string {
      return JSON.stringify(value);
    },
  })(name);

**Timestamp**

typescript
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

typescript
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**

typescript
const customSerial = customType<{ data: number; notNull: true; default: true }>(
  {
    dataType() {
      return 'serial';
    },
  },
);

**Text**

typescript
const customText = customType<{ data: string }>({
  dataType() {
    return 'text';
  },
});

**Boolean**

typescript
const customBoolean = customType<{ data: boolean }>({
  dataType() {
    return 'boolean';
  },
  fromDriver(value) {
    if (typeof value === 'boolean') {
      return value;
    }
    return value === 1;
  },
});

**Json**

typescript
const customJson = (name: string) =>
  customType<{ data: TData; driverData: string }>({
    dataType() {
      return 'json';
    },
    toDriver(value: TData): string {
      return JSON.stringify(value);
    },
  })(name);

**Timestamp**

typescript
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

typescript
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

typescript
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 creation

  • Column - class, that is representing Columns itself, that is used in query generation, migration mapping, etc.

    Each module has it's own class, representing ColumnBuilder or Column:

  • For pg -> PgColumnBuilder and PgColumn

  • For mysql -> MySqlColumnBuilder and MySqlColumn

  • For sqlite -> SQLiteColumnBuilder and SQLiteColumn

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

typescript
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]
$pgColumnBuilderBrand should 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 definition

  • getSQLType() - function, that shows datatype name in database and will be used in migration generation

  • mapFromDriverValue() - 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

typescript
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

typescript
override mapFromDriverValue(value: number | string): number {
  if (typeof value === 'string') {
    return parseInt(value);
  }
  return value;
}

Column class example

typescript
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]
$pgColumnBrand should 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

typescript
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

typescript
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

typescript
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.

typescript
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:

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.id

And here's how to do the same with Drizzle ORM:

typescript
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:

typescript
{
  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():

typescript
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:

typescript
{
  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:

typescript
.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:

typescript
{
  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:

typescript
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.

typescript
{
  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:

typescript
.select({
  cities, // shorthand for "cities: cities", the key can be anything
  user: {
    firstName: users.firstName,
  },
})
typescript
{
  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!):

typescript
.select({
  id: cities.id,
  cityAndUser: {
    cityName: cities.name,
    userId: users.id,
    firstName: users.firstName,
    lastName: users.lastName,
  }
})
typescript
{
  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():

typescript
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

ts
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

ts
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

ts
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:

typescript
// 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:

sql
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

shell
npm install -D drizzle-kit

Running with CLI options:

jsonc
// package.json
{
 "scripts": {
  "generate": "drizzle-kit generate --out migrations-folder --schema src/db/schema.ts"
 }
}
shell
npm run generate

File: 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.

ts
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.

ts
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:

text
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ content/
โ”‚   โ”‚   โ””โ”€โ”€ docs

Announcements markdown files:

text
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€data/
โ”‚   โ”‚   โ””โ”€โ”€ announcements

Roadmap markdown file:

text
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€data/
โ”‚   โ”‚   โ””โ”€โ”€ roadmap.md

Shipping section yaml file:

text
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€data/
โ”‚   โ”‚   โ””โ”€โ”€ shipping.yaml
text
progress: 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:

```ts copy {9} import { alias, cockroachTable, int4, string } from "drizzle-orm/cockroach-core";

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));

text
```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:

```ts {3} const result = await db.select({ id: users.id, lowerName: users.name.as("lower_name"), }).from(users); ``` ```sql select "id", "name" as "lower_name" from "users"; ```

You can also use .as() with sql expressions:

```ts {3} const result = await db.select({ id: users.id, lowerName: sql`lower(${users.name})`.as("lower_name"), }).from(users); ``` ```sql select "id", lower("name") as "lower_name" from "users"; ```

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:

```ts const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');

const result = await db.select().from(sq);

text
```sql
select "id", "name", "age" from (select "id", "name", "age" from "users" where "users"."id" = 42) "sq";

Subqueries can also be used in joins:

```ts const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');

const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id));

text
```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:

```ts const sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));

const result = await db.with(sq).select().from(sq);

text
```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:

```ts const sq = db.$with('sq').as(db.select({ name: sql`upper(${users.name})`.as('name'), }).from(users));

const result = await db.with(sq).select({ name: sq.name }).from(sq);

text
```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

arktype

Select schema

Defines the shape of data queried from the database - can be used to validate API responses.

ts copy
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 successfully

Views and enums are also supported.

ts copy
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.

ts copy
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.

ts copy
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.

ts copy
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.

ts
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:

ts
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:

ts
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)

ts
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

ts
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()

ts
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

ts
// 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 })
**Eventual consistency example**

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:

ts
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

ts
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

ts
const res = await db.select().from(users);

If you want to disable cache for this specific query, call .$withCache(false)

ts
// 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

ts
// 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

ts
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';
};
ts
const db = drizzle(process.env.DB_URL!, { cache: new TestGlobalCache() });
text
/* 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:
ts
db.execute(sql`select 1`);
text
- 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
ts
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';

drizzle-orm@rc drizzle-kit@rc -D

We have native support for all of them, yet if that's not enough for you, feel free to create custom types.

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

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.

typescript
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' })
sql
CREATE TABLE "table" (
	"int8" int8,
	"bigint" int8
);
```typescript import { sql } from "drizzle-orm"; import { bigint, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
bigint1: bigint().default(10),
bigint2: int8().default(10),
});

text
```sql
CREATE TABLE "table" (
	"bigint1" bigint DEFAULT 10,
	"bigint2" int8 DEFAULT 10
);

smallint

smallint int2
Small-range signed 2-byte integer

```typescript import { smallint, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
int2: int2(),
smallint: smallint()
});

text
```sql
CREATE TABLE "table" (
	"int2" int2,
	"smallint" smallint
);
```typescript import { sql } from "drizzle-orm"; import { int2, smallint, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
smallint1: int2().default(10),
smallint2: smallint().default(10)
});

text
```sql
CREATE TABLE "table" (
	"smallint1" int2 DEFAULT 10,
	"smallint2" int2 DEFAULT 10
);

int4

Signed 4-byte integer

```typescript import { int4, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
int4: int4()
});

text
```sql
CREATE TABLE "table" (
	"int4" int4
);
```typescript import { sql } from "drizzle-orm"; import { int4, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
int1: int4().default(10)
});

text
```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.

```typescript import { bool, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
boolean: bool()
});

text
```sql
CREATE TABLE "table" (
	"boolean" bool
);

---

string

text varchar, char

The STRING data type stores a string of Unicode characters.

For PostgreSQL compatibility, CockroachDB supports the following STRING-related types and their aliases:

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.

typescript
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"] })
sql
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.

typescript
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"] })
sql
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.

typescript
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"] }),
sql
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.

typescript
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"] }),
sql
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.

```typescript import { decimal, cockroachTable } from "drizzle-orm/cockroach-core";

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' }),
});

text
```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.

```typescript import { sql } from "drizzle-orm"; import { float, cockroachTable } from "drizzle-orm/cockroach-core";

const table = cockroachTable('table', {
float1: float(),
float2: float().default(10.10),
float3: float().default(sql'10.10'::float),
});

text
```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.

```typescript import { sql } from "drizzle-orm"; import { real, cockroachTable } from "drizzle-orm/cockroach-core";

const table = cockroachTable('table', {
real1: real(),
real2: real().default(10.10),
real3: real().default(sql'10.10'::real),
});

text
```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.

typescript
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`),
});
sql
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.

typescript
// 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.

typescript
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'`)
});
sql
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.

typescript
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'`)
});
sql
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.

ts
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')
});
sql
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.

```typescript import { time, cockroachTable } from "drizzle-orm/cockroach-core";

const table = cockroachTable('table', {
time1: time(),
time2: time({ withTimezone: true }),
time3: time({ precision: 6 }),
time4: time({ precision: 6, withTimezone: true })
});

text
```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.

typescript
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()`),
});
sql
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:

typescript
// will infer as date
timestamp: timestamp({ mode: "date" }),

// will infer as string
timestamp: timestamp({ mode: "string" }),

The string mode 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 strings to and from the database, so the behavior should be as predictable as possible
and aligned 100% with the database behavior

The date mode 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.

typescript
import { date, cockroachTable } from "drizzle-orm/cockroach-core";

const table = cockroachTable('table', {
	date: date(),
});
sql
CREATE TABLE "table" (
	"date" date
);
You can specify either `date` or `string` infer modes: ```typescript // will infer as date date: date({ mode: "date" }),

// will infer as string
date: date({ mode: "string" }),

text
### 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 }),
});
sql
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.

typescript
import { cockroachEnum, cockroachTable } from "drizzle-orm/cockroach-core";

export const moodEnum = cockroachEnum('mood', ['sad', 'ok', 'happy']);

export const table = cockroachTable('table', {
  mood: moodEnum(),
});
sql
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.

```typescript import { geometry, cockroachTable } from "drizzle-orm/cockroach-core";

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' })

text
```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.

```typescript import { inet, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
inet: inet(),
});

text
```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.

```typescript import { vector, cockroachTable } from "drizzle-orm/cockroach-core";

export const table = cockroachTable('table', {
embedding: vector({ dimensions: 3 }),
});

text
```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:

ts
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

To use this feature you would need to have `[email protected]` or higher and `[email protected]` or higher

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

ts
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.

```typescript import { sql } from "drizzle-orm"; import { int4, cockroachTable, uuid } from "drizzle-orm/cockroach-core"; import { sql } from 'drizzle-orm';

export const table = cockroachTable('table', {
int: int4().default(42),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sqlgen_random_uuid()),
});

text
```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.

Note: This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`
ts
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.

Note: This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`
ts
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.

```typescript import { int4, cockroachTable } from "drizzle-orm/cockroach-core";

const table = cockroachTable('table', {
int4: int4().notNull(),
});

text
```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.

typescript
import { int4, cockroachTable } from 'drizzle-orm/cockroach-core';

export const table = cockroachTable('table', {
  id: int4().primaryKey(),
});
sql
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

ts
import { customType, cockroachTable } from 'drizzle-orm/cockroach-core';

const customInteger = customType<{ data: number; }>(
  {
    dataType() {
      return 'int4';
    },
  },
);

export const users = cockroachTable("users", {
  id: customInteger(),
});

Text

ts
import { customType, cockroachTable } from 'drizzle-orm/cockroach-core';

const customText = customType<{ data: string }>({
  dataType() {
    return 'text';
  },
});

export const users = cockroachTable('users', {
  name: customText(),
});

Timestamp

ts
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:

ts
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

text
/* Detailed source-code truncated for AI context efficiency. */
text
* `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;

}

text
---

## 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))
sql
SELECT *
FROM "posts"
LEFT JOIN "comments" ON "posts"."id" = "comments"."post_id"
WHERE "posts"."id" = 10

With 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']}>

ts
await db.insert(users).values({ email: '[email protected]' })
sql
INSERT INTO "users" ("email") VALUES ('[email protected]')
```ts await db.update(users) .set({ email: '[email protected]' }) .where(eq(users.id, 1)) ``` ```sql UPDATE "users" SET "email" = '[email protected]' WHERE "users"."id" = 1 ```
```ts await db.delete(users).where(eq(users.id, 1)) ``` ```sql DELETE FROM "users" WHERE "users"."id" = 1 ```

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!

```ts const result = await db._query.users.findMany({ with: { posts: true }, }); ``` {/* ```sql SELECT * FROM users ... ``` */}

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

ts
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

ts
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:

typescript copy
await db.delete(users);

And you can delete with filters and conditions:

typescript copy
await db.delete(users).where(eq(users.name, 'Dan'));

Returning

You can delete a row and get it back in PostgreSQL:

typescript copy
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 }[]
sql
delete from "users" where "users"."name" = 'Dan' returning "id", "name";

delete from "users" where "users"."name" = 'Dan' returning "id";

WITH DELETE clause

Check how to use WITH statement with [select](/docs/cockroach/select#with-clause), [insert](/docs/cockroach/insert#with-insert-clause), [update](/docs/cockroach/update#with-update-clause)

Using the with clause can help you simplify complex queries by splitting them into smaller subqueries called common table expressions (CTEs):

typescript copy
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
	});
sql
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

Drizzle Kit lets you declare configuration options in TypeScript or JavaScript configuration files.

```plaintext {5} ๐Ÿ“ฆ โ”œ ... โ”œ ๐Ÿ“‚ drizzle โ”œ ๐Ÿ“‚ src โ”œ ๐Ÿ“œ drizzle.config.ts โ”” ๐Ÿ“œ package.json ``` ```ts import { defineConfig } from "drizzle-kit";

export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
out: "./drizzle",
});

text
</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

ts collapsable
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

plaintext {5-6}
๐Ÿ“ฆ 
 โ”œ ๐Ÿ“‚ drizzle
 โ”œ ๐Ÿ“‚ src
 โ”œ ๐Ÿ“œ .env
 โ”œ ๐Ÿ“œ drizzle-dev.config.ts
 โ”œ ๐Ÿ“œ drizzle-prod.config.ts
 โ”œ ๐Ÿ“œ package.json
 โ”” ๐Ÿ“œ tsconfig.json

Migrations 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

```plaintext {3} ๐Ÿ“ฆ โ”œ ... โ”œ ๐Ÿ“‚ drizzle โ”‚ โ”œ ๐Ÿ“‚ 20242409125510_premium_mister_fear โ”‚ โ”œ ๐Ÿ“œ user.ts โ”‚ โ”œ ๐Ÿ“œ post.ts โ”‚ โ”” ๐Ÿ“œ comment.ts โ”œ ๐Ÿ“‚ src โ”œ ๐Ÿ“œ drizzle.config.ts โ”” ๐Ÿ“œ package.json ``` ```ts {6} import { defineConfig } from "drizzle-kit";

export default defineConfig({
dialect: "cockroach",
schema: "./src/schema/*",
out: "./drizzle",
});

text
</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
```ts {4} import { defineConfig } from "drizzle-kit";

export default defineConfig({
out: "./drizzle",
});

text
## ---

### `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",
  }
});
ts
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
ts
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"]}>

ts
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"),
});
sql
SELECT column_name as column_name, lower(crdb_sql_type) as data_type FROM information_schema.columns WHERE table_name = 'users';
text
column_name   | data_type        
---------------+------------------------
 id            | int4
 first-name    | string
 LastName      | string
 email         | string
 phone_number  | string
```ts import * as p from "drizzle-orm/cockroach-core"

export 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"),
});

text
```sql
SELECT column_name as column_name, lower(crdb_sql_type) as data_type FROM information_schema.columns WHERE table_name = 'users';
text
column_name   | data_type        
---------------+------------------------
 id            | int4
 first-name    | string
 LastName      | string
 email         | string
 phone_number  | string

---

`tablesFilter`

If you want to run multiple projects with one database - check out [our guide](/docs/cockroach/goodies#multi-project-schema). `drizzle-kit push` and `drizzle-kit pull` will manage all tables by default. You can configure list of tables, schemas and extensions via `tablesFilters`, `schemaFilter` and `extensionFilters` options.

tablesFilter option lets you specify glob
based table names filter, e.g. ["users", "user_info"] or "user*"

type string string[]
default --
commands push pull
```ts import { defineConfig } from "drizzle-kit";

export default defineConfig({
dialect: "cockroach",
tablesFilter: ["users", "posts", "project1_*"],
});

text
### `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 Neon and Supabase, 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

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

ts
// 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

ts
// 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

ts
// 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

ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  ...
  entities: {
    roles: {
      provider: 'supabase'
    }
  }
});
You may encounter situations where Drizzle is slightly outdated compared to new roles specified by database providers, so you may need to use both the `provider` option and `exclude` additional roles. You can easily do this with Drizzle:
ts
// 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
ts
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
ts
export default defineConfig({
  dialect: "cockroach",
  breakpoints: false,
});