# 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. It supports every PostgreSQL, MySQL, SQLite and SingleStore database and is serverless-ready by design.
Source: https://orm.drizzle.team/docs/arktype
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
Starting from `drizzle-orm@1.0.0-beta.15`, `drizzle-arktype` has been deprecated in favor of first-class schema generation support within Drizzle ORM itself
You can still use `drizzle-arktype` package but all new update will be added to Drizzle ORM directly
# 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 { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { 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: { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Will parse successfully
```
Views and enums are also supported.
```ts copy
import { pgEnum } from 'drizzle-orm/pg-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
const roles = pgEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: 'admin' | 'basic' = rolesSchema(...);
const usersView = pgView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { 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 { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createInsertSchema } from 'drizzle-orm/arktype';
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = userInsertSchema(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = userInsertSchema(user); // Will parse successfully
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 { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { parse } from 'arktype';
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { id: 5, name: 'John' };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema(user); // Error: `id` is a generated column, it can't be updated
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema(user); // Will parse successfully
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 { pgTable, text, integer, json } from 'drizzle-orm/pg-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { parse, pipe, maxLength, object, string } from 'arktype';
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
bio: text(),
preferences: json()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => pipe(schema, maxLength(20)), // Extends schema
bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio?: string | undefined;
preferences: {
theme: string;
};
} = userSelectSchema(...);
```
### Data type reference
```ts
pg.boolean();
mysql.boolean();
sqlite.integer({ mode: 'boolean' });
// Schema
type.boolean;
```
```ts
pg.date({ mode: 'date' });
pg.timestamp({ mode: 'date' });
mysql.date({ mode: 'date' });
mysql.datetime({ mode: 'date' });
mysql.timestamp({ mode: 'date' });
sqlite.integer({ mode: 'timestamp' });
sqlite.integer({ mode: 'timestamp_ms' });
// Schema
type.Date;
```
```ts
pg.date({ mode: 'string' });
pg.timestamp({ mode: 'string' });
pg.cidr();
pg.inet();
pg.interval();
pg.macaddr();
pg.macaddr8();
pg.numeric();
pg.text();
pg.sparsevec();
pg.time();
mysql.binary();
mysql.date({ mode: 'string' });
mysql.datetime({ mode: 'string' });
mysql.decimal();
mysql.time();
mysql.timestamp({ mode: 'string' });
mysql.varbinary();
sqlite.numeric();
sqlite.text({ mode: 'text' });
// Schema
type.string;
```
```ts
pg.bit({ dimensions: ... });
// Schema
type(`/^[01]{${column.dimensions}}$/`);
```
```ts
pg.uuid();
// Schema
type(/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/iu);
```
```ts
pg.char({ length: ... });
mysql.char({ length: ... });
// Schema
type.string.exactlyLength(length);
```
```ts
pg.varchar({ length: ... });
mysql.varchar({ length: ... });
sqlite.text({ mode: 'text', length: ... });
// Schema
type.string.atMostLength(length);
```
```ts
mysql.tinytext();
// Schema
type.string.atMostLength(255); // unsigned 8-bit integer limit
```
```ts
mysql.text();
// Schema
type.string.atMostLength(65_535); // unsigned 16-bit integer limit
```
```ts
mysql.mediumtext();
// Schema
type.string.atMostLength(16_777_215); // unsigned 24-bit integer limit
```
```ts
mysql.longtext();
// Schema
type.string.atMostLength(4_294_967_295); // unsigned 32-bit integer limit
```
```ts
pg.text({ enum: ... });
pg.char({ enum: ... });
pg.varchar({ enum: ... });
mysql.tinytext({ enum: ... });
mysql.mediumtext({ enum: ... });
mysql.text({ enum: ... });
mysql.longtext({ enum: ... });
mysql.char({ enum: ... });
mysql.varchar({ enum: ... });
mysql.mysqlEnum(..., ...);
sqlite.text({ mode: 'text', enum: ... });
// Schema
type.enumerated(...enum);
```
```ts
mysql.tinyint();
// Schema
type.keywords.number.integer.atLeast(-128).atMost(127); // 8-bit integer lower and upper limit
```
```ts
mysql.tinyint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(255); // unsigned 8-bit integer lower and upper limit
```
```ts
pg.smallint();
pg.smallserial();
mysql.smallint();
// Schema
type.keywords.number.integer.atLeast(-32_768).atMost(32_767); // 16-bit integer lower and upper limit
```
```ts
mysql.smallint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(65_535); // unsigned 16-bit integer lower and upper limit
```
```ts
pg.real();
mysql.float();
// Schema
type.number.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.mediumint();
// Schema
type.keywords.number.integer.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.float({ unsigned: true });
// Schema
type.number.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.mediumint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
```
```ts
pg.integer();
pg.serial();
mysql.int();
// Schema
type.keywords.number.integer.atLeast(-2_147_483_648).atMost(2_147_483_647); // 32-bit integer lower and upper limit
```
```ts
mysql.int({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(4_294_967_295); // unsgined 32-bit integer lower and upper limit
```
```ts
pg.doublePrecision();
mysql.double();
mysql.real();
sqlite.real();
// Schema
type.number.atLeast(-140_737_488_355_328).atMost(140_737_488_355_327); // 48-bit integer lower and upper limit
```
```ts
mysql.double({ unsigned: true });
// Schema
type.number.atLeast(0).atMost(281_474_976_710_655); // unsigned 48-bit integer lower and upper limit
```
```ts
pg.bigint({ mode: 'number' });
pg.bigserial({ mode: 'number' });
mysql.bigint({ mode: 'number' });
mysql.bigserial({ mode: 'number' });
sqlite.integer({ mode: 'number' });
// Schema
type.keywords.number.integer.atLeast(-9_007_199_254_740_991).atMost(9_007_199_254_740_991); // Javascript min. and max. safe integers
```
```ts
mysql.serial();
// Schema
type.keywords.number.integer.atLeast(0).atMost(9_007_199_254_740_991); // Javascript max. safe integer
```
```ts
pg.bigint({ mode: 'bigint' });
pg.bigserial({ mode: 'bigint' });
mysql.bigint({ mode: 'bigint' });
sqlite.blob({ mode: 'bigint' });
// Schema
type.bigint.narrow(
(value, ctx) => value < -9_223_372_036_854_775_808n ? ctx.mustBe('greater than') : value > 9_223_372_036_854_775_807n ? ctx.mustBe('less than') : true
); // 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'bigint', unsigned: true });
// Schema
type.bigint.narrow(
(value, ctx) => value < 0n ? ctx.mustBe('greater than') : value > 18_446_744_073_709_551_615n ? ctx.mustBe('less than') : true
); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.year();
// Schema
type.keywords.number.integer.atLeast(1_901).atMost(2_155);
```
```ts
pg.geometry({ type: 'point', mode: 'tuple' });
pg.point({ mode: 'tuple' });
// Schema
type([type.number, type.number]);
```
```ts
pg.geometry({ type: 'point', mode: 'xy' });
pg.point({ mode: 'xy' });
// Schema
type({ x: type.number, y: type.number });
```
```ts
pg.halfvec({ dimensions: ... });
pg.vector({ dimensions: ... });
// Schema
type.number.array().exactlyLength(dimensions);
```
```ts
pg.line({ mode: 'abc' });
// Schema
type({ a: type.number, b: type.number, c: type.number });
```
```ts
pg.line({ mode: 'tuple' });
// Schema
type([type.number, type.number, type.number]);
```
```ts
pg.json();
pg.jsonb();
mysql.json();
sqlite.blob({ mode: 'json' });
sqlite.text({ mode: 'json' });
// Schema
type('string | number | boolean | null').or(type('unknown.any[] | Record'));
```
```ts
sqlite.blob({ mode: 'buffer' });
// Schema
type.instanceOf(Buffer);
```
```ts
pg.dataType().array(...);
// Schema
baseDataTypeSchema.array().exactlyLength(size);
```
Source: https://orm.drizzle.team/docs/cockroach/aliases
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));
```
```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);
```
```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));
```
```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);
```
```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);
```
```sql
with "sq" as (select upper("name") as "name" from "users")
select "name" from "sq";
```
Source: https://orm.drizzle.team/docs/cockroach/arktype
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](/docs/cockroach/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/cockroach/cache
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: "cacheman@upstash.com" });
```
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() });
```
```ts
import Keyv from "keyv";
export class TestGlobalCache extends Cache {
private globalTtl: number = 1000;
// This object will be used to store which query keys were used
// for a specific table, so we can later use it for invalidation.
private usedTablesPerKey: Record = {};
constructor(private kv: Keyv = new Keyv()) {
super();
}
// For the strategy, we have two options:
// - 'explicit': The cache is used only when .$withCache() is added to a query.
// - 'all': All queries are cached globally.
// The default behavior is 'explicit'.
override strategy(): "explicit" | "all" {
return "all";
}
// This function accepts query and parameters that cached into key param,
// allowing you to retrieve response values for this query from the cache.
override async get(key: string): Promise {
const res = (await this.kv.get(key)) ?? undefined;
return res;
}
// This function accepts several options to define how cached data will be stored:
// - 'key': A hashed query and parameters.
// - 'response': An array of values returned by Drizzle from the database.
// - 'tables': An array of tables involved in the select queries. This information is needed for cache invalidation.
// - 'isTag': A boolean flag indicating whether this cache entry represents a tag-based query.
// When true, the entry is associated with a specific tag rather than table-level invalidation,
// allowing you to invalidate cached queries by tag name. See the UpstashCache:
// https://github.com/drizzle-team/drizzle-orm/blob/beta/drizzle-orm/src/cache/upstash/cache.ts for an example
//
// For example, if a query uses the "users" and "posts" tables, you can store this information. Later, when the app executes
// any mutation statements on these tables, you can remove the corresponding key from the cache.
// If you're okay with eventual consistency for your queries, you can skip this option.
override async put(
key: string,
response: any,
tables: string[],
isTag: boolean = false,
config?: CacheConfig,
): Promise {
const ttl = config?.px ?? (config?.ex ? config.ex * 1000 : this.globalTtl);
await this.kv.set(key, response, ttl);
for (const table of tables) {
const keys = this.usedTablesPerKey[table];
if (keys === undefined) {
this.usedTablesPerKey[table] = [key];
} else {
keys.push(key);
}
}
}
// This function is called when insert, update, or delete statements are executed.
// You can either skip this step or invalidate queries that used the affected tables.
//
// The function receives an object with two keys:
// - 'tags': Used for queries labeled with a specific tag, allowing you to invalidate by that tag.
// - 'tables': The actual tables affected by the insert, update, or delete statements,
// helping you track which tables have changed since the last cache update.
override async onMutate(params: {
tags: string | string[];
tables: string | string[] | Table | Table[];
}): Promise {
const tagsArray = params.tags
? Array.isArray(params.tags)
? params.tags
: [params.tags]
: [];
const tablesArray = params.tables
? Array.isArray(params.tables)
? params.tables
: [params.tables]
: [];
const keysToDelete = new Set();
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
const keys = this.usedTablesPerKey[tableName] ?? [];
for (const key of keys) keysToDelete.add(key);
}
if (keysToDelete.size > 0 || tagsArray.length > 0) {
for (const tag of tagsArray) {
await this.kv.delete(tag);
}
for (const key of keysToDelete) {
await this.kv.delete(key);
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
this.usedTablesPerKey[tableName] = [];
}
}
}
}
}
```
## Limitations
#### Queries that won't be handled by the `cache` extension:
- Using cache with raw queries, such as:
```ts
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
```ts
await db._query.users.findMany();
```
- Using cache with views
Source: https://orm.drizzle.team/docs/cockroach/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](/docs/cockroach/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](/docs/cockroach/sql-schema-declaration#shape-your-data-schema)
### 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),
});
```
```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()
});
```
```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)
});
```
```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()
});
```
```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)
});
```
```sql
CREATE TABLE "table" (
"int1" int4 DEFAULT 10
);
```
### int8
An alias of **[bigint.](#bigint)**
### int2
An alias of **[smallint.](#smallint)**
### ---
### bool
Cockroach provides the standard SQL type bool.
For more info please refer to the official Cockroach **[docs.](https://www.cockroachlabs.com/docs/stable/bool)**
```typescript
import { bool, cockroachTable } from "drizzle-orm/cockroach-core";
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.
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.](https://www.cockroachlabs.com/docs/stable/string)**
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.](https://www.cockroachlabs.com/docs/stable/string)**
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.](https://www.cockroachlabs.com/docs/stable/string)**
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.](https://www.cockroachlabs.com/docs/stable/string)**
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.](https://www.cockroachlabs.com/docs/stable/decimal)**
```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' }),
});
```
```sql
CREATE TABLE "table" (
"decimal1" decimal,
"decimal2" decimal(100),
"decimal3" decimal(100, 20),
"decimalNum" decimal,
"decimalBig" decimal
);
```
### numeric
An alias of **[decimal.](#decimal)**
### float
`float` `float8` `double precision`
For more info please refer to the official CockroachDB **[docs.](https://www.cockroachlabs.com/docs/stable/float)**
```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`),
});
```
```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.](https://www.cockroachlabs.com/docs/stable/float)**
```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`),
});
```
```sql
CREATE TABLE "table" (
"real1" real,
"real2" real default 10.10,
"real3" real default '10.10'::real
);
```
### double precision
An alias of **[float.](#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.](https://www.cockroachlabs.com/docs/stable/jsonb)**
```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();
// won't compile
jsonb: jsonb().$type().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.](https://www.cockroachlabs.com/docs/stable/bit)**
```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.](https://www.cockroachlabs.com/docs/stable/bit)**
```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.](https://www.cockroachlabs.com/docs/stable/uuid)**
```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.](https://www.cockroachlabs.com/docs/stable/time)**
```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 })
});
```
```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.](https://www.cockroachlabs.com/docs/stable/timestamp)**
```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.](https://www.cockroachlabs.com/docs/stable/date)**
```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" }),
```
### 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)**
```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.](https://www.cockroachlabs.com/docs/stable/enum)**
```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.](https://www.cockroachlabs.com/docs/stable/spatial-data-overview)**
```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' })
```
```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.](https://www.cockroachlabs.com/docs/stable/inet)**
```typescript
import { inet, cockroachTable } from "drizzle-orm/cockroach-core";
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.
```typescript
import { vector, cockroachTable } from "drizzle-orm/cockroach-core";
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:
```ts
type UserId = number & { __brand: 'user_id' };
type Data = {
foo: string;
bar: number;
};
const users = cockroachTable('users', {
id: int4().$type().primaryKey(),
jsonbField: jsonb().$type(),
});
```
### Identity Columns
To use this feature you would need to have `drizzle-orm@0.32.0` or higher and `drizzle-kit@0.23.0` 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(),
description: text(),
});
```
You can specify all properties available for sequences in the `.generatedAlwaysAsIdentity()` function. Additionally, you can specify custom names for these sequences
CockroachDB docs [reference](https://www.cockroachlabs.com/docs/stable/create-table#parameters).
### 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(sql`gen_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.
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().$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(),
});
```
```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
);
```
Source: https://orm.drizzle.team/docs/cockroach/connect-overview
# Database connection with Drizzle
Use these CockroachDB drivers and providers with Drizzle ORM.
## CockroachDB connection docs
- [CockroachDB](/docs/cockroach/get-started-cockroach)
Source: https://orm.drizzle.team/docs/cockroach/custom-types
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
```ts
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;
/**
* Type helper, that represents what type database driver is returning for specific database data type
*
* Needed only in case driver's output and input for type differ
*
* Defaults to {@link driverData}
*/
driverOutput?: unknown;
/**
* Type helper, that represents what type field returns after being aggregated to JSON
*/
jsonData?: unknown;
/**
* What config type should be used for {@link CustomTypeParams} `dataType` generation
*/
config?: Record;
/**
* 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 {
/**
* 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 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;
}
```
Source: https://orm.drizzle.team/docs/cockroach/data-querying
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.
```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
```ts
await db.insert(users).values({ email: 'user@gmail.com' })
```
```sql
INSERT INTO "users" ("email") VALUES ('user@gmail.com')
```
```ts
await db.update(users)
.set({ email: 'user@gmail.com' })
.where(eq(users.id, 1))
```
```sql
UPDATE "users"
SET "email" = 'user@gmail.com'
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?
Source: https://orm.drizzle.team/docs/cockroach/delete
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"
```
Source: https://orm.drizzle.team/docs/cockroach/drizzle-config-file
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](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-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",
});
```
```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",
});
```
## ---
### `dialect`
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export` |
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
});
```
### `schema`
[`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 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",
});
```
## ---
### `dbCredentials`
Database connection credentials in a form of `url`,
`user:password@host:port/db` params or exceptions drivers() specific connection options.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
```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` |
```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';
```
```
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"),
});
```
```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`
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`](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 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_*"],
});
```
### `schemaFilter`
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 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` |
```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,
});
```
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-check
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit check`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/cockroach/drizzle-kit-generate)
`drizzle-kit check` command lets you check consistency of your generated SQL migrations history.
That's extremely useful when you have multiple developers working on the project and
altering database schema on different branches - read more about [migrations for teams](/docs/cockroach/kit-migrations-for-teams).
`drizzle-kit check` command requires you to specify `dialect`,
you can provide it either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
});
```
```shell
npx drizzle-kit check
```
```shell
npx drizzle-kit check --dialect=cockroach
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit check --config=drizzle-dev.config.ts
drizzle-kit check --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Ignore conflicts
In case you need `check` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit check --ignore-conflicts
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit check --dialect=cockroach
drizzle-kit check --dialect=cockroach --out=./migrations-folder
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-export
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit export`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
`drizzle-kit export` lets you export SQL representation of Drizzle schema and print in console SQL DDL representation on it.
Drizzle Kit `export` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Based on the current json snapshot it will generate SQL DDL statements
3. Output SQL DDL statements to console
It's designed to cover [codebase first](/docs/cockroach/migrations) approach of managing Drizzle migrations.
You can export the SQL representation of the Drizzle schema, allowing external tools like Atlas to handle all the migrations for you
`drizzle-kit export` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit export
```
```shell
npx drizzle-kit export --dialect=cockroach --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 export --config=drizzle-dev.config.ts
drizzle-kit export --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of available configurations
`drizzle-kit export` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `--sql` | generating SQL representation of Drizzle Schema |
By default, Drizzle Kit outputs SQL files, but in the future, we want to support different formats
drizzle-kit export --sql=true
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
### Example
Example of how to export drizzle schema to console with Drizzle schema located in `./src/schema.ts`
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ configs
â â đ drizzle.config.ts
â đ src
â â đ schema.ts
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
});
```
```ts filename='schema.ts'
import { cockroachTable, int4, string } from 'drizzle-orm/cockroach-core'
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
email: string('email').notNull(),
name: string('name')
});
```
Now let's run
```shell
npx drizzle-kit export --config=./configs/drizzle.config.ts
```
And it will successfully output SQL representation of drizzle schema
```bash
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"email" string NOT NULL,
"name" string
);
```
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-generate
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit generate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
`drizzle-kit generate` lets you generate SQL migrations based on your Drizzle schema upon declaration or on subsequent schema changes.
Drizzle Kit `generate` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. It will read through your previous migrations folders and compare current json snapshot to the most recent one
3. Based on json differences it will generate SQL migrations
4. Save `migration.sql` and `snapshot.json` in migration folder under current timestamp
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ migration.sql
â â đ snapshot.json
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string,
"email" string,
CONSTRAINT "users_email_key" UNIQUE("email")
);
```
It's designed to cover [code first](/docs/cockroach/migrations) approach of managing Drizzle migrations.
You can apply generated migrations using [`drizzle-kit migrate`](/docs/cockroach/drizzle-kit-migrate), using drizzle-orm's `migrate()`,
using external migration tools like [bytebase](https://www.bytebase.com/) or running migrations yourself directly on the database.
`drizzle-kit generate` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit generate
```
```shell
npx drizzle-kit generate --dialect=cockroach --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Custom migration file name
You can set custom migration file names by providing `--name` CLI option
```shell
npx drizzle-kit generate --name=init
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ snapshot.json
â â đ migration.sql
â đ src
â âĻ
```
### Multiple configuration files in one project
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
```
### Custom migrations
You can generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding. Extended docs on custom migrations - [see here](/docs/cockroach/kit-custom-migrations)
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {5}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed/migration.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
### Ignore conflicts
In case you need `generate` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit generate --ignore-conflicts
```
### Extended list of available configurations
`drizzle-kit generate` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `custom` | generate empty SQL for custom migration |
| `name` | generate migration with custom name |
| `ignore-conflicts` | skip commutativity conflict checks, default is `false` |
drizzle-kit generate --name=init
drizzle-kit generate --name=seed_users --custom
drizzle-kit generate --ignore-conflicts
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `out` | | Migrations output folder, default is `./drizzle` |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `breakpoints` | | SQL statements breakpoints, default is `true` |
### Extended example
Example of how to create a custom cockroach migration file named `0001_seed-users.sql`
with Drizzle schema located in `./src/schema.ts` and migrations folder named `./migrations` instead of default `./drizzle`.
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ migrations
â đ configs
â â đ drizzle.config.ts
â đ src
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
out: "./migrations",
});
```
Now let's run
```shell
npx drizzle-kit generate --config=./configs/drizzle.config.ts --name=seed-users --custom
```
And it will successfully generate
```plaintext {6}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed-users/migration.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-migrate
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit migrate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/cockroach/drizzle-kit-generate)
`drizzle-kit migrate` lets you apply SQL migrations generated by [`drizzle-kit generate`](/docs/cockroach/drizzle-kit-generate).
It's designed to cover [code first(option 3)](/docs/cockroach/migrations) approach of managing Drizzle migrations.
Drizzle Kit `migrate` command triggers a sequence of events:
1. Reads through migration folder and read all `.sql` migration files
2. Connects to the database and fetches entries from drizzle migrations log table
3. Based on previously applied migrations it will decide which new migrations to run
4. Runs SQL migrations and logs applied migrations to drizzle migrations table
```plaintext
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ 20242409135510_delicate_professor_xavie
â âĻ
```
```plaintext
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. reads migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
`drizzle-kit migrate` command requires you to specify both `dialect` and database connection credentials,
you can provide them either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit migrate
```
```shell
npx drizzle-kit migrate --dialect=cockroach --url=postgresql://user:password@host:port/dbname
```
### Applied migrations log in the database
Upon running migrations Drizzle Kit will persist records about successfully applied migrations in your database.
It will store them in migrations log table named `__drizzle_migrations` in `drizzle` schema.
You can customise both **table** and **schema** of that table via drizzle config file:
```ts filename="drizzle.config.ts" {8-9}
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
schema: 'public', // `drizzle` by default
},
});
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended example
Let's generate SQL migration and apply it to our database using `drizzle-kit generate` and `drizzle-kit migrate` commands
```plaintext
đĻ
â đ drizzle
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
migrations: {
table: 'journal',
schema: 'drizzle',
},
});
```
```ts
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
})
```
Now let's run
```shell
npx drizzle-kit generate --name=init
```
it will generate
```plaintext {5}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â âĻ
```
```sql
-- ./drizzle/0000_init.sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string
);
```
Now let's run
```shell
npx drizzle-kit migrate
```
and our SQL migration is now successfully applied to the database â
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-pull
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 Prerequisites from "@mdx/Prerequisites.astro";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import Npm from "@mdx/Npm.astro"
import Npx from "@mdx/Npx.astro"
# `drizzle-kit pull`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file) docs
`drizzle-kit pull` lets you literally pull(introspect) your existing database schema and generate `schema.ts` drizzle schema file,
it is designed to cover [database first](/docs/cockroach/migrations) approach of Drizzle migrations.
When you run Drizzle Kit `pull` command it will:
1. Pull database schema(DDL) from your existing database
2. Generate `schema.ts` drizzle schema file and save it to `out` folder
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââ
â â <--- CREATE TABLE "users" (
ââââââââââââââââââââââââââââ â â "id" int4 PRIMARY KEY,
â ~ drizzle-kit pull â â â "name" string,
âââŦâââââââââââââââââââââââââ â â "email" string unique
â â DATABASE â );
â â â
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string(),
},
(table) => [p.uniqueIndex('users_email_key').using('btree', table.email.asc())],
);
```
It is a great approach if you need to manage database schema outside of your TypeScript project or
you're using database, which is managed by somebody else.
`drizzle-kit pull` requires you to specify `dialect` and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit pull
```
```shell
npx drizzle-kit pull --dialect=cockroach --url=postgresql://user:password@host:port/dbname
```
### Multiple configuration files in one project
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 pull --config=drizzle-dev.config.ts
drizzle-kit pull --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Specifying database driver
Drizzle Kit does not come with a pre-bundled database driver,
it will automatically pick available database driver from your current project based on the `dialect` - [see discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2203).
### Initial pull
You can use the `--init` flag to mark the pulled schema as an applied migration in your database,
so that all subsequent migrations are diffed against the initial one
```shell
npx drizzle-kit pull --init
```
### Including tables, schemas and extensions
`drizzle-kit pull` will by default manage all tables in all schemas.
You can configure list of tables, schemas and extensions via `tablesFilters`, `schemaFilter` and `extensionFilters` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | `glob` based schema names filter, e.g. `["public", "drizzle"]` or `"drizzle*"`. Default is `"*"` |
Let's configure drizzle-kit to only operate with **all tables** in **public** schema
and let drizzle-kit know that there's a **postgis** extension installed,
which creates it's own tables in public schema, so drizzle can ignore them.
```ts filename="drizzle.config.ts" {9-11}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
schemaFilter: ["public"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit pull
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `driver` | | Drivers exceptions |
| `out` | | Migrations output folder path, default is `./drizzle` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `introspect-casing` | | Strategy for JS keys creation in columns, tables, etc. `preserve` `camel` |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
drizzle-kit pull --dialect=cockroach --url=postgresql://user:password@host:port/dbname
drizzle-kit pull --dialect=cockroach --driver=pglite url=database/
drizzle-kit pull --dialect=cockroach --tablesFilter='user*' url=postgresql://user:password@host:port/dbname
{/* TODO update gif */}

Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-push
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import DriversExamples from "@mdx/DriversExamples.mdx"
# `drizzle-kit push`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file) docs
`drizzle-kit push` lets you literally push your schema and subsequent schema changes directly to the
database while omitting SQL files generation, it's designed to cover [code first](/docs/cockroach/migrations)
approach of Drizzle migrations.
When you run Drizzle Kit `push` command it will:
1. Read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Pull(introspect) database schema
3. Based on differences between those two it will generate SQL migrations
4. Apply SQL migrations to the database
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
});
```
```
âââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
âââââââââââââââââââââââââââââââââââââââââââââ´âââââââââââââââââ
CREATE TABLE "users" ("id" int4 PRIMARY KEY, "name" string);
```
It's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
It pairs exceptionally well with blue/green deployment strategy and serverless databases like
[Planetscale](https://planetscale.com), [Neon](https://neon.tech), [Turso](https://turso.tech/drizzle) and others.
`drizzle-kit push` requires you to specify `dialect`, path to the `schema` file(s) and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit push
```
```shell
npx drizzle-kit push --dialect=cockroach --schema=./src/schema.ts --url=postgresql://user:password@host:port/dbname
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 push --config=drizzle-dev.config.ts
drizzle-kit push --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Specifying database driver
Drizzle Kit does not come with a pre-bundled database driver,
it will automatically pick an available database driver from your current project based on the `dialect` - [see discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2203).
### Including tables and schemas
`drizzle-kit push` will manage all tables and schemas by default.
You can configure list of tables, schemas via `tablesFilter` and `schemaFilter` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | Schema names filter, e.g. `["public", "drizzle"]`. Default is "*" |
Let's configure drizzle-kit to only operate with **all tables** in **public** schema
and let drizzle-kit know that there's a **postgis** extension installed,
which creates it's own tables in public schema, so drizzle can ignore them.
```ts filename="drizzle.config.ts" {9-10}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
schemaFilter: ["public"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit push
```
### Extended list of configurations
`drizzle-kit push` has a list of cli-only options
| | |
| :-------- | :---------------------------------------------------------------|
| `verbose` | print all SQL statements prior to execution |
| `explain` | print the planned SQL changes, without applying them (dry run) |
| `force` | auto-accept all data-loss statements |
drizzle-kit push --explain --verbose --force
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit push dialect=cockroach schema=src/schema.ts url=postgresql://user:password@host:port/dbname
drizzle-kit push dialect=cockroach schema=src/schema.ts driver=pglite url=database/
drizzle-kit push dialect=cockroach schema=src/schema.ts --tablesFilter='user*' url=postgresql://user:password@host:port/dbname
### Extended example
Let's declare drizzle schema in the project and push it to the database via `drizzle-kit push` command
```plaintext
đĻ
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```ts
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
})
```
Now let's run
```shell
npx drizzle-kit push
```
it will pull existing(empty) schema from the database and generate SQL migration and apply it under the hood
```sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string
);
```
DONE â
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-studio
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit studio`
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
- Drizzle Studio, our database browser - [read here](/drizzle-studio/overview)
`drizzle-kit studio` command spins up a server for [Drizzle Studio](/drizzle-studio/overview) hosted on [local.drizzle.studio](https://local.drizzle.studio).
It requires you to specify database connection credentials via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file.
By default it will start a Drizzle Studio server on `127.0.0.1:4983`
```ts {6}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit studio
```
### Configuring `host` and `port`
By default Drizzle Studio server starts on `127.0.0.1:4983`,
you can config `host` and `port` via CLI options
drizzle-kit studio --port=3000
drizzle-kit studio --host=0.0.0.0
drizzle-kit studio --host=0.0.0.0 --port=3000
### Logging
You can enable logging of every SQL statement by providing `verbose` flag
drizzle-kit studio --verbose
### Safari and Brave support
Safari and Brave block access to localhost by default.
You need to install [mkcert](https://github.com/FiloSottile/mkcert) and generate self-signed certificate:
1. Follow the mkcert [installation steps](https://github.com/FiloSottile/mkcert#installation)
2. Run `mkcert -install`
3. Restart your `drizzle-kit studio`
### Embeddable version of Drizzle Studio
While hosted version of Drizzle Studio for local development is free forever and meant to just enrich Drizzle ecosystem,
we have a B2B offering of an embeddable version of Drizzle Studio for businesses.
**Drizzle Studio component** - is a pre-bundled framework agnostic web component of Drizzle Studio
which you can embed into your UI `React` `Vue` `Svelte` `VanillaJS` etc.
That is an extremely powerful UI element that can elevate your offering
if you provide Database as a SaaS or a data centric SaaS solutions based
on SQL or for private non-customer facing in-house usage.
Database platforms using Drizzle Studio:
- [Turso](https://turso.tech/), our first customers since Oct 2023!
- [Neon](https://neon.tech/), [launch post](https://neon.tech/docs/changelog/2024-05-24)
- [Hydra](https://www.hydra.so/)
Data centric platforms using Drizzle Studio:
- [Nuxt Hub](https://hub.nuxt.com/), SÊbastien Chopin's [launch post](https://x.com/Atinux/status/1768663789832929520)
- [Deco.cx](https://deco.cx/)
You can read a detailed overview [here](https://www.npmjs.com/package/@drizzle-team/studio) and
if you're interested - hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Drizzle Studio chrome extension
Drizzle Studio [chrome extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd)
lets you browse your [PlanetScale](https://planetscale.com),
[Cloudflare](https://developers.cloudflare.com/d1/) and [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres)
serverless databases directly in their vendor admin panels!
### Limitations
Our hosted version Drizzle Studio is meant to be used for local development and not meant to be used on remote (VPS, etc).
If you want to deploy Drizzle Studio to your VPS - we have an alpha version of Drizzle Studio Gateway,
hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Is it open source?
No. Drizzle ORM and Drizzle Kit are fully open sourced, while Studio is not.
Drizzle Studio for local development is free to use forever to enrich Drizzle ecosystem,
open sourcing one would've break our ability to provide B2B offerings and monetise it, unfortunately.
Source: https://orm.drizzle.team/docs/cockroach/drizzle-kit-up
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit up`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/cockroach/drizzle-kit-generate)
`drizzle-kit up` command lets you upgrade drizzle schema snapshots to a newer version.
It's required whenever we introduce breaking changes to the json snapshots of the schema and upgrade the internal version.
`drizzle-kit up` command requires you to specify `dialect` param,
you can provide it either via [drizzle.config.ts](/docs/cockroach/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
});
```
```shell
npx drizzle-kit up
```
```shell
npx drizzle-kit up --dialect=cockroach
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit up --dialect=cockroach
drizzle-kit up --dialect=cockroach --out=./migrations-folder
{/* TODO: update up gif */}

Source: https://orm.drizzle.team/docs/cockroach/dynamic-query-building-copy
import Callout from '@mdx/Callout.astro';
# Dynamic query building
By default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once.
For example, in a `SELECT` statement there might only be one `WHERE` clause, so you can only invoke `.where()` once:
```ts
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // â Type error - where() can only be invoked once
```
In the previous ORM versions, when such restrictions weren't implemented, this example in particular was a source of confusion for many users, as they expected the query builder to "merge" multiple `.where()` calls into a single condition.
This behavior is useful for conventional query building, i.e. when you create the whole query at once.
However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it.
To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once.
To enable it, you need to call `.$dynamic()` on a query builder.
Let's see how it works by implementing a simple `withPagination` function that adds `LIMIT` and `OFFSET` clauses to a query based on the provided page number and an optional page size:
```ts
function withPagination(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // â Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // â
OK
```
Note that the `withPagination` function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:
```ts
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
This is possible, because `PgSelect` and other similar types are specifically designed to be used in dynamic query building. They can only be used in dynamic mode.
Here is the list of all types that can be used as generic parameters in dynamic query building:
{
|
Dialect
|
Type
|
|
Query
|
Select
|
Insert
|
Update
|
Delete
|
| Postgres |
PgSelect
|
PgInsert
|
PgUpdate
|
PgDelete
|
PgSelectQueryBuilder
|
| MySQL |
MySqlSelect
|
MySqlInsert
|
MySqlUpdate
|
MySqlDelete
|
MySqlSelectQueryBuilder
|
| SQLite |
SQLiteSelect
|
SQLiteInsert
|
SQLiteUpdate
|
SQLiteDelete
|
SQLiteSelectQueryBuilder
|
}
The `...QueryBuilder` types are for usage with [standalone query builder
instances](/docs/goodies#standalone-query-builder). DB query builders are
subclasses of them, so you can use them as well.
```ts
import { QueryBuilder } from 'drizzle-orm/pg-core';
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
Source: https://orm.drizzle.team/docs/cockroach/dynamic-query-building
import Callout from '@mdx/Callout.astro';
# Dynamic query building
By default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once.
For example, in a `SELECT` statement there might only be one `WHERE` clause, so you can only invoke `.where()` once:
```ts
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // â Type error - where() can only be invoked once
```
In the previous ORM versions, when such restrictions weren't implemented, this example in particular was a source of confusion for many users, as they expected the query builder to "merge" multiple `.where()` calls into a single condition.
This behavior is useful for conventional query building, i.e. when you create the whole query at once.
However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it.
To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once.
To enable it, you need to call `.$dynamic()` on a query builder.
Let's see how it works by implementing a simple `withPagination` function that adds `LIMIT` and `OFFSET` clauses to a query based on the provided page number and an optional page size:
```ts
function withPagination(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // â Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // â
OK
```
Note that the `withPagination` function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:
```ts
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
This is possible, because `CockroachSelect` and other similar types are specifically designed to be used in dynamic query building. They can only be used in dynamic mode.
Here is the list of all types that can be used as generic parameters in dynamic query building:
| Query | Type |
|:--------|:-----------------------------------|
| Select | `CockroachSelect` / `CockroachSelectQueryBuilder` |
| Insert | `CockroachInsert` |
| Update | `CockroachUpdate` |
| Delete | `CockroachDelete` |
The `...QueryBuilder` types are for usage with [standalone query builder
instances](/docs/cockroach/goodies#standalone-query-builder). DB query builders are
subclasses of them, so you can use them as well.
```ts
import { QueryBuilder } from 'drizzle-orm/cockroach-core';
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
Source: https://orm.drizzle.team/docs/cockroach/effect-schema
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# effect-schema
Allows you to generate [effect](https://effect.website/) schemas from Drizzle ORM schemas.
**Features**
- Create a select schema for tables and enums.
- Create insert and update schemas for tables.
- Supported dialect: CockroachDB.
#### Usage
```ts
import { int4, cockroachTable, text, timestamp } from 'drizzle-orm/cockroach-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/effect-schema';
import { Effect, Schema } from 'effect';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
email: text().notNull(),
role: text({ enum: ['admin', 'user'] }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// Schema for inserting a user - can be used to validate API requests
const UserInsert = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const UserUpdate = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const UserSelect = createSelectSchema(users);
// Overriding the fields
const UserInsert = createInsertSchema(users, {
role: Schema.String,
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const UserInsert = createInsertSchema(users, {
id: (schema) => schema.check(Schema.isGreaterThanOrEqualTo(0)),
role: Schema.String,
});
// Usage
const program = Effect.gen(function*() {
const parsedUser = yield* Schema.decodeUnknownEffect(UserInsert)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
});
```
Source: https://orm.drizzle.team/docs/cockroach/extensions
import Callout from '@mdx/Callout.astro';
Currently, there are no Cockroach extensions natively supported by Drizzle. Once those are added, we will have them here!
Source: https://orm.drizzle.team/docs/cockroach/generated-columns
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Generated Columns
Stored (or persistent) Generated Columns: These columns are computed when a row is inserted or updated and their values are stored in the database. This allows them to be indexed and can improve query performance since the values do not need to be recomputed for each query.
#### Database side
**Types**: `STORED` only
**How It Works**
- Automatically computes values based on other columns during insert or update.
**Capabilities**
- Simplifies data access by precomputing complex expressions.
- Enhances query performance with index support on generated columns.
**Limitations**
- Cannot specify default values.
- Expressions cannot reference other generated columns or include subqueries.
- Schema changes required to modify generated column expressions.
- Cannot directly use in primary keys, foreign keys, or unique constraints
For more info, please check [CockroachDB](https://www.cockroachlabs.com/docs/stable/computed-columns) docs
#### Drizzle side
In Drizzle you can specify `.generatedAlwaysAs()` function on any column type and add a supported sql query,
that will generate this column data for you.
#### Features
This function can accept generated expression in 2 ways:
**`sql`** tag - if you want drizzle to escape some values for you
```ts
export const test = cockroachTable("test", {
generatedName: string("gen_name").generatedAlwaysAs(sql`'hello "world"!'`),
});
```
```sql
CREATE TABLE "test" (
"gen_name" text GENERATED ALWAYS AS ('hello "world"!') STORED
);
```
**`callback`** - if you need to reference columns from a table
```ts
export const test = cockroachTable("test", {
name: string("first_name"),
generatedName: string("gen_name").generatedAlwaysAs(
(): SQL => sql`'hi, ' || ${test.name} || '!'`
),
});
```
```sql
CREATE TABLE "test" (
"first_name" string,
"gen_name" string GENERATED ALWAYS AS ('hi, ' || "test"."first_name" || '!') STORED
);
```
**Example** generated columns with full-text search
```typescript copy {17-19}
import { SQL, sql } from "drizzle-orm";
import { customType, index, int4, cockroachTable, string } from "drizzle-orm/cockroach-core";
const tsVector = customType<{ data: string }>({
dataType() {
return "tsvector";
},
});
export const test = cockroachTable(
"test",
{
id: int4("id").primaryKey().generatedAlwaysAsIdentity(),
content: string("content"),
contentSearch: tsVector("content_search", {
dimensions: 3,
}).generatedAlwaysAs(
(): SQL => sql`to_tsvector('english', ${test.content})`
),
},
(t) => [
index("idx_content_search").using("gin", t.contentSearch)
]
);
```
```sql {4}
CREATE TABLE "test" (
"id" int4 PRIMARY KEY GENERATED ALWAYS AS IDENTITY (INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"content" string,
"content_search" tsvector GENERATED ALWAYS AS (to_tsvector('english', "test"."content")) STORED
);
CREATE INDEX "idx_content_search" ON "test" USING gin ("content_search");
```
Source: https://orm.drizzle.team/docs/cockroach/get-started-cockroach
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextCockroach from "@mdx/WhatsNextCockroach.astro";
# Drizzle \<\> CockroachDB
- Database [connection basics](/docs/cockroach/connect-overview) with Drizzle
- node-postgres [basics](https://node-postgres.com/)
Drizzle has native support for CockroachDB connections with the `node-postgres` driver.
#### Step 1 - Install packages
drizzle-orm@rc pg
-D drizzle-kit@rc @types/pg
#### Step 2 - Initialize the driver and make a query
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/cockroach';
// You can specify any property from the node-postgres connection options
const db = drizzle({
connection: {
connectionString: process.env.DATABASE_URL,
ssl: true
}
});
const result = await db.execute('select 1');
```
If you need to provide your existing driver:
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from "drizzle-orm/cockroach";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/cockroach/goodies
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
## Type API
Use Drizzle's type helpers to infer `select` and `insert` models from a CockroachDB table schema.
```ts
import { string, int4, cockroachTable } from 'drizzle-orm/cockroach-core';
import { type InferSelectModel, type InferInsertModel } from 'drizzle-orm';
const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: string('name').notNull(),
});
type SelectUser = typeof users.$inferSelect;
type InsertUser = typeof users.$inferInsert;
type SelectUserAlt = InferSelectModel;
type InsertUserAlt = InferInsertModel;
```
## Logging
Enable default query logging by passing `{ logger: true }` to `drizzle`.
```ts
import { drizzle } from "drizzle-orm/cockroach";
const db = drizzle(process.env.DB_URL, { logger: true });
```
You can change the logs destination by creating a `DefaultLogger` instance and providing a custom `writer` to it:
```typescript copy
import { DefaultLogger, type LogWriter } from "drizzle-orm/logger";
import { drizzle } from "drizzle-orm/...";
class MyLogWriter implements LogWriter {
write(message: string) {
// Write to file, stdout, etc.
}
}
const logger = new DefaultLogger({ writer: new MyLogWriter() });
const db = drizzle(process.env.DB_URL, { logger });
```
You can also provide a custom logger.
```ts
import type { Logger } from 'drizzle-orm/logger';
import { drizzle } from "drizzle-orm/cockroach";
class MyLogger implements Logger {
logQuery(query: string, params: unknown[]): void {
console.log({ query, params });
}
}
const db = drizzle(process.env.DB_URL, { logger: new MyLogger() });
```
## Multi-project schema
Use `cockroachTableCreator` to customize table names when several projects share one database.
```ts
import { int4, string, cockroachTableCreator } from 'drizzle-orm/cockroach-core';
const cockroachTable = cockroachTableCreator((name) => `project1_${name}`);
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: string().notNull(),
});
```
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema/*',
out: './drizzle',
dialect: 'cockroach',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
tablesFilter: ['project1_*'],
});
```
```sql
CREATE TABLE "project1_users" (
"id" int4 PRIMARY KEY,
"name" string NOT NULL
);
```
## Printing SQL Query
Print generated SQL from a query with `.toSQL()`.
```ts
const query = db
.select({ id: users.id, name: users.name })
.from(users)
.groupBy(users.id)
.toSQL();
```
## Raw SQL Queries
Use `db.execute` for raw parametrized SQL.
```ts
import { sql } from 'drizzle-orm';
const statement = sql`select * from ${users} where ${users.id} = ${userId}`;
const result = await db.execute(statement);
```
## Standalone Query Builder
Use the CockroachDB query builder without creating a database instance.
```ts
import { QueryBuilder } from 'drizzle-orm/cockroach-core';
const qb = new QueryBuilder();
const query = qb.select().from(users).where(eq(users.name, 'Dan'));
const { sql, params } = query.toSQL();
```
## Get Typed Columns
Use `getColumns` to get a typed column map, which is useful when excluding fields from a selection.
```ts
import { getColumns } from 'drizzle-orm';
import { users } from './schema';
const { password, role, ...rest } = getColumns(users);
await db.select({ ...rest }).from(users);
```
```ts
import { int4, string, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: int4().primaryKey(),
name: string(),
email: string(),
password: string(),
role: string().$type<"admin" | "customer">(),
});
```
## Get Table Information
Use `getTableConfig` to inspect CockroachDB table metadata.
```ts
import { getTableConfig, cockroachTable } from 'drizzle-orm/cockroach-core';
export const table = cockroachTable(...);
const { columns, indexes, foreignKeys, checks, primaryKeys, name, schema } =
getTableConfig(table);
```
## Compare Object Types
Use `is()` instead of `instanceof` to check Drizzle object types.
```ts
import { Column, is } from 'drizzle-orm';
if (is(value, Column)) {
// value is narrowed to Column
}
```
## Mock Driver
Use `drizzle.mock()` when you need a typed database object without a real CockroachDB connection.
```ts
import { drizzle } from 'drizzle-orm/node-postgres';
import * as schema from './schema';
const db = drizzle.mock({ schema });
```
Source: https://orm.drizzle.team/docs/cockroach/indexes-constraints
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Indexes & Constraints
## Constraints
SQL constraints are the rules enforced on table columns. They are used to prevent invalid data from being entered into the database.
This ensures the accuracy and reliability of your data in the database.
### Default
The `DEFAULT` clause specifies a default value to use for the column if no value 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, uuid, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
integer1: int4().default(42),
integer2: int4().default(sql`24`),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sql`gen_random_uuid()`),
});
```
```sql
CREATE TABLE "table" (
"integer1" int4 DEFAULT 42,
"integer2" int4 DEFAULT 24,
"uuid1" uuid DEFAULT gen_random_uuid(),
"uuid2" uuid DEFAULT gen_random_uuid()
);
```
### Not null
By default, a column can hold **NULL** values. The `NOT NULL` constraint enforces a column to **NOT** accept **NULL** values.
This enforces a field to always contain a value, which means that you cannot insert a new record,
or update a record without adding a value to this field.
```typescript copy
import { int4, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
int4: int4().notNull(),
});
```
```sql
CREATE TABLE "table" (
"int4" int4 NOT NULL
);
```
### Unique
The `UNIQUE` constraint ensures that all values in a column are different.
Both the `UNIQUE` and `PRIMARY KEY` constraints provide a guarantee for uniqueness for a column or set of columns.
A `PRIMARY KEY` constraint automatically has a `UNIQUE` constraint.
You can have many `UNIQUE` constraints per table, but only one `PRIMARY KEY` constraint per table.
```typescript copy
import { int4, string, unique, cockroachTable } from "drizzle-orm/cockroach-core";
export const user = cockroachTable('user', {
id: int4().unique(),
});
export const table = cockroachTable('table', {
id: int4().unique('custom_name'),
});
export const composite = cockroachTable('composite_example', {
id: int4(),
name: string(),
}, (t) => [
unique().on(t.id, t.name),
unique('custom_name').on(t.id, t.name)
]);
```
```sql
CREATE TABLE "user" (
"id" int4 UNIQUE
);
CREATE TABLE "table" (
"id" int4 CONSTRAINT "custom_name" UNIQUE
);
CREATE TABLE "composite_example" (
"id" int4,
"name" string,
CONSTRAINT "composite_example_id_name_unique" UNIQUE("id","name"),
CONSTRAINT "custom_name" UNIQUE("id","name")
);
```
### Check
The `CHECK` constraint is used to limit the value range that can be placed in a column.
If you define a `CHECK` constraint on a column it will allow only certain values for this column.
If you define a `CHECK` constraint on a table it can limit the values in certain columns based on values in other columns in the row.
```typescript copy
import { sql } from "drizzle-orm";
import { check, int4, cockroachTable, string, uuid } from "drizzle-orm/cockroach-core";
export const users = cockroachTable(
"users",
{
id: uuid().defaultRandom().primaryKey(),
username: string().notNull(),
age: int4(),
},
(table) => [
check("age_check1", sql`${table.age} > 21`),
]
);
```
```sql
CREATE TABLE "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"username" string NOT NULL,
"age" int4,
CONSTRAINT "age_check1" CHECK ("users"."age" > 21)
);
```
### Primary Key
The `PRIMARY KEY` constraint uniquely identifies each record in a table.
Primary keys must contain `UNIQUE` values, and cannot contain `NULL` values.
A table can have only **ONE** primary key; and in the table, this primary key can consist of single or multiple columns (fields).
```typescript copy
import { int4, string, cockroachTable } from "drizzle-orm/cockroach-core";
export const user = cockroachTable('user', {
id: int4('id').primaryKey(),
});
export const table = cockroachTable('table', {
id: string('cuid').primaryKey(),
});
```
```sql
CREATE TABLE "user" (
"id" int4 PRIMARY KEY
);
CREATE TABLE "table" (
"cuid" string PRIMARY KEY
);
```
### Composite Primary Key
Just like `PRIMARY KEY`, composite primary key uniquely identifies each record in a table using multiple fields.
Drizzle ORM provides a standalone `primaryKey` operator for that:
```typescript copy {17,19}
import { int4, string, primaryKey, cockroachTable } from "drizzle-orm/cockroach-core";
export const user = cockroachTable("user", {
id: int4("id").primaryKey(),
name: string("name"),
});
export const book = cockroachTable("book", {
id: int4("id").primaryKey(),
name: string("name"),
});
export const booksToAuthors = cockroachTable("books_to_authors", {
authorId: int4("author_id"),
bookId: int4("book_id"),
}, (table) => [
primaryKey({ columns: [table.bookId, table.authorId] }),
// Or PK with custom name
primaryKey({ name: 'custom_name', columns: [table.bookId, table.authorId] }),
]);
```
```sql {6,9}
...
CREATE TABLE "books_to_authors" (
"author_id" int4,
"book_id" int4,
PRIMARY KEY("book_id","author_id")
);
ALTER TABLE "books_to_authors" ADD CONSTRAINT "custom_name" PRIMARY KEY("book_id","author_id");
```
### Foreign key
The `FOREIGN KEY` constraint is used to prevent actions that would destroy links between tables.
A `FOREIGN KEY` is a field (or collection of fields) in one table, that refers to the `PRIMARY KEY` in another table.
The table with the foreign key is called the child table, and the table with the primary key is called the referenced or parent table.
Drizzle ORM provides several ways to declare foreign keys.
You can declare them in a column declaration statement:
```typescript copy {11}
import { int4, string, cockroachTable } from "drizzle-orm/cockroach-core";
export const user = cockroachTable("user", {
id: int4("id").primaryKey(),
name: string("name"),
});
export const book = cockroachTable("book", {
id: int4("id"),
name: string("name"),
authorId: int4("author_id").references(() => user.id)
});
```
```sql {12}
CREATE TABLE "user" (
"id" int4 PRIMARY KEY,
"name" string
);
CREATE TABLE "book" (
"id" int4,
"name" string,
"author_id" int4
);
ALTER TABLE "book" ADD CONSTRAINT "book_author_id_user_id_fkey" FOREIGN KEY ("author_id") REFERENCES "user"("id");
```
If you want to do a self reference, due to a TypeScript limitations you will have to either explicitly
set return type for reference callback or use a standalone `foreignKey` operator.
```typescript copy {6,16-18}
import { int4, string, foreignKey, cockroachTable, type AnyCockroachColumn } from "drizzle-orm/cockroach-core";
export const user = cockroachTable("user", {
id: int4().primaryKey(),
name: string(),
parentId: int4("parent_id").references((): AnyCockroachColumn => user.id)
});
// or
export const user = cockroachTable("user", {
id: int4(),
name: string(),
parentId: int4("parent_id"),
}, (table) => [
foreignKey({
columns: [table.parentId],
foreignColumns: [table.id],
name: "custom_fk"
})
]);
```
```sql {7}
CREATE TABLE "user" (
"id" int4 PRIMARY KEY,
"name" string,
"parent_id" int4
);
ALTER TABLE "user" ADD CONSTRAINT "user_parent_id_user_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "user"("id");
```
To declare multi-column foreign keys you can use a dedicated `foreignKey` operator:
```typescript copy {15-19}
import { int4, string, foreignKey, cockroachTable, primaryKey } from "drizzle-orm/cockroach-core";
export const user = cockroachTable("user", {
firstName: string("firstName"),
lastName: string("lastName"),
}, (table) => [
primaryKey({ columns: [table.firstName, table.lastName]})
]);
export const profile = cockroachTable("profile", {
id: int4("id").primaryKey(),
userFirstName: string("user_first_name"),
userLastName: string("user_last_name"),
}, (table) => [
foreignKey({
columns: [table.userFirstName, table.userLastName],
foreignColumns: [user.firstName, user.lastName],
name: "custom_fk"
})
])
```
```sql {13}
CREATE TABLE "user" (
"firstName" string,
"lastName" string,
CONSTRAINT "user_pkey" PRIMARY KEY("firstName","lastName")
);
CREATE TABLE "profile" (
"id" int4 PRIMARY KEY,
"user_first_name" string,
"user_last_name" string
);
ALTER TABLE "profile" ADD CONSTRAINT "custom_fk" FOREIGN KEY ("user_first_name","user_last_name") REFERENCES "user"("firstName","lastName");
```
## Indexes
Drizzle ORM provides API for both `index` and `unique index` declaration:
```typescript copy {8-9}
import { int4, string, index, uniqueIndex, cockroachTable } from "drizzle-orm/cockroach-core";
export const user = cockroachTable("user", {
id: int4().primaryKey(),
name: string(),
email: string(),
}, (table) => [
index("name_idx").on(table.name),
uniqueIndex("email_idx").on(table.email)
]);
```
```sql {3,6}
CREATE TABLE "user" (
...,
CONSTRAINT "email_idx" UNIQUE("email")
);
CREATE INDEX "name_idx" ON "user" ("name");
```
Drizzle ORM provides a set of params for index creation:
```ts
// `.on()`
index('name')
.on(table.column1.asc(), ...)
.where(sql``) // sql expression
// `.onOnly()`
index('name')
.onOnly(table.column1.asc(), ...)
.where(sql``) // sql expression
// Second Example, with `.using()`
index('name')
.using('btree', table.column1.asc(), sql`lower(${table.column2})`)
.where(sql``) // sql expression
```
Source: https://orm.drizzle.team/docs/cockroach/insert
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
# SQL Insert
All the values provided to `.values()`are parameterized automatically.
For example, this query:
```ts
await db.insert(users).values({ name: 'Andrew' });
```
will be translated to:
```sql
insert into "users" ("id", "name") values (default, $1) -- params: ['Andrew']
```
Drizzle ORM provides you the most SQL-like way to insert rows into the database tables.
Inserting data with Drizzle is extremely straightforward and sql-like. See for yourself:
```typescript copy
await db.insert(users).values({ name: 'Andrew' });
```
```sql
insert into "users" ("id", "name", "age") values (default, 'Andrew', default)
```
If you need insert type for a particular table you can use `typeof usersTable.$inferInsert` syntax.
```typescript copy
type NewUser = typeof users.$inferInsert;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);
```
## returning
You can insert a row and get it back in CockroachDB like such:
```typescript copy
await db.insert(users).values({ name: "Dan" }).returning();
// partial return
await db.insert(users).values({ name: "Partial Dan" }).returning({ insertedId: users.id });
```
## Insert multiple rows
```typescript copy
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);
```
## Upserts and conflicts
Drizzle ORM provides simple interfaces for handling upserts and conflicts.
### On conflict do nothing
`onConflictDoNothing` will cancel the insert if there's a conflict:
```typescript copy
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing();
// explicitly specify conflict target
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing({ target: users.id });
```
### On conflict do update
`onConflictDoUpdate` will update the row if there's a conflict:
```typescript
await db.insert(users)
.values({ id: 1, name: 'Dan' })
.onConflictDoUpdate({ target: users.id, set: { name: 'John' } });
```
#### `where` clauses
`on conflict do update` can have a `where` clause in two different places -
as part of the conflict target (i.e. for partial indexes) or as part of the `update` clause:
```sql
insert into "employees" ("employee_id", "name")
values (123, 'John Doe')
on conflict ("employee_id") where name <> 'John Doe'
do update set "name" = excluded.name;
insert into "employees" ("employee_id", "name")
values (123, 'John Doe')
on conflict ("employee_id") do update set "name" = 'John Doe'
where name <> 'John Doe';
```
To specify these conditions in Drizzle, you can use `setWhere` and `targetWhere` clauses:
```typescript
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
targetWhere: sql`name <> 'John Doe'`,
set: { name: sql`excluded.name` }
});
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
set: { name: 'John Doe' },
setWhere: sql`name <> 'John Doe'`
});
```
Upsert with composite indexes, or composite primary keys for `onConflictDoUpdate`:
```typescript
await db.insert(users)
.values({ firstName: 'John', lastName: 'Doe' })
.onConflictDoUpdate({
target: [users.firstName, users.lastName],
set: { firstName: 'John1' }
});
```
## `with insert` clause
Check how to use WITH statement with [select](/docs/cockroach/select#with-clause), [update](/docs/cockroach/update#with-update-clause), [delete](/docs/cockroach/delete#with-delete-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 userCount = db.$with('user_count').as(
db.select({ value: sql`count(*)`.as('value') }).from(users)
);
const result = await db.with(userCount)
.insert(users)
.values([
{ username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }
])
.returning({
admin: users.admin
});
```
```sql
with "user_count" as (select count(*) as "value" from "users")
insert into "users" ("username", "admin")
values ('user1', ((select * from "user_count") = 0))
returning "admin"
```
## Insert into ... select
As the CockroachDB documentation mentions:
A query (SELECT statement) that supplies the rows to be inserted
Drizzle supports the current syntax for all dialects, and all of them share the same syntax. Let's review some common scenarios and API usage.
There are several ways to use select inside insert statements, allowing you to choose your preferred approach:
- You can pass a query builder inside the select function.
- You can use a query builder inside a callback.
- You can pass an SQL template tag with any custom select query you want to use
```ts
const insertedEmployees = await db
.insert(employees)
.select(
db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
)
.returning({
id: employees.id,
name: employees.name
});
```
```ts
const qb = new QueryBuilder();
await db.insert(employees).select(
qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
() => db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
(qb) => qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
sql`select "users"."id" as "id", "users"."name" as "name" from "users" where "users"."role" = 'employee'`
);
```
```ts
await db.insert(employees).select(
() => sql`select "users"."id" as "id", "users"."name" as "name" from "users" where "users"."role" = 'employee'`
);
```
Source: https://orm.drizzle.team/docs/cockroach/joins
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Joins [SQL]
Join clause in SQL is used to combine 2 or more tables, based on related columns between them.
Drizzle ORM joins syntax is a balance between the SQL-likeness and type safety.
## Join types
Drizzle ORM has APIs for `INNER JOIN [LATERAL]`, `FULL JOIN`, `LEFT JOIN [LATERAL]`, `RIGHT JOIN`, `CROSS JOIN [LATERAL]`.
Lets have a quick look at examples based on below table schemas:
```typescript copy
export const users = cockroachTable("users", {
id: int4().primaryKey(),
name: string().notNull(),
age: int4(),
});
export const pets = cockroachTable("pets", {
id: int4().primaryKey(),
name: string().notNull(),
ownerId: int4("owner_id")
.notNull()
.references(() => users.id),
});
```
### Left Join
```typescript copy
const result = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Left Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).leftJoinLateral(subquery, sql`true`)
```
```sql
select ... from "users" left join lateral (select ... from "pets" where "users"."age" >= 16) "userPets" on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Right Join
```typescript copy
const result = await db.select().from(users).rightJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" right join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join
```typescript copy
const result = await db.select().from(users).innerJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" inner join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).innerJoinLateral(subquery, sql`true`)
```
```sql
select ... from "users" inner join lateral (select ... from "pets" where "users"."age" >= 16) "userPets" on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Full Join
```typescript copy
const result = await db.select().from(users).fullJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" full join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Cross Join
```typescript copy
const result = await db.select().from(users).crossJoin(pets)
```
```sql
select ... from "users" cross join "pets"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Cross Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).crossJoinLateral(subquery)
```
```sql
select ... from "users" cross join lateral (select ... from "pets" where "users"."age" >= 16) "userPets"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
## Partial select
If you need to select a particular subset of fields or to have a flat response type, Drizzle ORM
supports joins with partial select and will automatically infer return type based on `.select({ ... })` structure.
```typescript copy
await db.select({
userId: users.id,
petId: pets.id,
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select "users"."id", "pets"."id" from "users" left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
}[];
```
You might've noticed that `petId` can be null now, it's because we're left joining and there can be users without a pet.
It's very important to keep in mind when using `sql` operator for partial selection fields and aggregations when needed,
you should to use `sql` for proper result type inference, that one is on you!
```typescript copy
const result = await db.select({
userId: users.id,
petId: pets.id,
petName1: sql`upper(${pets.name})`,
petName2: sql`upper(${pets.name})`,
//Ëwe should explicitly tell 'string | null' in type, since we're left joining that field
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select "users"."id", "pets"."id", upper("pets"."name")...
from "users"
left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
petName1: unknown;
petName2: string | null;
}[];
```
To avoid plethora of nullable fields when joining tables with lots of columns we can utilise our **nested select object syntax**,
our smart type inference will make whole object nullable instead of making all table fields nullable!
```typescript copy
await db.select({
userId: users.id,
userName: users.name,
pet: {
id: pets.id,
name: pets.name,
upperName: sql`upper(${pets.name})`
}
}).from(users).fullJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" full join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number | null;
userName: string | null;
pet: {
id: number;
name: string;
upperName: string;
} | null;
}[];
```
## Aliases & Selfjoins
Drizzle ORM supports table aliases which comes really handy when you need to do selfjoins.
Lets say you need to fetch users with their parents:
```typescript copy
import { user } from "./schema";
import { alias } from "drizzle-orm/cockroach-core";
const parent = alias(user, "parent");
const result = await db
.select()
.from(user)
.leftJoin(parent, eq(parent.id, user.parentId));
```
```sql
select ... from "user" left join "user" "parent" on "parent"."id" = "user"."parent_id"
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
parentId: number;
};
parent: {
id: number;
name: string;
parentId: number;
} | null;
}[];
```
```typescript
export const user = cockroachTable("user", {
id: int4().primaryKey(),
name: string().notNull(),
parentId: int4("parent_id").notNull().references((): AnyCockroachColumn => user.id)
});
```
## Aggregating results
Drizzle ORM delivers name-mapped results from the driver without changing the structure.
You're free to operate with results the way you want, here's an example of mapping many-one relational data:
```typescript
type User = typeof users.$inferSelect;
type Pet = typeof pets.$inferSelect;
const rows = await db
.select({
user: users,
pet: pets,
})
.from(users)
.leftJoin(pets, eq(users.id, pets.ownerId));
const result = rows.reduce>(
(acc, row) => {
const user = row.user;
const pet = row.pet;
if (typeof acc[user.id] === "undefined") {
acc[user.id] = { user, pets: [] };
}
if (pet) {
acc[user.id]!.pets.push(pet);
}
return acc;
},
{}
);
// result type
const result: Record;
```
## Many-to-one example
```typescript
import { cockroachTable, string, int4 } from "drizzle-orm/cockroach-core";
import { drizzle } from "drizzle-orm/cockroach";
const cities = cockroachTable("cities", {
id: int4().primaryKey(),
name: string(),
});
const users = cockroachTable("users", {
id: int4().primaryKey(),
name: string(),
cityId: int4("city_id").references(() => cities.id),
});
const db = drizzle(process.env.DATABASE_URL);
const result = await db.select().from(cities).leftJoin(users, eq(cities.id, users.cityId));
```
## Many-to-many example
```typescript
const users = cockroachTable("users", {
id: int4().primaryKey(),
name: string(),
});
const chatGroups = cockroachTable("chat_groups", {
id: int4().primaryKey(),
name: string(),
});
const usersToChatGroups = cockroachTable("users_to_chat_groups", {
userId: int4("user_id")
.notNull()
.references(() => users.id),
groupId: int4("group_id")
.notNull()
.references(() => chatGroups.id),
});
// querying user group with id 1 and all the participants(users)
await db.select()
.from(usersToChatGroups)
.leftJoin(users, eq(usersToChatGroups.userId, users.id))
.leftJoin(chatGroups, eq(usersToChatGroups.groupId, chatGroups.id))
.where(eq(chatGroups.id, 1));
```
Source: https://orm.drizzle.team/docs/cockroach/kit-custom-migrations
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
- Drizzle Kit [overview](/docs/cockroach/kit-overview) and [config file](/docs/cockroach/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/cockroach/drizzle-kit-generate)
- `drizzle-kit migrate` command - [read here](/docs/cockroach/drizzle-kit-migrate)
Drizzle lets you generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding, which you can then run with [`drizzle-kit migrate`](/docs/cockroach/drizzle-kit-migrate) command.
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init_sql
â â đ 20242409135510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409135510_seed-users.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
### Running JavaScript and TypeScript migrations
We will add ability to run custom JavaScript and TypeScript migration/seeding scripts in the upcoming release, you can follow [github discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2832).
Source: https://orm.drizzle.team/docs/cockroach/kit-overview
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/cockroach/get-started)
- Drizzle schema fundamentals - [read here](/docs/cockroach/sql-schema-declaration)
- Database connection basics - [read here](/docs/cockroach/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/cockroach/migrations)
**Drizzle Kit** is a CLI tool for managing SQL database migrations with Drizzle.
-D drizzle-kit
Make sure to first go through Drizzle [get started](/docs/cockroach/get-started) and [migration fundamentals](/docs/cockroach/migrations) and pick SQL migration flow that suits your business needs best.
Based on your schema, Drizzle Kit let's you generate and run SQL migration files,
push schema directly to the database, pull schema from database, spin up drizzle studio and has a couple of utility commands.
drizzle-kit generate
drizzle-kit migrate
drizzle-kit push
drizzle-kit pull
drizzle-kit check
drizzle-kit up
drizzle-kit studio
drizzle-kit export
| | |
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`drizzle-kit generate`](/docs/cockroach/drizzle-kit-generate) | lets you generate SQL migration files based on your Drizzle schema either upon declaration or on subsequent changes, [see here](/docs/cockroach/drizzle-kit-generate). |
| [`drizzle-kit migrate`](/docs/cockroach/drizzle-kit-migrate) | lets you apply generated SQL migration files to your database, [see here](/docs/cockroach/drizzle-kit-migrate). |
| [`drizzle-kit pull`](/docs/cockroach/drizzle-kit-pull) | lets you pull(introspect) database schema, convert it to Drizzle schema and save it to your codebase, [see here](/docs/cockroach/drizzle-kit-pull) |
| [`drizzle-kit push`](/docs/cockroach/drizzle-kit-push) | lets you push your Drizzle schema to database either upon declaration or on subsequent schema changes, [see here](/docs/cockroach/drizzle-kit-push) |
| [`drizzle-kit studio`](/docs/cockroach/drizzle-kit-studio) | will connect to your database and spin up proxy server for Drizzle Studio which you can use for convenient database browsing, [see here](/docs/cockroach/drizzle-kit-studio) |
| [`drizzle-kit check`](/docs/cockroach/drizzle-kit-check) | will walk through all generate migrations and check for any race conditions(collisions) of generated migrations, [see here](/docs/cockroach/drizzle-kit-check) |
| [`drizzle-kit up`](/docs/cockroach/drizzle-kit-up) | used to upgrade snapshots of previously generated migrations, [see here](/docs/cockroach/drizzle-kit-up) |
| [`drizzle-kit export`](/docs/cockroach/drizzle-kit-export) | used to convert a TypeScript schema into raw SQL DDL and print it out [see here](/docs/cockroach/drizzle-kit-export) |
Drizzle Kit is configured through [drizzle.config.ts](/docs/cockroach/drizzle-config-file) configuration file or via CLI params.
It's required to at least provide SQL `dialect` and `schema` path for Drizzle Kit to know how to generate migrations.
```
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle.config.ts <--- Drizzle config file
â đ package.json
â đ tsconfig.json
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "cockroach",
schema: "./src/schema.ts",
});
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
dialect: "cockroach",
schema: "./src/schema.ts",
entities: {
roles: {
exclude: ["admin"],
include: ["user"],
provider: "supabase",
},
},
dbCredentials: {
url: "./database/",
},
schemaFilter: "public",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "public",
},
breakpoints: true,
verbose: true,
});
```
You can provide Drizzle Kit config path via CLI param, it's very useful when you have multiple database stages or multiple databases or different databases on the same project:
drizzle-kit push --config=drizzle-dev.drizzle.config
drizzle-kit push --config=drizzle-prod.drizzle.config
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
Source: https://orm.drizzle.team/docs/cockroach/migrations
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
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 Npm from '@mdx/Npm.astro';
import Tag from '@mdx/Tag.astro'
# Drizzle migrations fundamentals
SQL databases require you to specify a **strict schema** of entities you're going to store upfront
and if (when) you need to change the shape of those entities - you will need to do it via **schema migrations**.
There're multiple production grade ways of managing database migrations.
Drizzle is designed to perfectly suits all of them, regardless of you going **database first** or **codebase first**.
**Database first** is when your database schema is a source of truth. You manage your database schema either directly on the database or
via database migration tools and then you pull your database schema to your codebase application level entities.
**Codebase first** is when database schema in your codebase is a source of truth and is under version control. You declare and manage your database schema in JavaScript/TypeScript
and then you apply that schema to the database itself either with Drizzle, directly or via external migration tools.
#### How can Drizzle help?
We've built [**drizzle-kit**](/docs/cockroach/kit-overview) - CLI app for managing migrations with Drizzle.
```shell
drizzle-kit migrate
drizzle-kit generate
drizzle-kit push
drizzle-kit pull
drizzle-kit export
```
It is designed to let you choose how to approach migrations based on your current business demands.
It fits in both database and codebase first approaches, it lets you **push your schema** or **generate SQL migration** files or **pull the schema** from database.
It is perfect wether you work alone or in a team.
**Now let's pick the best option for your project:**
**Option 1**
> I manage database schema myself using external migration tools or by running SQL migrations directly on my database.
> From Drizzle I just need to get current state of the schema from my database and save it as TypeScript schema file.
That's a **database first** approach. You have your database schema as a **source of truth** and
Drizzle lets you pull database schema to TypeScript using [`drizzle-kit pull`](/docs/cockroach/drizzle-kit-pull) command.
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââ
â â <--- CREATE TABLE "users" (
ââââââââââââââââââââââââââââ â â "id" INT4 PRIMARY KEY,
â ~ drizzle-kit pull â â â "name" STRING,
âââŦâââââââââââââââââââââââââ â DATABASE â "email" STRING UNIQUE
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string(),
}, (table) => [
uniqueIndex("users_email_key").using("btree", table.email.asc()),
]);
```
**Option 2**
> I want to have database schema in my TypeScript codebase,
> I don't wanna deal with SQL migration files.
> I want Drizzle to "push" my schema directly to the database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a **source of truth** and
Drizzle lets you push schema changes to the database using [`drizzle-kit push`](/docs/cockroach/drizzle-kit-push) command.
That's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
```typescript {6} filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(), // <--- added column
});
```
```
Add column to `users` table
ââââââââââââââââââââââââââââââ
â + email: string().unique() â
âââŦâââââââââââââââââââââââââââ
â
v
ââââââââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦâââââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´âââââââââââââââââââââââ
ALTER TABLE "users" ADD COLUMN "email" string;
CREATE UNIQUE INDEX "users_email_key" ON "users" ("email");
```
**Option 3**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and apply them to my database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/cockroach/drizzle-kit-generate)
and then apply them to the database with [`drizzle-kit migrate`](/docs/cockroach/drizzle-kit-migrate) commands.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string,
"email" string,
CONSTRAINT "users_email_key" UNIQUE("email")
);
```
```
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. read migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 4**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and I want Drizzle to apply them during runtime
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/cockroach/drizzle-kit-generate) and then
you can apply them to the database during runtime of your application.
This approach is widely used for **monolithic** applications when you apply database migrations
during zero downtime deployment and rollback DDL changes if something fails.
This is also used in **serverless** deployments with migrations running in **custom resource** once during deployment process.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string,
"email" string,
CONSTRAINT "users_email_key" UNIQUE("email")
);
```
```ts
// index.ts
import { drizzle } from "drizzle-orm/cockroach"
import { migrate } from 'drizzle-orm/cockroach/migrator';
const db = drizzle(process.env.DATABASE_URL);
await migrate(db);
```
```
âââââââââââââââââââââââââ
â npx tsx src/index.ts â
âââŦââââââââââââââââââââââ
â
â 1. init database connection ââââââââââââââââââââââââââââ
â 2. read migration.sql files in migrations folder â â
3. fetch migration history from database -------------> â â
â 4. pick previously unapplied migrations <-------------- â DATABASE â
â 5. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 5**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me,
> but I will apply them to my database myself or via external migration tools
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/cockroach/drizzle-kit-generate) and then
you can apply them to the database either directly or via external migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous scheama
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string,
"email" string,
CONSTRAINT "users_email_key" UNIQUE("email")
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
directly to the database
â ââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââŦâââ>â â
â â â Database â
or via external tools â â â
â â ââââââââââââââââââââââ
â ââââââââââââââââââââââ â
ââââ Bytebase ââââââââââââââ
ââââââââââââââââââââââ¤
â Liquibase â
ââââââââââââââââââââââ¤
â Atlas â
ââââââââââââââââââââââ¤
â etcâĻ â
ââââââââââââââââââââââ
[â] done!
```
**Option 6**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to output the SQL representation of my Drizzle schema to the console,
> and I will apply them to my database via [Atlas](https://atlasgo.io/guides/orms/drizzle)
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you export SQL statements based on your schema changes with [`drizzle-kit export`](/docs/cockroach/drizzle-kit-generate) and then
you can apply them to the database via [Atlas](https://atlasgo.io/guides/orms/drizzle) or other external SQL migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/cockroach-core";
export const users = p.cockroachTable("users", {
id: p.int4().primaryKey(),
name: p.string(),
email: p.string().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit export â
âââŦâââââââââââââââââââââââ
â
â 1. read your drizzle schema
2. generated SQL representation of your schema
â 3. outputs to console
â
â
v
```
```sql
CREATE TABLE "users" (
"id" int4 PRIMARY KEY,
"name" string,
"email" string,
CONSTRAINT "users_email_key" UNIQUE("email")
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
via Atlas
â ââââââââââââââââ
â ââââââââââââââââââââââ â â
ââââ Atlas ââââââââââââ>â Database â
ââââââââââââââââââââââ â â
ââââââââââââââââ
[â] done!
```
Source: https://orm.drizzle.team/docs/cockroach/operators
import Section from '@mdx/Section.astro';
import Callout from "@mdx/Callout.astro";
# Filter and conditional operators
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select "id", "name", "age" from "users" where "users"."id" = $1; -- params: [42]
```
We natively support all dialect specific filter and conditional operators.
You can import all filter & conditional from `drizzle-orm`:
```typescript copy
import { eq, ne, gt, gte, ... } from "drizzle-orm";
```
### eq
Value equal to `n`
```typescript copy
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column, 5));
```
```sql copy
SELECT * FROM "table" WHERE "table"."column" = 5
```
```typescript
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" = "table"."column2"
```
### ne
Value is not equal to `n`
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" <> 5
```
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" <> "table"."column2"
```
## ---
### gt
Value is greater than `n`
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" > 5
```
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" > "table"."column2"
```
### gte
Value is greater than or equal to `n`
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" >= 5
```
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" >= "table"."column2"
```
### lt
Value is less than `n`
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" < 5
```
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" < "table"."column2"
```
### lte
Value is less than or equal to `n`.
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" <= 5
```
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" <= "table"."column2"
```
## ---
### exists
Value exists
```typescript
import { exists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(exists(query));
```
```sql
SELECT * FROM "table" WHERE EXISTS (SELECT * FROM "table2")
```
### notExists
```typescript
import { notExists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(notExists(query));
```
```sql
SELECT * FROM "table" WHERE NOT EXISTS (SELECT * FROM "table2")
```
### isNull
Value is `null`
```typescript
import { isNull } from "drizzle-orm";
db.select().from(table).where(isNull(table.column));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" IS NULL)
```
### isNotNull
Value is not `null`
```typescript
import { isNotNull } from "drizzle-orm";
db.select().from(table).where(isNotNull(table.column));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" IS NOT NULL)
```
## ---
### inArray
Value is in array of values
```typescript
import { inArray } from "drizzle-orm";
db.select().from(table).where(inArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM "table" WHERE "table"."column" IN (1, 2, 3, 4)
```
```typescript
import { inArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(inArray(table.column, query));
```
```sql
SELECT * FROM "table" WHERE "table"."column" IN (SELECT "table2"."column" FROM "table2")
```
### notInArray
Value is not in array of values
```typescript
import { notInArray } from "drizzle-orm";
db.select().from(table).where(notInArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT IN (1, 2, 3, 4)
```
```typescript
import { notInArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(notInArray(table.column, query));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT IN (SELECT "table2"."column" FROM "table2")
```
## ---
### between
Value is between two values
```typescript
import { between } from "drizzle-orm";
db.select().from(table).where(between(table.column, 2, 7));
```
```sql
SELECT * FROM "table" WHERE "table"."column" BETWEEN 2 AND 7
```
### notBetween
Value is not between two value
```typescript
import { notBetween } from "drizzle-orm";
db.select().from(table).where(notBetween(table.column, 2, 7));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT BETWEEN 2 AND 7
```
## ---
### like
Value is like other value, case sensitive
```typescript
import { like } from "drizzle-orm";
db.select().from(table).where(like(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" LIKE '%llo wor%'
```
### notLike
Value is not like other value, case sensitive
```typescript
import { notLike } from "drizzle-orm";
db.select().from(table).where(notLike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT LIKE '%llo wor%'
```
### ilike
Value is like some other value, case insensitive
```typescript
import { ilike } from "drizzle-orm";
db.select().from(table).where(ilike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" ILIKE '%llo wor%'
```
### notIlike
Value is not like some other value, case insensitive
```typescript
import { notIlike } from "drizzle-orm";
db.select().from(table).where(notIlike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT ILIKE '%llo wor%'
```
## ---
### not
All conditions must return `false`.
```typescript
import { eq, not } from "drizzle-orm";
db.select().from(table).where(not(eq(table.column, 5)));
```
```sql
SELECT * FROM "table" WHERE NOT ("table"."column" = 5)
```
### and
All conditions must return `true`.
```typescript
import { gt, lt, and } from "drizzle-orm";
db.select().from(table).where(and(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" > 5 AND "table"."column" < 7)
```
### or
One or more conditions must return `true`.
```typescript
import { gt, lt, or } from "drizzle-orm";
db.select().from(table).where(or(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" > 5 OR "table"."column" < 7)
```
## ---
### arrayContains
Test that a column or expression contains all elements of the list passed as the second argument
```typescript
import { arrayContains } from "drizzle-orm";
const contains = await db.select({ id: posts.id }).from(posts)
.where(arrayContains(posts.tags, ['Typescript', 'ORM']));
const withSubQuery = await db.select({ id: posts.id }).from(posts)
.where(arrayContains(
posts.tags,
db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)),
));
```
```sql
select "id" from "posts" where "posts"."tags" @> {Typescript,ORM};
select "id" from "posts" where "posts"."tags" @> (select "tags" from "posts" where "posts"."id" = 1);
```
### arrayContained
Test that the list passed as the second argument contains all elements of a column or expression
```typescript
import { arrayContained } from "drizzle-orm";
const contained = await db.select({ id: posts.id }).from(posts)
.where(arrayContained(posts.tags, ['Typescript', 'ORM']));
```
```sql
select "id" from "posts" where "posts"."tags" <@ {Typescript,ORM};
```
### arrayOverlaps
Test that a column or expression contains any elements of the list passed as the second argument.
```typescript
import { arrayOverlaps } from "drizzle-orm";
const overlaps = await db.select({ id: posts.id }).from(posts)
.where(arrayOverlaps(posts.tags, ['Typescript', 'ORM']));
```
```sql
select "id" from "posts" where "posts"."tags" && {Typescript,ORM}
```
Source: https://orm.drizzle.team/docs/cockroach/overview
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro'
# Drizzle ORM
Drizzle ORM is a headless TypeScript ORM with a head. đ˛
> Drizzle is a good friend who's there for you when necessary and doesn't bother when you need some space.
It looks and feels simple, performs on day _1000_ of your project,\
lets you do things your way, and is there when you need it.
**It's the only ORM with both [relational](/docs/cockroach/rqb) and [SQL-like](/docs/cockroach/select) query APIs**,
providing you the best of both worlds when it comes to accessing your relational data.
Drizzle is lightweight, performant, typesafe, non-lactose, gluten-free, sober, flexible and **serverless-ready by design**.
Drizzle is not just a library, it's an experience. đ¤Š
[](https://bestofjs.org/projects/drizzle-orm)
## Headless ORM?
First and foremost, Drizzle is a library and a collection of complementary opt-in tools.
**ORM** stands for _object relational mapping_, and developers tend to call Django-like or Spring-like tools an ORM.
We truly believe it's a misconception based on legacy nomenclature, and we call them **data frameworks**.
With data frameworks you have to build projects **around them** and not **with them**.
**Drizzle** lets you build your project the way you want, without interfering with your project or structure.
Using Drizzle you can define and manage database schemas in TypeScript, access your data in a SQL-like
or relational way, and take advantage of opt-in tools
to push your developer experience _through the roof_. đ¤¯
## Why SQL-like?
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate/abstract you away from SQL, which
leads to a double learning curve: needing to know 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 can have zero to no
learning curve and access to the full power of SQL.
We bring all the familiar **[SQL schema](/docs/cockroach/sql-schema-declaration)**, **[queries](/docs/cockroach/select)**,
**[automatic migrations](/docs/cockroach/migrations)** and **[one more thing](/docs/cockroach/rqb)**. â¨
```typescript copy
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
```
```typescript copy
// manage your schema
export const countries = cockroachTable('countries', {
id: int4().primaryKey(),
name: varchar({ length: 256 }),
});
export const cities = cockroachTable('cities', {
id: int4().primaryKey(),
name: varchar({ length: 256 }),
countryId: int4('country_id').references(() => countries.id),
});
```
```sql
-- generate migrations
CREATE TABLE "countries" (
"id" int4 PRIMARY KEY,
"name" varchar(256)
);
CREATE TABLE "cities" (
"id" int4 PRIMARY KEY,
"name" varchar(256),
"country_id" int4
);
ALTER TABLE "cities" ADD CONSTRAINT "cities_country_id_countries_id_fkey" FOREIGN KEY ("country_id") REFERENCES "countries"("id");
```
## Why not SQL-like?
We're always striving for a perfectly balanced solution, and while SQL-like does cover 100% of the needs,
there are certain common scenarios where you can query data in a better way.
We've built the **[Queries API](/docs/cockroach/rqb)** for you, so you can fetch relational nested data from the database
in the most convenient and performant way, and never think about joins and data mapping.
**Drizzle always outputs exactly 1 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
},
});
```
## Serverless?
The best part is no part. **Drizzle has exactly 0 dependencies!**

Drizzle ORM is dialect-specific, slim, performant and serverless-ready **by design**.
We've spent a lot of time to make sure you have best-in-class SQL dialect support, including Postgres, MySQL, and others.
Drizzle operates natively through industry-standard database drivers. We support all major **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[SingleStore](/docs/singlestore/get-started-singlestore)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** drivers out there, and we're adding new ones **[really fast](https://twitter.com/DrizzleORM/status/1653082492742647811?s=20)**.
## Welcome on board!
More and more companies are adopting Drizzle in production, experiencing immense benefits in both DX and performance.
**We're always there to help, so don't hesitate to reach out. We'll gladly assist you in your Drizzle journey!**
We have an outstanding **[Discord community](https://driz.link/discord)** and welcome all builders to our **[Twitter](https://twitter.com/drizzleorm)**.
Now go build something awesome with Drizzle and your **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** database. đ
### Video Showcase
{/* tRPC + NextJS App Router = Simple Typesafe APIs
Jack Herrington 19:17
https://www.youtube.com/watch?v=qCLV0Iaq9zU */}
{/* https://www.youtube.com/watch?v=qDunJ0wVIec */}
{/* https://www.youtube.com/watch?v=NZpPMlSAez0 */}
{/* https://www.youtube.com/watch?v=-A0kMiJqQRY */}
Source: https://orm.drizzle.team/docs/cockroach/perf-queries
# Query performance
When it comes to **Drizzle** â we're a thin TypeScript layer on top of SQL with
almost 0 overhead and to make it actual 0, you can utilise our prepared statements API.
**When you run a query on the database, there are several things that happen:**
- all the configurations of the query builder got concatenated to the SQL string
- that string and params are sent to the database driver
- driver compiles SQL query to the binary SQL executable format and sends it to the database
With prepared statements you do SQL concatenation once on the Drizzle ORM side and then database
driver is able to reuse precompiled binary SQL instead of parsing query all the time.
It has extreme performance benefits on large SQL queries.
## Prepared statement
```typescript copy {3}
const db = drizzle(...);
const prepared = db.select().from(customers).prepare("statement_name");
const res1 = await prepared.execute();
const res2 = await prepared.execute();
const res3 = await prepared.execute();
```
## Placeholder
Whenever you need to embed a dynamic runtime value - you can use the `sql.placeholder(...)` api
```ts {6,9-10,15,18}
import { sql } from "drizzle-orm";
const p1 = db
.select()
.from(customers)
.where(eq(customers.id, sql.placeholder('id')))
.prepare("p1")
await p1.execute({ id: 10 }) // SELECT * FROM customers WHERE id = 10
await p1.execute({ id: 12 }) // SELECT * FROM customers WHERE id = 12
const p2 = db
.select()
.from(customers)
.where(sql`lower(${customers.name}) like ${sql.placeholder('name')}`)
.prepare("p2");
await p2.execute({ name: '%an%' }) // SELECT * FROM customers WHERE lower(name) like '%an%'
```
Source: https://orm.drizzle.team/docs/cockroach/query-utils
import $count from '@mdx/$count-cockroach.mdx';
# Drizzle query utils
### $count
<$count/>
Source: https://orm.drizzle.team/docs/cockroach/read-replicas
# Read Replicas
When your project involves a set of read replica instances, and you require a convenient method for managing
SELECT queries from read replicas, as well as performing create, delete, and update operations on the primary
instance, you can leverage the `withReplicas()` function within Drizzle
```ts copy
import { sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/cockroach';
import { boolean, jsonb, cockroachTable, int4, string, timestamp, withReplicas } from 'drizzle-orm/cockroach-core';
const usersTable = cockroachTable("users", {
id: int4().primaryKey(),
name: string().notNull(),
verified: boolean().notNull().default(false),
jsonb: jsonb().$type(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
const primaryDb = drizzle("postgres://user:password@host:port/primary_db");
const read1 = drizzle("postgres://user:password@host:port/read_replica_1");
const read2 = drizzle("postgres://user:password@host:port/read_replica_2");
const db = withReplicas(primaryDb, [read1, read2]);
```
You can now use the `db` instance the same way you did before. Drizzle will
handle the choice between read replica and the primary instance automatically
```ts
// Read from either the read1 connection or the read2 connection
await db.select().from(usersTable)
// Use the primary database for the delete operation
await db.delete(usersTable).where(eq(usersTable.id, 1))
```
You can use the `$primary` key to force using primary instances even for read operations
```ts
// read from primary
await db.$primary.select().from(usersTable);
```
With Drizzle, you can also specify custom logic for choosing read replicas.
You can make a weighted decision or any other custom selection method for random read replica choice.
Here is an implementation example of custom logic for selecting read replicas,
where the first replica has a 70% chance of being chosen, and the second replica has a 30%
chance of being selected.
Keep in mind that you can implement any type of random selection method for read replicas
```ts
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!
});
await db.select().from(usersTable)
```
Source: https://orm.drizzle.team/docs/cockroach/relations-schema-declaration
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle Relations Fundamentals
In the world of databases, especially relational databases, the concept of relations is absolutely fundamental.
Think of "relations" as the connections and links between different pieces of data. Just like in real life,
where people have relationships with each other, or objects are related to categories, databases use relations to model how different
types of information are connected and work together.
### Normalization
Normalization is the process of organizing data in your database to reduce redundancy (duplication) and improve data integrity
(accuracy and consistency). Think of it like tidying up a messy filing cabinet. Instead of having all sorts of papers
crammed into one folder, you organize them into logical folders and categories to make everything easier to find and manage.
- **Reduces Data Redundancy**: Imagine storing a customer's address every time they place an order. If the address changes, you'd have to update it in multiple places! Normalization helps you store information in one place and refer to it from other places, minimizing repetition.
- **Improves Data Integrity**: Less redundancy means less chance of inconsistencies. If you update an address in one place, it's updated everywhere it's needed.
- **Prevents Anomalies**: Normalization helps prevent issues like:
1. **Insertion Anomalies**: Difficulty adding new data because you're missing related information.
2. **Update Anomalies**: Having to update the same information in multiple rows.
3. **Deletion Anomalies**: Accidentally losing valuable information when you delete something seemingly unrelated.
- **Easier to Understand and Maintain**: A normalized database is generally more logically structured and easier to understand, query, and modify.
Normalization is often described in terms of "normal forms" (1NF, 2NF, 3NF, and beyond). While the details can get quite technical, the core ideas are straightforward:
#### 1NF (First Normal Form): `Atomic Values`
**Goal**: Each column should hold a single, indivisible value. No repeating groups of data within a single cell
**Example**: Instead of having a single `address` column that stores `123 Main St, City, USA`, you'd
break it down into separate columns: `street_address`, `city`, `state`, `zip_code`.
```sql
-- Unnormalized (violates 1NF)
CREATE TABLE "Customers_Unnormalized" (
"customer_id" INT4 PRIMARY KEY,
"name" VARCHAR(255),
"address" VARCHAR(255) -- Problem: Multiple pieces of info in one column
);
-- Normalized to 1NF
CREATE TABLE "Customers_1NF" (
"customer_id" INT4 PRIMARY KEY,
"name" VARCHAR(255),
"street_address" VARCHAR(255),
"city" VARCHAR(255),
"state" VARCHAR(255),
"zip_code" VARCHAR(10)
);
```
#### 2NF (Second Normal Form): `Eliminate Redundant Data Dependent on Part of the Key`
**Goal**: Applies when you have a table with a composite primary key (a primary key made up of two or more columns).
2NF ensures that all non-key attributes are fully dependent on the entire composite primary key, not just part of it.
Imagine we have a table called `order_items`. This table tracks items within orders, and we use a composite primary key (`order_id`, `product_id`)
because a single order can have multiple of the same product (though in this simplified example, let's assume each product appears only
once per order for clarity, but the composite key logic still applies).
```sql
CREATE TABLE "OrderItems_Unnormalized" (
"order_id" INT4,
"product_id" VARCHAR(10),
"product_name" VARCHAR(100),
"product_price" DECIMAL(10, 2),
"quantity" INT4,
"order_date" DATE,
PRIMARY KEY ("order_id", "product_id") -- Composite Primary Key
);
INSERT INTO "OrderItems_Unnormalized" ("order_id", "product_id", "product_name", "product_price", "quantity", "order_date") VALUES
(101, 'A123', 'Laptop', 1200.00, 1, '2023-10-27'),
(101, 'B456', 'Mouse', 25.00, 2, '2023-10-27'),
(102, 'A123', 'Laptop', 1200.00, 1, '2023-10-28'),
(103, 'C789', 'Keyboard', 75.00, 1, '2023-10-29');
```
```
+------------------------------------------------------------------------------------+
| OrderItems_Unnormalized |
+------------------------------------------------------------------------------------+
| PK (order_id, product_id) | product_name | product_price | quantity | order_date |
+------------------------------------------------------------------------------------+
| 101, A123 | Laptop | 1200.00 | 1 | 2023-10-27 |
| 101, B456 | Mouse | 25.00 | 2 | 2023-10-27 |
| 102, A123 | Laptop | 1200.00 | 1 | 2023-10-28 |
| 103, C789 | Keyboard | 75.00 | 1 | 2023-10-29 |
+------------------------------------------------------------------------------------+
```
**Problem**: Notice that `product_name` and `product_price` are repeated whenever the same `product_id` appears in different orders.
These attributes are only dependent on `product_id`, which is part of the composite primary key (`order_id`, `product_id`), but not the entire key.
This is a partial dependency.
To achieve 2NF, we need to remove the partially dependent attributes (`product_name`, `product_price`) and place them in a separate table where they are
fully dependent on the primary key of that new table.
```
+-------------------+ 1:M +---------------------------+
| Products | <---------- | OrderItems_2NF |
+-------------------+ +---------------------------+
| PK product_id | | PK (order_id, product_id) |
| product_name | | quantity |
| product_price | | order_date |
+-------------------+ | FK product_id |
+---------------------------+
```
```sql
CREATE TABLE "Products" (
"product_id" VARCHAR(10) PRIMARY KEY,
"product_name" VARCHAR(100),
"product_price" DECIMAL(10, 2)
);
CREATE TABLE "OrderItems_2NF" (
"order_id" INT4,
"product_id" VARCHAR(10),
"quantity" INT4,
"order_date" DATE,
PRIMARY KEY ("order_id", "product_id"), -- Composite Primary Key remains
FOREIGN KEY ("product_id") REFERENCES "Products"("product_id") -- Foreign Key to Products
);
-- Insert data into Products
INSERT INTO "Products" ("product_id", "product_name", "product_price") VALUES
('A123', 'Laptop', 1200.00),
('B456', 'Mouse', 25.00),
('C789', 'Keyboard', 75.00);
-- Insert data into OrderItems_2NF (referencing Products)
INSERT INTO "OrderItems_2NF" ("order_id", "product_id", "quantity", "order_date") VALUES
(101, 'A123', 1, '2023-10-27'),
(101, 'B456', 2, '2023-10-27'),
(102, 'A123', 1, '2023-10-28'),
(103, 'C789', 1, '2023-10-29');
```
#### 3NF (Third Normal Form): `Eliminate Redundant Data Dependent on Non-Key Attributes`
**Goal**: Remove data that is dependent on other non-key attributes. This is about eliminating transitive dependencies.
**Problem**: Let's say we have a `suppliers` table. We store supplier information, including their `zip_code`, `city`, and `state`. `supplier_id` is the primary key.
```sql
CREATE TABLE "suppliers" (
"supplier_id" VARCHAR(10) PRIMARY KEY,
"supplier_name" VARCHAR(255),
"zip_code" VARCHAR(10),
"city" VARCHAR(100),
"state" VARCHAR(50)
);
INSERT INTO "suppliers" ("supplier_id", "supplier_name", "zip_code", "city", "state") VALUES
('S1', 'Acme Corp', '12345', 'Anytown', 'NY'),
('S2', 'Beta Inc', '67890', 'Otherville', 'CA'),
('S3', 'Gamma Ltd', '12345', 'Anytown', 'NY');
```
```
+---------------------------------------------------------------+
| suppliers |
+---------------------------------------------------------------+
| PK supplier_id | supplier_name | zip_code | city | state |
+---------------------------------------------------------------+
| S1 | Acme Corp | 12345 | Anytown | NY |
| S2 | Beta Inc | 67890 | Otherville | CA |
| S3 | Gamma Ltd | 12345 | Anytown | NY |
+---------------------------------------------------------------+
```
**Solution**: To achieve 3NF, we remove the attributes dependent on the non-key attribute (`city`, `state` dependent on `zip_code`) and put
them into a separate table keyed by the non-key attribute itself (`zip_code`).
```
+-------------------+ 1:M +--------------------+
| zip_codes | <---------- | suppliers |
+-------------------+ +--------------------+
| PK zip_code | | PK supplier_id |
| city | | supplier_name |
| state | | FK zip_code |
+-------------------+ +--------------------+
```
```sql
CREATE TABLE "zip_codes" (
"zip_code" VARCHAR(10) PRIMARY KEY,
"city" VARCHAR(100),
"state" VARCHAR(50)
);
CREATE TABLE "suppliers" (
"supplier_id" VARCHAR(10) PRIMARY KEY,
"supplier_name" VARCHAR(255),
"zip_code" VARCHAR(10), -- Foreign Key to zip_codes
FOREIGN KEY ("zip_code") REFERENCES "zip_codes"("zip_code")
);
-- Insert data into zip_codes
INSERT INTO "zip_codes" ("zip_code", "city", "state") VALUES
('12345', 'Anytown', 'NY'),
('67890', 'Otherville', 'CA');
-- Insert data into suppliers (referencing zip_codes)
INSERT INTO "suppliers" ("supplier_id", "supplier_name", "zip_code") VALUES
('S1', 'Acme Corp', '12345'),
('S2', 'Beta Inc', '67890'),
('S3', 'Gamma Ltd', '12345');
```
There are additional normal forms, such as `4NF`, `5NF`, `6NF`, `EKNF`, `ETNF`, and `DKNF`. We won't cover these here, but we will create a
dedicated set of tutorials for them in our guides and tutorials section.
### Database Relationships
#### One-to-One
In a one-to-one relationship, each record in `table A` is related to at most one record in `table B`, and each record in `table B` is
related to at most one record in `table A`. It's a very direct, exclusive pairing.
1. **User Profiles and User Account Details**: Think of a website. Each user account (in a Users table) might have exactly one user profile (in a UserProfiles table) containing more detailed information.
2. **Employees and Parking Spaces**: An Employees table and a ParkingSpaces table. Each employee might be assigned at most one parking space, and each parking space is assigned to at most one employee.
3. **Splitting Tables for Organization**: Sometimes, you might split a very wide table into two for better organization or security reasons, maintaining a 1-1 relationship between them.
```
Table A (One Side) Table B (One Side)
+---------+ +---------+
| PK (A) | <---------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
```
#### One-to-Many
In a one-to-many relationship, one record in `table A` can be related to many records in `table B`, but each
record in `table B` is related to at most one record in `table A`. Think of it as a "parent-child" relationship.
1. **Customers and Orders**: One customer can place many orders, but each order belongs to only one customer.
2. **Authors and Books**: One author can write many books, but (let's simplify for now and say) each book is written by one primary author.
3. **Departments and Employees**: One department can have many employees, but each employee belongs to only one department.
```
Table A (One Side) Table B (Many Side)
+---------+ +---------+
| PK (A) | ----------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
(One) (Many)
```
#### Many-to-Many
In a many-to-many relationship, one record in `table A` can be related to many records in `table B`, and one
record in `table B` can be related to many records in `table A`. It's a more complex, bidirectional relationship.
1. **Students and Courses**: One student can enroll in many courses, and one course can have many students enrolled.
2. **Products and Categories**: One product can belong to multiple categories (e.g., a "T-shirt" can be
in "Clothing" and "Summer Wear" categories), and one category can contain many products.
3. **Authors and Books**: A book can be written by multiple authors, and an author can write multiple books.
```
Table A (Many Side) Junction Table Table B (Many Side)
+---------+ +-------------+ +---------+
| PK (A) | -------->| FK (A) | <----| FK (B) |
| ... | | FK (B) | | ... |
+---------+ +-------------+ +---------+
(Many) (Junction) (Many)
```
Many-to-many relationships are not directly implemented with foreign keys between the two main tables.
Instead, you need a `junction` table (also called an associative table or bridging table).
This table acts as an intermediary to link records from both tables.
```sql
-- Table for Students (Many side)
CREATE TABLE "students" (
"id" INT4 PRIMARY KEY,
"name" VARCHAR(255)
);
-- Table for Courses (Many side)
CREATE TABLE "courses" (
"id" INT4 PRIMARY KEY,
"name" VARCHAR(255),
"credits" INT4
);
-- Junction Table: Enrollments (Connects Students and Courses - M-M relationship)
CREATE TABLE "enrollments" (
"id" INT4 GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- Optional, but good practice for junction tables
"student_id" INT4,
"course_id" INT4,
"enrollment_date" DATE,
-- Composite Foreign Keys (often part of a composite primary key or unique constraint)
FOREIGN KEY ("student_id") REFERENCES "students"("id"),
FOREIGN KEY ("course_id") REFERENCES "courses"("id"),
UNIQUE ("student_id", "course_id") -- Prevent duplicate enrollments for the same student and course
);
```
### Why Foreign Keys?
You might think of foreign key constraints as simply a way to validate data - ensuring
that when you enter a value in a foreign key column, that value actually exists in the primary key
column of another table. And you'd be partially right! This value checking is the mechanism foreign keys use.
But it's crucial to understand that this validation is not the end goal, it's the means
to a much larger purpose. Foreign key constraints are fundamentally about:
We've discussed relationships like `One-to-Many` between Customers and Orders.
A foreign key is the SQL language's way of telling the database:
> Hey database, I want to enforce a 1-M relationship here. Every value in the customer_id column of the
Orders table must correspond to a valid customer_id in the Customers table.
It's not just a suggestion; it's a constraint the database actively enforces.
The database becomes relationship-aware because of the foreign key.
- This is the core of "data integrity" in the context of relationships. Referential integrity means
that relationships between tables remain consistent and valid over time.
- Foreign keys prevent orphaned records. What's an orphaned record? In our Customer-Order example,
an order that exists in the Orders table but doesn't have a corresponding customer in the Customers
table would be an orphan. Foreign keys prevent this from happening (or control what happens
if you try to delete a customer with orders - via CASCADE, SET NULL, etc.).
- Why is preventing orphans important? Orphaned records break the logical structure of your data.
If you have an order without a customer, you lose crucial context. Queries become unreliable, reports
become inaccurate, and your application's logic can break down.
**Example**:
```
Without a foreign key, you could accidentally delete a customer from the Customers
table while their orders still exist in the Orders table. Suddenly, you have orders that point to
a customer that no longer exists! A foreign key constraint prevents this data inconsistency.
```
- Foreign keys are not just about technical enforcement; they are also a crucial part of database design documentation.
- When you see a foreign key in a database schema, it immediately tells you:
`Table 'X' is related to Table 'Y' in this way.` It's a clear visual and structural indicator of relationships.
- This makes databases easier to understand, maintain, and evolve over time. New developers can quickly
grasp how different parts of the database are connected.
In essence, foreign key constraints are not just about checking values; they are about:
1. Defining the rules of your data relationships
2. Actively enforcing those rules at the database level
3. Guaranteeing data integrity and consistency within those relationships
4. Making your database more robust, reliable, and understandable
### Why NOT Foreign Keys?
While highly beneficial, there are some scenarios where you might reconsider or use Foreign Keys with caution.
These are typically edge cases and often involve trade-offs.
- **Scenario**: Extremely high-volume transactional systems (e.g., real-time logging, very high-frequency trading
platforms, massive IoT data ingestion).
- **Explanation**: Every time you insert or update data in a table with a foreign key, the database system
needs to perform checks to ensure referential integrity. In extremely high-write scenarios, these
checks can introduce a small but potentially noticeable performance overhead.
- **Scenario**: Systems where data is distributed across multiple database nodes or clusters (common in sharded databases, cloud environments, and microservices).
- **Explanation**: Cross-node foreign keys can introduce significant complexity and performance overhead. Validating referential integrity requires communication between nodes, leading to increased latency. Distributed transactions needed to maintain consistency are also more complex and can be less performant than local transactions. In such architectures, application-level data integrity checks or eventual consistency models might be considered alternatives.
- **Scenario**: Integrating a relational database with older legacy systems or non-relational data stores (e.g., NoSQL, flat files, external APIs).
- **Explanation**: Legacy systems or non-relational data might not consistently adhere to the referential integrity rules enforced by foreign keys. Imposing foreign keys in such scenarios can lead to data import issues, data inconsistencies, and might necessitate complex data transformation or application-level integrity management instead. You might need to carefully evaluate the data quality and consistency of the external sources and potentially rely on application logic or ETL processes to ensure data integrity instead of strictly enforcing foreign keys at the database level.
You can also check out some great explanations from the PlanetScale team in their [article](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints#why-does-planetscale-not-recommend-constraints)
### Polymorphic Relations
Polymorphic relationships are a more advanced concept that allows a single relationship to point to
different types of entities or tables. It's about creating more flexible and adaptable relationships when
you have different kinds of data that share some commonality.
Imagine you have an `activities` log. An activity could be a `comment` a `like` or a `share`.
Each of these `activity` types has different details. Instead of creating separate tables and
relationships for each activity type and the things they relate to, you might use a polymorphic approach.
- **Comments/Reviews**: A "Comment" might be related to different types of content: articles, products, videos, etc.
Instead of having separate article_id, product_id, video_id columns in a Comments table, you can use a
polymorphic relationship.
```
+---------------------+
| **Comments** |
+---------------------+
| PK comment_id |
| commentable_type | ------> [Polymorphic Relationship]
| commentable_id | -------->
| user_id |
| comment_text |
| ... |
+---------------------+
^
|
+---------------------+ +---------------------+ +---------------------+
| **Articles** | | **Products** | | **Videos** |
+---------------------+ +---------------------+ +---------------------+
| PK article_id | | PK product_id | | PK video_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +---------------------+
```
- **Notifications:** A notification could be related to a user, an order, a system event, etc.
```
+----------------------+
| **Notifications** |
+----------------------+
| PK notification_id |
| notifiable_type | ------> [Polymorphic Relationship]
| notifiable_id | -------->
| user_id |
| message |
| ... |
+----------------------+
^
|
+---------------------+ +---------------------+ +-----------------------+
| **Users** | | **Orders** | | **System Events** |
+---------------------+ +---------------------+ +-----------------------+
| PK user_id | | PK order_id | | PK event_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +-----------------------+
```
Polymorphic relationships are more complex and are often handled at the application level or
using more advanced database features (depending on the specific database system). Standard SQL doesn't
have direct, built-in support for enforcing polymorphic foreign key constraints in the same way as regular foreign keys.
Source: https://orm.drizzle.team/docs/cockroach/relations
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import IsSupportedChipGroup from '@mdx/IsSupportedChipGroup.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import CodeTab from '@mdx/CodeTab.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
# Drizzle soft relations
The sole purpose of Drizzle relations is to let you query your relational data in the most simple and consise way:
```ts
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle({ client, schema });
const result = await db._query.users.findMany({
with: {
posts: true,
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```ts
import { drizzle } from 'drizzle-orm/cockroach';
import { eq } from 'drizzle-orm';
import { posts, users } from './schema';
const db = drizzle(client);
const res = await db.select()
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.orderBy(users.id)
const mappedResult =
```
### One-to-one
Drizzle ORM provides you an API to define `one-to-one` relations between tables with the `relations` operator.
An example of a `one-to-one` relation between users and users, where a user can invite another (this example uses a self reference):
```typescript copy {10-15}
import { cockroachTable, int4, text, } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: text(),
invitedBy: int4('invited_by'),
});
export const usersRelations = relations(users, ({ one }) => ({
invitee: one(users, {
fields: [users.invitedBy],
references: [users.id],
}),
}));
```
Another example would be a user having a profile information stored in separate table. In this case, because the foreign key is stored in the "profile_info" table, the user relation have neither fields or references. This tells Typescript that `user.profileInfo` is nullable:
```typescript copy {9-17}
import { cockroachTable, int4, text, jsonb } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const usersRelations = relations(users, ({ one }) => ({
profileInfo: one(profileInfo),
}));
export const profileInfo = cockroachTable('profile_info', {
id: int4('id').primaryKey(),
userId: int4('user_id').references(() => users.id),
metadata: jsonb('metadata'),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, { fields: [profileInfo.userId], references: [users.id] }),
}));
const user = await queryUserWithProfileInfo();
//____^? type { id: number, profileInfo: { ... } | null }
```
### One-to-many
Drizzle ORM provides you an API to define `one-to-many` relations between tables with `relations` operator.
Example of `one-to-many` relation between users and posts they've written:
```typescript copy {9-11, 19-24}
import { cockroachTable, int4, text } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = cockroachTable('posts', {
id: int4('id').primaryKey(),
content: text('content'),
authorId: int4('author_id'),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
Now lets add comments to the posts:
```typescript copy {14,17-22,24-29}
...
export const posts = cockroachTable('posts', {
id: int4('id').primaryKey(),
content: text('content'),
authorId: int4('author_id'),
});
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
comments: many(comments)
}));
export const comments = cockroachTable('comments', {
id: int4('id').primaryKey(),
text: text('text'),
authorId: int4('author_id'),
postId: int4('post_id'),
});
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, {
fields: [comments.postId],
references: [posts.id],
}),
}));
```
### Many-to-many
Drizzle ORM provides you an API to define `many-to-many` relations between tables through so called `junction` or `join` tables,
they have to be explicitly defined and store associations between related tables.
Example of `many-to-many` relation between users and groups:
```typescript copy {9-11, 18-20, 37-46}
import { relations } from 'drizzle-orm/_relations';
import { int4, cockroachTable, primaryKey, text } from 'drizzle-orm/cockroach-core';
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const usersRelations = relations(users, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const groups = cockroachTable('groups', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroups = cockroachTable(
'users_to_groups',
{
userId: int4('user_id')
.notNull()
.references(() => users.id),
groupId: int4('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [
primaryKey({ columns: [t.userId, t.groupId] })
],
);
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, {
fields: [usersToGroups.groupId],
references: [groups.id],
}),
user: one(users, {
fields: [usersToGroups.userId],
references: [users.id],
}),
}));
```
### Foreign keys
You might've noticed that `relations` look similar to foreign keys â they even have a `references` property. So what's the difference?
While foreign keys serve a similar purpose, defining relations between tables, they work on a different level compared to `relations`.
Foreign keys are a database level constraint, they are checked on every `insert`/`update`/`delete` operation and throw an error if a constraint is violated.
On the other hand, `relations` are a higher level abstraction, they are used to define relations between tables on the application level only.
They do not affect the database schema in any way and do not create foreign keys implicitly.
What this means is `relations` and foreign keys can be used together, but they are not dependent on each other.
You can define `relations` without using foreign keys (and vice versa), which allows them to be used with databases that do not support foreign keys.
The following two examples will work exactly the same in terms of querying the data using Drizzle relational queries.
```ts {15}
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const profileInfo = cockroachTable('profile_info', {
id: int4('id').primaryKey(),
userId: int4("user_id"),
metadata: jsonb("metadata"),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, {
fields: [profileInfo.userId],
references: [users.id],
}),
}));
```
```ts {15}
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: text('name'),
});
export const profileInfo = cockroachTable('profile_info', {
id: int4('id').primaryKey(),
userId: int4("user_id").references(() => users.id),
metadata: jsonb("metadata"),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, {
fields: [profileInfo.userId],
references: [users.id],
}),
}));
```
### Foreign key actions
For more information check the [CockroachDB foreign keys docs](https://www.cockroachlabs.com/docs/v26.2/foreign-key.html)
You can specify actions that should occur when the referenced data in the parent table is modified. These actions are known as "foreign key actions." CockroachDB provides several options for these actions.
On Delete/ Update Actions
- `CASCADE`: When a row in the parent table is deleted, all corresponding rows in the child table will also be deleted. This ensures that no orphaned rows exist in the child table.
- `NO ACTION`: This is the default action. It prevents the deletion of a row in the parent table if there are related rows in the child table. The DELETE operation in the parent table will fail.
- `RESTRICT`: Similar to NO ACTION, it prevents the deletion of a parent row if there are dependent rows in the child table. It is essentially the same as NO ACTION and included for compatibility reasons.
- `SET DEFAULT`: If a row in the parent table is deleted, the foreign key column in the child table will be set to its default value if it has one. If it doesn't have a default value, the DELETE operation will fail.
- `SET NULL`: When a row in the parent table is deleted, the foreign key column in the child table will be set to NULL. This action assumes that the foreign key column in the child table allows NULL values.
> Analogous to ON DELETE there is also ON UPDATE which is invoked when a referenced column is changed (updated). The possible actions are the same, except that column lists cannot be specified for SET NULL and SET DEFAULT. In this case, CASCADE means that the updated values of the referenced column(s) should be copied into the referencing row(s).
in drizzle you can add foreign key action using `references()` second argument.
type of the actions
```typescript
export type UpdateDeleteAction = 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default';
// second argument of references interface
actions?: {
onUpdate?: UpdateDeleteAction;
onDelete?: UpdateDeleteAction;
} | undefined
```
In the following example, adding `onDelete: 'cascade'` to the author field on the `posts` schema means that deleting the `user` will also delete all related Post records.
```typescript {11}
import { cockroachTable, int4, text } from 'drizzle-orm/cockroach-core';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: text(),
});
export const posts = cockroachTable('posts', {
id: int4().primaryKey(),
name: text(),
author: int4().references(() => users.id, { onDelete: 'cascade' }).notNull(),
});
```
For constraints specified with the `foreignKey` operator, foreign key actions are defined with the syntax:
```typescript {18-19}
import { foreignKey, cockroachTable, int4, text } from 'drizzle-orm/cockroach-core';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: text(),
});
export const posts = cockroachTable('posts', {
id: int4().primaryKey(),
name: text(),
author: int4().notNull(),
}, (table) => [
foreignKey({
name: "author_fk",
columns: [table.author],
foreignColumns: [users.id],
})
.onDelete('cascade')
.onUpdate('cascade')
]);
```
### Disambiguating relations
Drizzle also provides the `relationName` option as a way to disambiguate
relations when you define multiple of them between the same two tables. For
example, if you define a `posts` table that has the `author` and `reviewer`
relations.
```ts {9-12, 21-32}
import { cockroachTable, int4, text } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: text(),
});
export const usersRelations = relations(users, ({ many }) => ({
author: many(posts, { relationName: 'author' }),
reviewer: many(posts, { relationName: 'reviewer' }),
}));
export const posts = cockroachTable('posts', {
id: int4().primaryKey(),
content: text(),
authorId: int4('author_id'),
reviewerId: int4('reviewer_id'),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
relationName: 'author',
}),
reviewer: one(users, {
fields: [posts.reviewerId],
references: [users.id],
relationName: 'reviewer',
}),
}));
```
Source: https://orm.drizzle.team/docs/cockroach/rqb
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Drizzle Queries
Drizzle ORM is designed to be a thin typed layer on top of SQL.
We truly believe we've designed the best way to operate an SQL database from TypeScript and it's time to make it better.
Relational queries are meant to provide you with a great developer experience for querying
nested relational data from an SQL database, avoiding multiple joins and complex data mappings.
It is an extension to the existing schema definition and query builder.
You can opt-in to use it based on your needs.
We've made sure you have both the best-in-class developer experience and performance.
```typescript copy /schema/3
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle({ schema });
const result = await db._query.users.findMany({
with: {
posts: true
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```typescript copy
import { int4, string, cockroachTable } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: string().notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = cockroachTable('posts', {
id: int4().primaryKey(),
content: string('content').notNull(),
authorId: int4('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```
â ī¸ If you have SQL schema declared in multiple files you can do it like that
```typescript copy /schema/3
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
const result = await db._query.users.findMany({
with: {
posts: true
},
});
```
```ts
// schema declaration in the first file
```
```ts
// schema declaration in the second file
```
## Querying
Relational queries are an extension to Drizzle's original **[query builder](/docs/cockroach/select)**.
You need to provide all `tables` and `relations` from your schema file/files upon `drizzle()`
initialization and then just use the `db._query` API.
`drizzle` import path depends on the **[database driver](/docs/cockroach/connect-overview)** you're using.
```ts
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle({ schema });
await db._query.users.findMany(...);
```
```ts
// if you have schema in multiple files
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/cockroach';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
await db._query.users.findMany(...);
```
```typescript copy
import { type AnyCockroachColumn, boolean, int4, cockroachTable, primaryKey, string, timestamp } from 'drizzle-orm/cockroach-core';
import { relations } from 'drizzle-orm/_relations';
export const users = cockroachTable('users', {
id: int4('id').primaryKey(),
name: string('name').notNull(),
verified: boolean('verified').notNull(),
invitedBy: int4('invited_by').references((): AnyCockroachColumn => users.id),
});
export const usersRelations = relations(users, ({ one, many }) => ({
invitee: one(users, { fields: [users.invitedBy], references: [users.id] }),
usersToGroups: many(usersToGroups),
posts: many(posts),
}));
export const groups = cockroachTable('groups', {
id: int4('id').primaryKey(),
name: string('name').notNull(),
description: string('description'),
});
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroups = cockroachTable('users_to_groups', {
id: int4('id').primaryKey(),
userId: int4('user_id').notNull().references(() => users.id),
groupId: int4('group_id').notNull().references(() => groups.id),
}, (t) => [
primaryKey({ columns: [t.userId, t.groupId] })
]);
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, { fields: [usersToGroups.groupId], references: [groups.id] }),
user: one(users, { fields: [usersToGroups.userId], references: [users.id] }),
}));
export const posts = cockroachTable('posts', {
id: int4('id').primaryKey(),
content: string('content').notNull(),
authorId: int4('author_id').references(() => users.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const comments = cockroachTable('comments', {
id: int4('id').primaryKey(),
content: string('content').notNull(),
creator: int4('creator').references(() => users.id),
postId: int4('post_id').references(() => posts.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
export const commentsRelations = relations(comments, ({ one, many }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.creator], references: [users.id] }),
likes: many(commentLikes),
}));
export const commentLikes = cockroachTable('comment_likes', {
id: int4('id').primaryKey(),
creator: int4('creator').references(() => users.id),
commentId: int4('comment_id').references(() => comments.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
export const commentLikesRelations = relations(commentLikes, ({ one }) => ({
comment: one(comments, { fields: [commentLikes.commentId], references: [comments.id] }),
author: one(users, { fields: [commentLikes.creator], references: [users.id] }),
}));
```
Drizzle provides `.findMany()` and `.findFirst()` APIs.
### Find many
```typescript copy
const users = await db._query.users.findMany();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
}[];
```
### Find first
`.findFirst()` will add `limit 1` to the query.
```typescript copy
const user = await db._query.users.findFirst();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
};
```
### Include relations
`With` operator lets you combine data from multiple related tables and properly aggregate results.
**Getting all posts with comments:**
```typescript copy
const posts = await db._query.posts.findMany({
with: {
comments: true,
},
});
```
**Getting first post with comments:**
```typescript copy
const post = await db._query.posts.findFirst({
with: {
comments: true,
},
});
```
You can chain nested with statements as much as necessary.
For any nested `with` queries Drizzle will infer types using [Core Type API](/docs/cockroach/goodies#type-api).
**Get all users with posts. Each post should contain a list of comments:**
```typescript copy
const users = await db._query.users.findMany({
with: {
posts: {
with: {
comments: true,
},
},
},
});
```
### Partial fields select
`columns` parameter lets you include or omit columns you want to get from the database.
Drizzle performs partial selects on the query level, no additional data is transferred from the database.
Keep in mind that **a single SQL statement is outputted by Drizzle.**
**Get all posts with just `id`, `content` and include `comments`:**
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});
```
**Get all posts without `content`:**
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
content: false,
},
});
```
When both `true` and `false` select options are present, all `false` options are ignored.
If you include the `name` field and exclude the `id` field, `id` exclusion will be redundant,
all fields apart from `name` would be excluded anyways.
**Exclude and Include fields in the same query:**
```typescript copy
const users = await db._query.users.findMany({
columns: {
name: true,
id: false // ignored
},
});
```
```ts
// result type
const users: {
name: string;
};
```
**Only include columns from nested relations:**
```typescript copy
const res = await db._query.users.findMany({
columns: {},
with: {
posts: true
}
});
```
```ts
// result type
const res: {
posts: {
id: number,
string: string
}
}[];
```
### Nested partial fields select
Just like with **[`partial select`](#partial-select)**, you can include or exclude columns of nested relations:
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});
```
### Select filters
Just like in our SQL-like query builder,
relational queries API lets you define filters and conditions with the list of our **[`operators`](/docs/cockroach/operators)**.
You can either import them from `drizzle-orm` or use from the callback syntax:
```typescript copy
import { eq } from 'drizzle-orm';
const users = await db._query.users.findMany({
where: eq(users.id, 1)
})
```
```ts copy
const users = await db._query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
})
```
Find post with `id=1` and comments that were created before particular date:
```typescript copy
await db._query.posts.findMany({
where: (posts, { eq }) => (eq(posts.id, 1)),
with: {
comments: {
where: (comments, { lt }) => lt(comments.createdAt, new Date()),
},
},
});
```
### Limit & Offset
Drizzle ORM provides `limit` & `offset` API for queries and for the nested entities.
**Find 5 posts:**
```typescript copy
await db._query.posts.findMany({
limit: 5,
});
```
**Find posts and get 3 comments at most:**
```typescript copy
await db._query.posts.findMany({
with: {
comments: {
limit: 3,
},
},
});
```
`offset` is only available for top level query.
```typescript
await db._query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // incorrect â
limit: 3,
},
},
});
```
Find posts with comments from the 5th to the 10th post:
```typescript copy
await db._query.posts.findMany({
limit: 5,
offset: 5,
with: {
comments: true,
},
});
```
### Order By
Drizzle provides API for ordering in the relational query builder.
You can use same ordering **[core API](/docs/cockroach/select#order-by)** or use
`order by` operator from the callback with no imports.
```typescript copy
import { desc, asc } from 'drizzle-orm';
await db._query.posts.findMany({
orderBy: [asc(posts.id)],
});
```
```typescript copy
await db._query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
});
```
**Order by `asc` + `desc`:**
```typescript copy
await db._query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
with: {
comments: {
orderBy: (comments, { desc }) => [desc(comments.id)],
},
},
});
```
### Include custom fields
Relational query API lets you add custom additional fields.
It's useful when you need to retrieve data and apply additional functions to it.
As of now aggregations are not supported in `extras`, please use **[`core queries`](/docs/cockroach/select)** for that.
```typescript copy {5}
import { sql } from 'drizzle-orm';
await db._query.users.findMany({
extras: {
loweredName: sql`lower(${users.name})`.as('lowered_name'),
},
})
```
```typescript copy {3}
await db._query.users.findMany({
extras: {
loweredName: (users, { sql }) => sql`lower(${users.name})`.as('lowered_name'),
},
})
```
`lowerName` as a key will be included to all fields in returned object.
You have to explicitly specify `.as("")`
To retrieve all users with groups, but with the fullName field included (which is a concatenation of firstName and lastName),
you can use the following query with the Drizzle relational query builder.
```typescript copy
const res = await db._query.users.findMany({
extras: {
fullName: sql`concat(${users.name}, " ", ${users.name})`.as('full_name'),
},
with: {
usersToGroups: {
with: {
group: true,
},
},
},
});
```
```ts
// result type
const res: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
fullName: string;
usersToGroups: {
group: {
id: number;
name: string;
description: string | null;
};
}[];
}[];
```
To retrieve all posts with comments and add an additional field to calculate the size of the post content and the size of each comment content:
```typescript copy
const res = await db._query.posts.findMany({
extras: (table, { sql }) => ({
contentLength: (sql`length(${table.content})`).as('content_length'),
}),
with: {
comments: {
extras: {
commentSize: sql`length(${comments.content})`.as('comment_size'),
},
},
},
});
```
```ts
// result type
const res: {
id: number;
createdAt: Date;
content: string;
authorId: number | null;
contentLength: number;
comments: {
id: number;
createdAt: Date;
content: string;
creator: number | null;
postId: number | null;
commentSize: number;
}[];
};
```
### Prepared statements
Prepared statements are designed to massively improve query performance â [see here.](/docs/cockroach/perf-queries)
In this section, you can learn how to define placeholders and execute prepared statements
using the Drizzle relational query builder.
##### **Placeholder in `where`**
```ts copy
const prepared = db._query.users.findMany({
where: ((users, { eq }) => eq(users.id, placeholder('id'))),
with: {
posts: {
where: ((users, { eq }) => eq(users.id, placeholder('pid'))),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ id: 1, pid: 1 });
```
##### **Placeholder in `limit`**
```ts copy
const prepared = db._query.users.findMany({
with: {
posts: {
limit: placeholder('limit'),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ limit: 1 });
```
##### **Placeholder in `offset`**
```ts copy
const prepared = db._query.users.findMany({
offset: placeholder('offset'),
with: {
posts: true,
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ offset: 1 });
```
##### **Multiple placeholders**
```ts copy
const prepared = db._query.users.findMany({
limit: placeholder('uLimit'),
offset: placeholder('uOffset'),
where: ((users, { eq, or }) => or(eq(users.id, placeholder('id')), eq(users.id, 3))),
with: {
posts: {
where: ((users, { eq }) => eq(users.id, placeholder('pid'))),
limit: placeholder('pLimit'),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 });
```
Source: https://orm.drizzle.team/docs/cockroach/schemas
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# Table schemas
If you declare an entity within a schema, query builder will prepend schema names in queries:
`select * from "schema"."users"`
```ts copy {3,5,7}
import { int4, string, cockroachSchema } from "drizzle-orm/cockroach-core";
export const mySchema = cockroachSchema("my_schema");
export const colors = mySchema.enum('colors', ['red', 'green', 'blue']);
export const mySchemaUsers = mySchema.table('users', {
id: int4('id').primaryKey(),
name: string('name'),
color: colors('color').default('red'),
});
```
```sql
CREATE SCHEMA "my_schema";
CREATE TYPE "my_schema"."colors" AS ENUM('red', 'green', 'blue');
CREATE TABLE "my_schema"."users" (
"id" int4 PRIMARY KEY,
"name" string,
"color" "my_schema"."colors" DEFAULT 'red'::"my_schema"."colors"
);
```
{/* TODO: ??? example > **Warning**
> If you will have tables with same names in different schemas then drizzle will respond with `never[]` error in result types and error from database
>
> In this case you may use [alias syntax](./joins#join-aliases-and-self-joins) */}
Source: https://orm.drizzle.team/docs/cockroach/seed-functions
import Callout from '@mdx/Callout.astro';
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
# Generators
For now, specifying `arraySize` along with `isUnique` in generators that support it will result in unique values being generated (not unique arrays), which will then be packed into arrays.
## ---
### `default`
Generates the same given value each time the generator is called.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`defaultValue` |-- |`any`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.default({
// value you want to generate
defaultValue: "post content",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `valuesFromArray`
Generates values from given array
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`values` |-- |`any[]` \| `{ weight: number; values: any[] }[]`
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
title: funcs.valuesFromArray({
// Array of values you want to generate (can be an array of weighted values)
values: ["Title1", "Title2", "Title3", "Title4", "Title5"],
// Property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `intPrimaryKey`
Generates sequential integers starting from 1.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |-- |-- |--
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
id: funcs.intPrimaryKey(),
},
},
}));
```
### `number`
Generates numbers with a floating point within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`precision` |`100` |`number`
| |`maxValue` |``` `precision * 1000` if isUnique equals false``` ``` `precision * count` if isUnique equals true``` |`number`
| |`minValue` |`-maxValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitPrice: funcs.number({
// lower border of range.
minValue: 10,
// upper border of range.
maxValue: 120,
// precision of generated number:
// precision equals 10 means that values will be accurate to one tenth (1.2, 34.6);
// precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67).
precision: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `int`
Generates integers within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxValue` |``` `1000` if isUnique equals false``` ``` `count * 10` if isUnique equals true``` |`number \| bigint`
| |`minValue` |`-maxValue` |`number \| bigint`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitsInStock: funcs.int({
// lower border of range.
minValue: 0,
// lower border of range.
maxValue: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `boolean`
Generates boolean values (true or false)
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
isAvailable: funcs.boolean({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `date`
Generates a date within the given range
| | param | default | type
|:-| :-------- | :-------------- | :--------
| |`minDate` |``` `'2024-05-08' - 4 years` - if maxDate is not set``` ``` `maxDate - 8 years` - if maxDate is set ``` |`string \| Date`
| |`maxDate` |``` `'2024-05-08' + 4 years` - if minDate is not set``` ``` `minDate + 8 years` - if minDate is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
If only one of the parameters (`minDate` or `maxDate`) is provided, the unspecified parameter will be calculated by adding or subtracting 8 years to/from the specified one
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthDate: funcs.date({
// lower border of range.
minDate: "1990-01-01",
// upper border of range.
maxDate: "2010-12-31",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `time`
Generates time in 24-hour format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |`'00:00:00.000Z'` |`string \| Date`
| |`max` |`'23:59:59.999Z'` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthTime: funcs.time({
// lower border of range.
min: "11:12:13.141",
// upper border of range.
max: "15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `timestamp`
Generates timestamps
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.timestamp({
// lower border of range.
min: "2025-03-07T11:12:13.141",
// upper border of range.
max: "2025-03-08T15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `datetime`
Generates datetime objects
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.datetime({
// lower border of range.
min: "2025-03-07 13:12:13Z",
// upper border of range.
max: "2025-03-09 15:12:13Z",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `year`
Generates years in `YYYY` format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthYear: funcs.year({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `json`
Generates JSON objects with a fixed structure
```ts
{ email, name, isGraduated, hasJob, salary, startedWorking, visitedCountries}
// or
{ email, name, isGraduated, hasJob, visitedCountries }
```
> The JSON structure will be picked randomly
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
metadata: funcs.json({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `interval`
Generates time intervals.
Example of a generated value: `1 year 12 days 5 minutes`
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
timeSpentOnWebsite: funcs.interval({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `string`
Generates random strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
hashedPassword: funcs.string({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `uuid`
Generates v4 UUID strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
id: funcs.uuid({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `firstName`
Generates a person's first name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
firstName: funcs.firstName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `lastName`
Generates a person's last name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
lastName: funcs.lastName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `fullName`
Generates a person's full name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
fullName: funcs.fullName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `email`
Generates unique email addresses
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
email: funcs.email({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `phoneNumber`
Generates unique phone numbers
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`template` |-- |`string`
| |`prefixes` |[Used dataset for prefixes](https://github.com/OleksiiKH0240/drizzle-orm/blob/main/drizzle-seed/src/datasets/phonesInfo.ts) |`string[]`
| |`generatedDigitsNumbers` | `7` - `if prefixes was defined` |`number \| number[]`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
//generate phone number using template property
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `template` - phone number template, where all '#' symbols will be substituted with generated digits.
template: "+(380) ###-####",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
//generate phone number using prefixes and generatedDigitsNumbers properties
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: 7,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
// generate phone number using prefixes and generatedDigitsNumbers properties but with different generatedDigitsNumbers for prefixes
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67", "+1"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: [7, 7, 10],
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `country`
Generates country's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
country: funcs.country({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `city`
Generates city's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
city: funcs.city({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `streetAddress`
Generates street address
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
streetAddress: funcs.streetAddress({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `jobTitle`
Generates job titles
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
jobTitle: funcs.jobTitle({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `postcode`
Generates postal codes
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
postcode: funcs.postcode({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `state`
Generates US states
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
state: funcs.state({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `companyName`
Generates random company's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
company: funcs.companyName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `loremIpsum`
Generates `lorem ipsum` text sentences.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`sentencesCount` | 1 |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.loremIpsum({
// `sentencesCount` - number of sentences you want to generate as one generated value(string).
sentencesCount: 2,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `point`
Generates 2D points within specified ranges for x and y coordinates.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxXValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minXValue` |`-maxXValue` |`number`
| |`maxYValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minYValue` |`-maxYValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
triangles: {
columns: {
pointCoords: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minXValue` - lower bound of range for x coordinate.
minXValue: -5,
// `maxXValue` - upper bound of range for x coordinate.
maxXValue: 20,
// `minYValue` - lower bound of range for y coordinate.
minYValue: 0,
// `maxYValue` - upper bound of range for y coordinate.
maxYValue: 30,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `line`
Generates 2D lines within specified ranges for a, b and c parameters of line.
```
line equation: a*x + b*y + c = 0
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxAValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minAValue` |`-maxAValue` |`number`
| |`maxBValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minBValue` |`-maxBValue` |`number`
| |`maxCValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minCValue` |`-maxCValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
lines: {
columns: {
lineParams: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minAValue` - lower bound of range for a parameter.
minAValue: -5,
// `maxAValue` - upper bound of range for x parameter.
maxAValue: 20,
// `minBValue` - lower bound of range for y parameter.
minBValue: 0,
// `maxBValue` - upper bound of range for y parameter.
maxBValue: 30,
// `minCValue` - lower bound of range for y parameter.
minCValue: 0,
// `maxCValue` - upper bound of range for y parameter.
maxCValue: 10,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `bitString`
Generates bit strings based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`dimensions` |`database column bit-length` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
bitStringTable: {
columns: {
bit: funcs.bitString({
// desired length of each bit string (e.g., `dimensions = 3` produces values like `'010'`).
dimensions: 12,
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
}),
},
},
}));
```
### `inet`
Generates ip addresses based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`ipAddress` |`'ipv4'` |`'ipv4' \| 'ipv6'`
| |`includeCidr`|`true` |`boolean`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
inetTable: {
columns: {
inet: funcs.inet({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// type of IP address to generate â either "ipv4" or "ipv6";
ipAddress: "ipv4",
// determines whether generated IPs include a CIDR suffix.
includeCidr: true,
}),
},
},
}));
```
### `geometry`
Generates geometry objects based on the given parameters.
Currently, if you set arraySize to a value greater than 1
or try to insert more than one `geometry point` element into a `geometry(point, 0)[]` column in CockroachDB or CockroachDB via drizzle-orm,
youâll encounter an error.
This bug is already in the backlog.
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, cockroachTable } from 'drizzle-orm/cockroach-core';
const geometryTable = cockroachTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(3),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// currently arraySize with values > 1 are not supported
arraySize: 3,
}),
},
},
}));
```
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, cockroachTable } from 'drizzle-orm/cockroach-core';
const geometryTable = cockroachTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(1),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// will work as expected
arraySize: 1,
}),
},
},
}));
```
Currently, if you set the SRID of a `geometry(point)` column to anything other than 0 (for example, 4326) in your drizzle-orm table declaration,
youâll encounter an error during the seeding process.
This bug is already in the backlog.
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, cockroachTable } from 'drizzle-orm/cockroach-core';
const geometryTable = cockroachTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 4326 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, cockroachTable } from 'drizzle-orm/cockroach-core';
const geometryTable = cockroachTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 0 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`type` |`'point'` |`'point'`
| |`srid` |`4326` |`4326 \| 3857`
| |`decimalPlaces` |`6` |`1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryPointTuple: funcs.geometry({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 1,
// geometry type to generate; currently only `'point'` is supported;
type: "point",
// Spatial Reference System Identifier: determines what type of point will be generated - either `4326` or `3857`;
srid: 4326,
// number of decimal places for points when `srid` is `4326` (e.g., `decimalPlaces = 3` produces values like `'point(30.723 46.482)'`).
decimalPlaces: 5,
}),
},
},
}));
```
### `vector`
Generates vectors based on the provided parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`decimalPlaces` |`2` |`number`
| |`dimensions` |`database columnâs dimensions` |`number`
| |`minValue` |`-1000` |`number`
| |`maxValue` |`1000` |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
vectorTable: {
columns: {
vector: funcs.vector({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// number of decimal places for each vector element (e.g., `decimalPlaces = 3` produces values like `1.123`);
decimalPlaces: 5,
// number of elements in each generated vector (e.g., `dimensions = 3` produces values like `[1,2,3]`);
dimensions: 12,
// minimum allowed value for each vector element;
minValue: -100,
// maximum allowed value for each vector element.
maxValue: 100,
}),
},
},
}));
```
Source: https://orm.drizzle.team/docs/cockroach/seed-overview
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
# Drizzle Seed
`drizzle-seed` is a TypeScript library that helps you generate deterministic, yet realistic,
fake data to populate your database. By leveraging a seedable pseudorandom number generator (pRNG),
it ensures that the data you generate is consistent and reproducible across different runs.
This is especially useful for testing, development, and debugging purposes.
#### What is Deterministic Data Generation?
Deterministic data generation means that the same input will always produce the same output.
In the context of `drizzle-seed`, when you initialize the library with the same seed number,
it will generate the same sequence of fake data every time. This allows for predictable and repeatable data sets.
#### Pseudorandom Number Generator (pRNG)
A pseudorandom number generator is an algorithm that produces a sequence of numbers
that approximates the properties of random numbers. However, because it's based on an initial value
called a seed, you can control its randomness. By using the same seed, the pRNG will produce the
same sequence of numbers, making your data generation process reproducible.
#### Benefits of Using a pRNG:
- Consistency: Ensures that your tests run on the same data every time.
- Debugging: Makes it easier to reproduce and fix bugs by providing a consistent data set.
- Collaboration: Team members can share seed numbers to work with the same data sets.
With drizzle-seed, you get the best of both worlds: the ability to generate realistic fake data and the control to reproduce it whenever needed.
## Installation
drizzle-seed
## Basic Usage
In this example we will create 10 users with random names and ids
```ts {12}
import { cockroachTable, int4, text } from "drizzle-orm/cockroach-core";
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
const users = cockroachTable("users", {
id: int4().primaryKey(),
name: text().notNull(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users });
}
main();
```
## Options
**`count`**
By default, the `seed` function will create 10 entities.
However, if you need more for your tests, you can specify this in the seed options object
```ts
await seed(db, schema, { count: 1000 });
```
**`seed`**
If you need a seed to generate a different set of values for all subsequent runs, you can define a different number
in the `seed` option. Any new number will generate a unique set of values
```ts
await seed(db, schema, { seed: 12345 });
```
## Reset database
With `drizzle-seed`, you can easily reset your database and seed it with new values, for example, in your test suites
```ts
// path to a file with schema you want to reset
import * as schema from "./schema.ts";
import { reset } from "drizzle-seed";
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await reset(db, schema);
}
main();
```
Different dialects will have different strategies for database resetting
For CockroachDB, the `drizzle-seed` package will generate `TRUNCATE` statements with the `CASCADE` option to
ensure that all tables are empty after running the reset function
```sql
TRUNCATE tableName1, tableName2, ... CASCADE;
```
## Refinements
In case you need to change the behavior of the seed generator functions that `drizzle-seed` uses by default, you can specify your own implementation and even use your own list of values for the seeding process
`.refine` is a callback that receives a list of all available generator functions from `drizzle-seed`. It should return an object with keys representing the tables you want to refine, defining their behavior as needed.
Each table can specify several properties to simplify seeding your database:
- `columns`: Refine the default behavior of each column by specifying the required generator function, or exclude the column from seeding by specifying `false`.
- `count`: Specify the number of rows to insert into the database. By default, it's 10. If a global count is defined in the `seed()` options, the count defined here will override it for this specific table.
- `with`: Define how many referenced entities to create for each parent table if you want to generate associated entities.
You can also specify a weighted random distribution for the number of referenced values you want to create. For details on this API, you can refer to [Weighted Random docs](#weighted-random) docs section
**API**
```ts
await seed(db, schema).refine((f) => ({
users: {
columns: {},
count: 10,
with: {
posts: 10
}
},
}));
```
Let's check a few examples with an explanation of what will happen:
```ts filename='schema.ts'
import { cockroachTable, int4, text } from "drizzle-orm/cockroach-core";
export const users = cockroachTable("users", {
id: int4().primaryKey(),
name: text().notNull(),
});
export const posts = cockroachTable("posts", {
id: int4().primaryKey(),
description: text(),
userId: int4().references(() => users.id),
});
```
**Example 1**: Seed only the `users` table with 20 entities and with refined seed logic for the `name` column
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: f.fullName(),
},
count: 20
}
}));
}
main();
```
**Example 2**: Seed only the `users` table, excluding the `name` column from seeding
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: false, // the name column will not be seeded, allowing the database to use its default value.
}
}
}));
}
main();
```
**Example 3**: Seed the `users` table with 20 entities and add 10 `posts` for each `user` by seeding the `posts` table and creating a reference from `posts` to `users`
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 20,
with: {
posts: 10
}
}
}));
}
main();
```
**Example 4**: Seed the `users` table with 5 entities and populate the database with 100 `posts` without connecting them to the `users` entities. Refine `id` generation for `users` so
that it will give any int from `10000` to `20000` and remains unique, and refine `posts` to retrieve values from a self-defined array
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 5,
columns: {
id: f.int({
minValue: 10000,
maxValue: 20000,
isUnique: true,
}),
}
},
posts: {
count: 100,
columns: {
description: f.valuesFromArray({
values: [
"The sun set behind the mountains, painting the sky in hues of orange and purple",
"I can't believe how good this homemade pizza turned out!",
"Sometimes, all you need is a good book and a quiet corner.",
"Who else thinks rainy days are perfect for binge-watching old movies?",
"Tried a new hiking trail today and found the most amazing waterfall!",
// ...
],
})
}
}
}));
}
main();
```
There are many more possibilities that we will define in these docs, but for now, you can explore a few sections in this documentation. Check the [Generators](/docs/cockroach/seed-functions) section to get familiar with all the available generator functions you can use.
A particularly great feature is the ability to use weighted randomization, both for generator values created for a column and for determining the number of related entities that can be generated by `drizzle-seed`.
Please check [Weighted Random docs](#weighted-random) for more info.
## Weighted Random
There may be cases where you need to use multiple datasets with a different priority that should be inserted into your database during the seed stage. For such cases, drizzle-seed provides an API called weighted random
The Drizzle Seed package has a few places where weighted random can be used:
- Columns inside each table refinements
- The `with` property, determining the amount of related entities to be created
Let's check an example for both:
```ts filename="schema.ts"
import { cockroachTable, int4, text, varchar, doublePrecision } from "drizzle-orm/cockroach-core";
export const orders = cockroachTable(
"orders",
{
id: int4().primaryKey(),
name: text().notNull(),
quantityPerUnit: varchar().notNull(),
unitPrice: doublePrecision().notNull(),
unitsInStock: int4().notNull(),
unitsOnOrder: int4().notNull(),
reorderLevel: int4().notNull(),
discontinued: int4().notNull(),
}
);
export const details = cockroachTable(
"details",
{
unitPrice: doublePrecision().notNull(),
quantity: int4().notNull(),
discount: doublePrecision().notNull(),
orderId: int4()
.notNull()
.references(() => orders.id, { onDelete: "cascade" }),
}
);
```
**Example 1**: Refine the `unitPrice` generation logic to generate `5000` random prices, with a 30% chance of prices between 10-100 and a 70% chance of prices between 100-300
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
count: 5000,
columns: {
unitPrice: f.weightedRandom(
[
{
weight: 0.3,
value: f.int({ minValue: 10, maxValue: 100 })
},
{
weight: 0.7,
value: f.number({ minValue: 100, maxValue: 300, precision: 100 })
}
]
),
}
}
}));
}
main();
```
**Example 2**: For each order, generate 1 to 3 details with a 60% chance, 5 to 7 details with a 30% chance, and 8 to 10 details with a 10% chance
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/cockroach";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3] },
{ weight: 0.3, count: [5, 6, 7] },
{ weight: 0.1, count: [8, 9, 10] },
]
}
}
}));
}
main();
```
## Complex example
```ts
import { seed } from "drizzle-seed";
import * as schema from "./schema.ts";
const main = async () => {
const titlesOfCourtesy = ["Ms.", "Mrs.", "Dr."];
const unitsOnOrders = [0, 10, 20, 30, 50, 60, 70, 80, 100];
const reorderLevels = [0, 5, 10, 15, 20, 25, 30];
const quantityPerUnit = [
"100 - 100 g pieces",
"100 - 250 g bags",
"10 - 200 g glasses",
"10 - 4 oz boxes",
"10 - 500 g pkgs.",
"10 - 500 g pkgs."
];
const discounts = [0.05, 0.15, 0.2, 0.25];
await seed(db, schema).refine((funcs) => ({
customers: {
count: 10000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" }),
fax: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
employees: {
count: 200,
columns: {
firstName: funcs.firstName(),
lastName: funcs.lastName(),
title: funcs.jobTitle(),
titleOfCourtesy: funcs.valuesFromArray({ values: titlesOfCourtesy }),
birthDate: funcs.date({ minDate: "2010-12-31", maxDate: "2010-12-31" }),
hireDate: funcs.date({ minDate: "2010-12-31", maxDate: "2024-08-26" }),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
country: funcs.country(),
homePhone: funcs.phoneNumber({ template: "(###) ###-####" }),
extension: funcs.int({ minValue: 428, maxValue: 5467 }),
notes: funcs.loremIpsum()
}
},
orders: {
count: 50000,
columns: {
shipVia: funcs.int({ minValue: 1, maxValue: 3 }),
freight: funcs.number({ minValue: 0, maxValue: 1000, precision: 100 }),
shipName: funcs.streetAddress(),
shipCity: funcs.city(),
shipRegion: funcs.state(),
shipPostalCode: funcs.postcode(),
shipCountry: funcs.country()
},
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3, 4] },
{ weight: 0.2, count: [5, 6, 7, 8, 9, 10] },
{ weight: 0.15, count: [11, 12, 13, 14, 15, 16, 17] },
{ weight: 0.05, count: [18, 19, 20, 21, 22, 23, 24, 25] },
]
}
},
suppliers: {
count: 1000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
products: {
count: 5000,
columns: {
name: funcs.companyName(),
quantityPerUnit: funcs.valuesFromArray({ values: quantityPerUnit }),
unitPrice: funcs.weightedRandom(
[
{
weight: 0.5,
value: funcs.int({ minValue: 3, maxValue: 300 })
},
{
weight: 0.5,
value: funcs.number({ minValue: 3, maxValue: 300, precision: 100 })
}
]
),
unitsInStock: funcs.int({ minValue: 0, maxValue: 125 }),
unitsOnOrder: funcs.valuesFromArray({ values: unitsOnOrders }),
reorderLevel: funcs.valuesFromArray({ values: reorderLevels }),
discontinued: funcs.int({ minValue: 0, maxValue: 1 })
}
},
details: {
columns: {
unitPrice: funcs.number({ minValue: 10, maxValue: 130 }),
quantity: funcs.int({ minValue: 1, maxValue: 130 }),
discount: funcs.weightedRandom(
[
{ weight: 0.5, value: funcs.valuesFromArray({ values: discounts }) },
{ weight: 0.5, value: funcs.default({ defaultValue: 0 }) }
]
)
}
}
}));
}
main();
```
```ts
import type { AnyCockroachColumn } from "drizzle-orm/cockroach-core";
import { int4, numeric, cockroachTable, text, timestamp, varchar } from "drizzle-orm/cockroach-core";
export const customers = cockroachTable('customer', {
id: varchar({ length: 256 }).primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text(),
region: text(),
country: text().notNull(),
phone: text().notNull(),
fax: text(),
});
export const employees = cockroachTable(
'employee',
{
id: int4().primaryKey(),
lastName: text().notNull(),
firstName: text(),
title: text().notNull(),
titleOfCourtesy: text().notNull(),
birthDate: timestamp().notNull(),
hireDate: timestamp().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text().notNull(),
country: text().notNull(),
homePhone: text().notNull(),
extension: int4().notNull(),
notes: text().notNull(),
reportsTo: int4().references((): AnyCockroachColumn => employees.id),
photoPath: text(),
},
);
export const orders = cockroachTable('order', {
id: int4().primaryKey(),
orderDate: timestamp().notNull(),
requiredDate: timestamp().notNull(),
shippedDate: timestamp(),
shipVia: int4().notNull(),
freight: numeric().notNull(),
shipName: text().notNull(),
shipCity: text().notNull(),
shipRegion: text(),
shipPostalCode: text(),
shipCountry: text().notNull(),
customerId: text().notNull().references(() => customers.id, { onDelete: 'cascade' }),
employeeId: int4().notNull().references(() => employees.id, { onDelete: 'cascade' }),
});
export const suppliers = cockroachTable('supplier', {
id: int4().primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
region: text(),
postalCode: text().notNull(),
country: text().notNull(),
phone: text().notNull(),
});
export const products = cockroachTable('product', {
id: int4().primaryKey(),
name: text().notNull(),
quantityPerUnit: text().notNull(),
unitPrice: numeric().notNull(),
unitsInStock: int4().notNull(),
unitsOnOrder: int4().notNull(),
reorderLevel: int4().notNull(),
discontinued: int4().notNull(),
supplierId: int4().notNull().references(() => suppliers.id, { onDelete: 'cascade' }),
});
export const details = cockroachTable('order_detail', {
unitPrice: numeric().notNull(),
quantity: int4().notNull(),
discount: numeric().notNull(),
orderId: int4().notNull().references(() => orders.id, { onDelete: 'cascade' }),
productId: int4().notNull().references(() => products.id, { onDelete: 'cascade' }),
});
```
## Limitations
#### Types limitations for `with`
Due to certain TypeScript limitations and the current API in Drizzle, it is not possible to properly infer references between tables, especially when circular dependencies between tables exist.
This means the `with` option will display all tables in the schema, and you will need to manually select the one that has a one-to-many relationship
The `with` option works for one-to-many relationships. For example, if you have one `user` and many `posts`, you can use users `with` posts, but you cannot use posts `with` users
#### Type limitations for the third parameter in Drizzle tables:
Currently, we do not have type support for the third parameter in Drizzle tables. While it will work at runtime, it will not function correctly at the type level
Source: https://orm.drizzle.team/docs/cockroach/seed-versioning
import Callout from "@mdx/Callout.astro";
import TableWrapper from "@mdx/TableWrapper.astro";
# Versioning
`drizzle-seed` uses versioning to manage outputs for static and dynamic data. To ensure true
determinism, ensure that values remain unchanged when using the same `seed` number. If changes are made to
static data sources or dynamic data generation logic, the version will be updated, allowing
you to choose between sticking with the previous version or using the latest.
You can upgrade to the latest `drizzle-seed` version for new features, such as additional
generators, while maintaining deterministic outputs with a previous version if needed. This
is particularly useful when you need to rely on existing deterministic data while accessing new functionality.
```ts
await seed(db, schema, { version: '2' });
```
## History
| api version | npm version | Changed generators |
| :-------------- | :-------------- | :------------- |
| `v1` | `0.1.1` | |
| `v2` | `0.2.1` |`string()`, `interval({ isUnique: true })` |
| `v3` | `0.4.0` |`hash generating function` |
| `v4 (LTS) ` | `1.0.0-beta.8` |`uuid` |
> This is not an actual API change; it is just an example of how we will proceed with `drizzle-seed` versioning.
For example, `lastName` generator was changed, and new version, `V2`, of this generator became available.
Later, `firstName` generator was changed, making `V3` version of this generator available.
| | `V1` | `V2` | `V3(latest)` |
| :--------------: | :--------------: | :-------------: | :--------------: |
| **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | |
| **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` |
##### Use the `firstName` generator of version 3 and the `lastName` generator of version 2
```ts
await seed(db, schema);
```
If you are not ready to use latest generator version right away, you can specify max version to use
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 2
```ts
await seed(db, schema, { version: '2' });
```
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 1.
```ts
await seed(db, schema, { version: '1' });
```
## Version 2
#### Unique `interval` generator was changed
An older version of the generator could produce intervals like `1 minute 60 seconds` and `2 minutes 0 seconds`, treating them as distinct intervals.
However, when the `1 minute 60 seconds` interval is inserted into a CockroachDB database, it is automatically converted to `2 minutes 0 seconds`. As a result, attempting to insert the `2 minutes 0 seconds` interval into a unique column afterwards will cause an error
You will be affected, if your table includes a unique column of type `interval`:
```ts
import { drizzle } from "drizzle-orm/cockroach";
import { cockroachTable, interval } from "drizzle-orm/cockroach-core";
import { seed } from "drizzle-seed";
const intervals = cockroachTable("intervals", {
interval: interval().unique()
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals });
}
main();
```
You will be affected, if you use the unique `interval` generator in your seeding script, as shown in the script below:
```ts
import { drizzle } from "drizzle-orm/cockroach";
import { cockroachTable, interval, char, varchar, text } from "drizzle-orm/cockroach-core";
import { seed } from "drizzle-seed";
const intervals = cockroachTable("intervals", {
interval: interval().unique(),
interval1: interval(),
interval2: char({ length: 256 }).unique(),
interval3: char({ length: 256 }),
interval4: varchar().unique(),
interval5: varchar(),
interval6: text().unique(),
interval7: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals }).refine((f) => ({
intervals: {
columns: {
interval: f.interval({ isUnique: true }),
interval1: f.interval({ isUnique: true }),
interval2: f.interval({ isUnique: true }),
interval3: f.interval({ isUnique: true }),
interval4: f.interval({ isUnique: true }),
interval5: f.interval({ isUnique: true }),
interval6: f.interval({ isUnique: true }),
interval7: f.interval({ isUnique: true }),
}
}
}));
}
main();
```
#### `string` generators were changed: both non-unique and unique
Ability to generate a unique string based on the length of the text column (e.g., `varchar(20)`)
You will be affected, if your table includes a column of a text-like type with a maximum length parameter or a unique column of a text-like type:
```ts
import { drizzle } from "drizzle-orm/cockroach";
import { cockroachTable, char, varchar, text } from "drizzle-orm/cockroach-core";
import { seed } from "drizzle-seed";
const strings = cockroachTable("strings", {
string2: char({ length: 256 }).unique(),
string3: char({ length: 256 }),
string4: varchar().unique(),
string5: varchar({ length: 256 }).unique(),
string6: varchar({ length: 256 }),
string7: text().unique(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings });
}
main();
```
You will be affected, if you use the `string` generator in your seeding script, as shown in the script below:
```ts
import { drizzle } from "drizzle-orm/cockroach";
import { cockroachTable, char, varchar, text } from "drizzle-orm/cockroach-core";
import { seed } from "drizzle-seed";
const strings = cockroachTable("strings", {
string1: char({ length: 256 }).unique(),
string2: char({ length: 256 }),
string3: char({ length: 256 }),
string4: varchar(),
string5: varchar().unique(),
string6: varchar({ length: 256 }).unique(),
string7: varchar({ length: 256 }),
string8: varchar({ length: 256 }),
string9: text().unique(),
string10: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings }).refine((f) => ({
strings: {
columns: {
string1: f.string({ isUnique: true }),
string2: f.string(),
string3: f.string({ isUnique: true }),
string4: f.string({ isUnique: true }),
string5: f.string({ isUnique: true }),
string6: f.string({ isUnique: true }),
string7: f.string(),
string8: f.string({ isUnique: true }),
string9: f.string({ isUnique: true }),
string10: f.string({ isUnique: true }),
}
}
}));
}
main();
```
## Version 3
#### `hash generating function was changed`
The previous version of the hash generating function generated different hashes depending on whether Bun or Node.js was used, and hashes also varied across versions of Node.js.
The new hash generating function will generate the same hash regardless of the version of Node.js or Bun, resulting in deterministic data generation across all versions.
All generators will output different values compared to the previous version, even with the same seed number.
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '3' });
```
**Switch to the old version**
The previous version of hash generating function is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 hash generating function:
```ts
await seed(db, schema, { version: '2' });
```
## Version 4
#### `uuid` generator was changed
UUID values generated by the old version of the `uuid` generator fail Zodâs v4 UUID validation.
example
```ts
import { createSelectSchema } from 'drizzle-zod';
import { seed } from 'drizzle-seed';
await seed(db, { uuidTest: schema.uuidTest }, { count: 1 }).refine((funcs) => ({
uuidTest: {
columns: {
col1: funcs.uuid()
}
}
})
);
const uuidSelectSchema = createSelectSchema(schema.uuidTest);
const res = await db.select().from(schema.uuidTest);
// the line below will throw an error when using old version of uuid generator
uuidSelectSchema.parse(res[0]);
```
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '4' });
```
**Switch to the old version**
The previous version of `uuid` generator is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '2' });
```
To use the v3 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '3' });
```
Source: https://orm.drizzle.team/docs/cockroach/select
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import $count from '@mdx/$count-cockroach.mdx';
# SQL Select
Drizzle provides you the most SQL-like way to fetch data from your database, while remaining type-safe and composable.
It natively supports mostly every query feature and capability of every dialect,
and whatever it doesn't support yet, can be added by the user with the powerful [sql](/docs/cockroach/sql) operator.
For the following examples, let's assume you have a `users` table defined like this:
```typescript
import { cockroachTable, serial, string } from 'drizzle-orm/cockroach-core';
export const users = cockroachTable('users', {
id: int4().primaryKey(),
name: string().notNull(),
age: int4(),
});
```
### Basic select
Select all rows from a table including all columns:
```typescript
const result = await db.select().from(users);
/*
{
id: number;
name: string;
age: number | null;
}[]
*/
```
```sql
select "id", "name", "age" from "users";
```
Notice that the result type is inferred automatically based on the table definition, including columns nullability.
Drizzle always explicitly lists columns in the `select` clause instead of using `select *`.
This is required internally to guarantee the fields order in the query result, and is also generally considered a good practice.
### Partial select
In some cases, you might want to select only a subset of columns from a table.
You can do that by providing a selection object to the `.select()` method:
```typescript copy
const result = await db.select({
field1: users.id,
field2: users.name,
}).from(users);
const { field1, field2 } = result[0];
```
```sql
select "id", "name" from "users";
```
Like in SQL, you can use arbitrary expressions as selection fields, not just table columns:
```typescript
const result = await db.select({
id: users.id,
lowerName: sql`lower(${users.name})`,
}).from(users);
```
```sql
select "id", lower("name") from "users";
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `string`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a string), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/cockroach/sql#sqlmapwith) method.
### Conditional select
You can have a dynamic selection object based on some condition:
```typescript
async function selectUsers(withName: boolean) {
return db
.select({
id: users.id,
...(withName ? { name: users.name } : {}),
})
.from(users);
}
const result = await selectUsers(true);
```
### Distinct select
You can use `.selectDistinct()` instead of `.select()` to retrieve only unique rows from a dataset:
```ts
await db.selectDistinct().from(users).orderBy(users.id, users.name);
await db.selectDistinct({ id: users.id }).from(users).orderBy(users.id);
```
```sql
select distinct "id", "name", "age" from "users" order by "users"."id", "users"."name";
select distinct "id" from "users" order by "users"."id";
```
In CockroachDB, you can also use the `distinct on` clause to specify how the unique rows are determined:
```ts
await db.selectDistinctOn([users.id]).from(users).orderBy(users.id);
await db.selectDistinctOn([users.name], { name: users.name }).from(users).orderBy(users.name);
```
```sql
select distinct on ("users"."id") "id", "name", "age" from "users" order by "users"."id";
select distinct on ("users"."name") "name" from "users" order by "users"."name";
```
### Advanced select
Powered by TypeScript, Drizzle APIs let you build your select queries in a variety of flexible ways.
Sneak peek of advanced partial select, for more detailed advanced usage examples - see our [dedicated guide](/docs/cockroach/guides/include-or-exclude-columns).
```ts
import { getColumns, sql } from 'drizzle-orm';
await db.select({
...getColumns(posts),
titleLength: sql`length(${posts.title})`,
}).from(posts);
```
```ts
import { getColumns } from 'drizzle-orm';
const { content, ...rest } = getColumns(posts); // exclude "content" column
await db.select({ ...rest }).from(posts); // select all other columns
```
```ts
await db.query.posts.findMany({
columns: {
title: true,
},
});
```
```ts
await db.query.posts.findMany({
columns: {
content: false,
},
});
```
## ---
### Filters
You can filter the query results using the [filter operators](/docs/cockroach/operators) in the `.where()` method:
```typescript copy
import { eq, lt, gte, ne } from 'drizzle-orm';
await db.select().from(users).where(eq(users.id, 42));
await db.select().from(users).where(lt(users.id, 42));
await db.select().from(users).where(gte(users.id, 42));
await db.select().from(users).where(ne(users.id, 42));
...
```
```sql
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" < 42;
select "id", "name", "age" from "users" where "users"."id" >= 42;
select "id", "name", "age" from "users" where "users"."id" <> 42;
```
All filter operators are implemented using the [`sql`](/docs/cockroach/sql) function.
You can use it yourself to write arbitrary SQL filters, or build your own operators.
For inspiration, you can check how the operators provided by Drizzle are [implemented](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/sql/expressions/conditions.ts).
```typescript copy
import { sql } from 'drizzle-orm';
import type { CockroachColumn } from "drizzle-orm/cockroach-core";
function equals42(col: CockroachColumn) {
return sql`${col} = 42`;
}
await db.select().from(users).where(sql`${users.id} < 42`);
await db.select().from(users).where(sql`${users.id} = 42`);
await db.select().from(users).where(equals42(users.id));
await db.select().from(users).where(sql`${users.id} >= 42`);
await db.select().from(users).where(sql`${users.id} <> 42`);
await db.select().from(users).where(sql`lower(${users.name}) = 'aaron'`);
```
```sql
select "id", "name", "age" from "users" where "users"."id" < 42;
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" >= 42;
select "id", "name", "age" from "users" where "users"."id" <> 42;
select "id", "name", "age" from "users" where lower("users"."name") = 'aaron';
```
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select "id", "name", "age" from "users" where "users"."id" = $1; -- params: [42]
```
Inverting condition with a `not` operator:
```typescript copy
import { eq, not, sql } from 'drizzle-orm';
await db.select().from(users).where(not(eq(users.id, 42)));
await db.select().from(users).where(sql`not ${users.id} = 42`);
```
```sql
select "id", "name", "age" from "users" where not ("users"."id" = 42);
select "id", "name", "age" from "users" where not "users"."id" = 42;
```
You can safely alter schema, rename tables and columns
and it will be automatically reflected in your queries because of template interpolation,
as opposed to hardcoding column or table names when writing raw SQL.
### Combining filters
You can logically combine filter operators with `and()` and `or()` operators:
```typescript copy
import { eq, and, sql } from 'drizzle-orm';
await db.select().from(users).where(
and(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 and ${users.name} = 'Dan'`);
```
```sql
select "id", "name", "age" from "users" where (("users"."id" = 42) and ("users"."name" = 'Dan'));
select "id", "name", "age" from "users" where "users"."id" = 42 and "users"."name" = 'Dan';
```
```typescript copy
import { eq, or, sql } from 'drizzle-orm';
await db.select().from(users).where(
or(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 or ${users.name} = 'Dan'`);
```
```sql
select "id", "name", "age" from "users" where (("users"."id" = 42) or ("users"."name" = 'Dan'));
select "id", "name", "age" from "users" where "users"."id" = 42 or "users"."name" = 'Dan';
```
### Advanced filters
In combination with TypeScript, Drizzle APIs provide you powerful and flexible ways to combine filters in queries.
Sneak peek of conditional filtering, for more detailed advanced usage examples - see our [dedicated guide](/docs/cockroach/guides/conditional-filters-in-query).
```ts
const searchPosts = async (term?: string) => {
await db
.select()
.from(posts)
.where(term ? ilike(posts.title, term) : undefined);
};
await searchPosts();
await searchPosts('AI');
```
```ts
const searchPosts = async (filters: SQL[]) => {
await db
.select()
.from(posts)
.where(and(...filters));
};
const filters: SQL[] = [];
filters.push(ilike(posts.title, 'AI'));
filters.push(inArray(posts.category, ['Tech', 'Art', 'Science']));
filters.push(gt(posts.views, 200));
await searchPosts(filters);
```
## ---
### Limit & offset
Use `.limit()` and `.offset()` to add limit and offset clauses to the query - for example, to implement pagination:
```ts
await db.select().from(users).limit(10);
await db.select().from(users).limit(10).offset(10);
```
```sql
select "id", "name", "age" from "users" limit 10;
select "id", "name", "age" from "users" limit 10 offset 10;
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.select().from(users).orderBy(users.name);
await db.select().from(users).orderBy(desc(users.name));
// order by multiple fields
await db.select().from(users).orderBy(users.name, users.name2);
await db.select().from(users).orderBy(asc(users.name), desc(users.name2));
```
```sql
select "id", "name", "name2", "age" from "users" order by "users"."name";
select "id", "name", "name2", "age" from "users" order by "users"."name" desc;
select "id", "name", "name2", "age" from "users" order by "users"."name", "users"."name";
select "id", "name", "name2", "age" from "users" order by "users"."name" asc, "users"."name2" desc;
```
### Advanced pagination
Powered by TypeScript, Drizzle APIs let you implement all possible SQL pagination and sorting approaches.
Sneak peek of advanced pagination, for more detailed advanced
usage examples - see our dedicated [limit offset pagination](/docs/cockroach/guides/limit-offset-pagination)
and [cursor pagination](/docs/cockroach/guides/cursor-based-pagination) guides.
```ts
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.limit(4) // the number of rows to return
.offset(4); // the number of rows to skip
```
```ts
const getUsers = async (page = 1, pageSize = 3) => {
return await db.query.users.findMany({
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
offset: (page - 1) * pageSize,
});
};
await getUsers();
```
```ts
const getUsers = async (page = 1, pageSize = 10) => {
const sq = db
.select({ id: users.id })
.from(users)
.orderBy(users.id)
.limit(pageSize)
.offset((page - 1) * pageSize)
.as('subquery');
return await db.select().from(users).innerJoin(sq, eq(users.id, sq.id)).orderBy(users.id);
};
```
```ts
const nextUserPage = async (cursor?: number, pageSize = 3) => {
return await db
.select()
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined) // if cursor is provided, get rows after it
.limit(pageSize) // the number of rows to return
.orderBy(asc(users.id)); // ordering
};
// pass the cursor of the last row of the previous page (id)
await nextUserPage(3);
```
## ---
### WITH clause
Check how to use WITH statement with [insert](/docs/cockroach/insert#with-insert-clause), [update](/docs/cockroach/update#with-update-clause), [delete](/docs/cockroach/delete#with-delete-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 sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
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";
```
You can also provide `insert`, `update` and `delete` statements inside `with`
```typescript copy
const sq = db.$with('sq').as(
db.insert(users).values({ name: 'John' }).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (insert into "users" ("id", "name", "age") values (default, 'John', default) returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
```typescript copy
const sq = db.$with('sq').as(
db.update(users).set({ age: 25 }).where(eq(users.name, 'John')).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (update "users" set "age" = 25 where "users"."name" = 'John' returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
```typescript copy
const sq = db.$with('sq').as(
db.delete(users).where(eq(users.name, 'John')).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (delete from "users" where "users"."name" = $1 returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
To select arbitrary SQL values as fields in a CTE and reference them in other CTEs or in the main query,
you need to add aliases to them:
```typescript copy
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);
```
If you don't provide an alias, the field type will become `DrizzleTypeError` and you won't be able to reference it in other queries.
If you ignore the type error and still try to use the field,
you will get a runtime error, since there's no way to reference that field without an alias.
### Select from subquery
Just like in SQL, you can embed queries into other queries by using the subquery API:
```typescript copy
const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');
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 be used in any place where a table can be used, for example in joins:
```typescript copy
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));
```
```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";
```
## ---
### Aggregations
With Drizzle, you can do aggregations using functions like `sum`, `count`, `avg`, etc. by
grouping and filtering with `.groupBy()` and `.having()` respectfully, same as you would do in raw SQL:
```typescript
import { gt, sql } from "drizzle-orm";
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as int)`,
})
.from(users)
.groupBy(users.age);
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as int)`,
})
.from(users)
.groupBy(users.age)
.having(({ count }) => gt(count, 1));
```
```sql
select "age", cast(count("id") as int)
from "users"
group by "users"."age";
select "age", cast(count("id") as int)
from "users"
group by "users"."age"
having cast(count("users"."id") as int) > 1;
```
Alternatively, you can use [`.mapWith(Number)`](/docs/cockroach/sql#sqlmapwith) to cast the value to a number at runtime.
If you need count aggregation - we recommend using our [$count](/docs/cockroach/select#count) API
### Aggregations helpers
Drizzle has a set of wrapped `sql` functions, so you don't need to write
`sql` templates for common cases in your app
Remember, aggregation functions are often used with the GROUP BY clause of the SELECT statement.
So if you are selecting using aggregating functions and other columns in one query,
be sure to use the `.groupBy` clause
**count**
Returns the number of values in `expression`.
```ts
import { count } from 'drizzle-orm'
await db.select({ value: count() }).from(users);
await db.select({ value: count(users.id) }).from(users);
```
```sql
select count(*) from "users";
select count("id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(*)`.mapWith(Number)
}).from(users);
await db.select({
value: sql`count(${users.id})`.mapWith(Number)
}).from(users);
```
**countDistinct**
Returns the number of non-duplicate values in `expression`.
```ts
import { countDistinct } from 'drizzle-orm'
await db.select({ value: countDistinct(users.id) }).from(users);
```
```sql
select count(distinct "id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(distinct ${users.id})`.mapWith(Number)
}).from(users);
```
**avg**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avg } from 'drizzle-orm'
await db.select({ value: avg(users.id) }).from(users);
```
```sql
select avg("id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(${users.id})`.mapWith(String)
}).from(users);
```
**avgDistinct**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avgDistinct } from 'drizzle-orm'
await db.select({ value: avgDistinct(users.id) }).from(users);
```
```sql
select avg(distinct "id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**sum**
Returns the sum of all non-null values in `expression`.
```ts
import { sum } from 'drizzle-orm'
await db.select({ value: sum(users.id) }).from(users);
```
```sql
select sum("id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(${users.id})`.mapWith(String)
}).from(users);
```
**sumDistinct**
Returns the sum of all non-null and non-duplicate values in `expression`.
```ts
import { sumDistinct } from 'drizzle-orm'
await db.select({ value: sumDistinct(users.id) }).from(users);
```
```sql
select sum(distinct "id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**max**
Returns the maximum value in `expression`.
```ts
import { max } from 'drizzle-orm'
await db.select({ value: max(users.id) }).from(users);
```
```sql
select max("id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`max(${expression})`.mapWith(users.id)
}).from(users);
```
**min**
Returns the minimum value in `expression`.
```ts
import { min } from 'drizzle-orm'
await db.select({ value: min(users.id) }).from(users);
```
```sql
select min("id") from "users";
```
```ts
// It's equivalent to writing
await db.select({
value: sql`min(${users.id})`.mapWith(users.id)
}).from(users);
```
A more advanced example:
```typescript copy
const orders = cockroachTable("order", {
id: int4().primaryKey(),
orderDate: timestamp("order_date", { withTimezone: true }).notNull(),
requiredDate: timestamp("required_date", { withTimezone: true }).notNull(),
shippedDate: timestamp("shipped_date", { withTimezone: true }),
shipVia: int4("ship_via").notNull(),
freight: numeric("freight").notNull(),
shipName: string("ship_name").notNull(),
shipCity: string("ship_city").notNull(),
shipRegion: string("ship_region"),
shipPostalCode: string("ship_postal_code"),
shipCountry: string("ship_country").notNull(),
customerId: string("customer_id").notNull(),
employeeId: int4("employee_id").notNull(),
});
const details = cockroachTable("order_detail", {
unitPrice: numeric("unit_price").notNull(),
quantity: int4("quantity").notNull(),
discount: numeric("discount").notNull(),
orderId: int4("order_id").notNull(),
productId: int4("product_id").notNull(),
});
await db
.select({
id: orders.id,
shippedDate: orders.shippedDate,
shipName: orders.shipName,
shipCity: orders.shipCity,
shipCountry: orders.shipCountry,
productsCount: sql`cast(count(${details.productId}) as int)`,
quantitySum: sql`sum(${details.quantity})`,
totalPrice: sql`sum(${details.quantity} * ${details.unitPrice})`,
})
.from(orders)
.leftJoin(details, eq(orders.id, details.orderId))
.groupBy(orders.id)
.orderBy(asc(orders.id))
```
### $count
<$count/>
Source: https://orm.drizzle.team/docs/cockroach/sequences
import Callout from '@mdx/Callout.astro';
# Sequences
Sequences in CockroachDB and CockroachDB are special single-row tables created to generate unique identifiers, often used for auto-incrementing primary key values. They provide a thread-safe way to generate unique sequential values across multiple sessions.
```ts
import { cockroachSchema, cockroachSequence } from 'drizzle-orm/cockroach-core';
// No params specified
export const customSequence = cockroachSequence('name');
// Sequence with params
export const customSequence = cockroachSequence('name', {
startWith: 100,
maxValue: 10000,
minValue: 100,
cache: 10,
increment: 2,
});
// Sequence in custom schema
export const customSchema = cockroachSchema('custom_schema');
export const customSequence = customSchema.sequence('name');
```
Source: https://orm.drizzle.team/docs/cockroach/set-operations
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
import Section from "@mdx/Section.astro";
# Set Operations
SQL set operations combine the results of multiple query blocks into a single result.
The SQL standard defines the following three set operations: `UNION`, `INTERSECT`, `EXCEPT`, `UNION ALL`, `INTERSECT ALL`, `EXCEPT ALL`.
### Union
Combine all results from two query blocks into a single result, omitting any duplicates.
Get all names from customers and users tables without duplicates.
```typescript copy
import { union } from 'drizzle-orm/cockroach-core'
import { users, customers } from './schema'
const allNamesForUserQuery = db.select({ name: users.name }).from(users);
const result = await union(
allNamesForUserQuery,
db.select({ name: customers.name }).from(customers)
).limit(10);
```
```sql
(select "name" from "sellers")
union
(select "name" from "customers")
limit $1 -- params: [10]
```
```ts copy
import { users, customers } from './schema'
const result = await db
.select({ name: users.name })
.from(users)
.union(db.select({ name: customers.name }).from(customers))
.limit(10);
```
```sql
(select "name" from "sellers")
union
(select "name" from "customers")
limit $1 -- params: [10]
```
```typescript copy
import { int4, cockroachTable, string, varchar } from "drizzle-orm/cockroach-core";
export const users = cockroachTable('sellers', {
id: int4().primaryKey(),
name: varchar({ length: 256 }).notNull(),
address: string(),
});
export const customers = cockroachTable('customers', {
id: int4().primaryKey(),
name: varchar({ length: 256 }).notNull(),
city: string(),
email: varchar({ length: 256 }).notNull()
});
```
### Union All
Combine all results from two query blocks into a single result, with duplicates.
Let's consider a scenario where you have two tables, one representing online sales and the other
representing in-store sales. In this case, you want to combine the data from both tables into a
single result set. Since there might be duplicate transactions,
you want to keep all the records and not eliminate duplicates.
```typescript copy
import { unionAll } from 'drizzle-orm/cockroach-core'
import { onlineSales, inStoreSales } from './schema'
const onlineTransactions = db.select({ transaction: onlineSales.transactionId }).from(onlineSales);
const inStoreTransactions = db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales);
const result = await unionAll(onlineTransactions, inStoreTransactions);
```
```sql
(select "transaction_id" from "online_sales")
union all
(select "transaction_id" from "in_store_sales")
```
```ts copy
import { onlineSales, inStoreSales } from './schema'
const result = await db
.select({ transaction: onlineSales.transactionId })
.from(onlineSales)
.unionAll(
db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales)
);
```
```sql
(select "transaction_id" from "online_sales")
union all
(select "transaction_id" from "in_store_sales")
```
```typescript copy
import { int4, cockroachTable, timestamp } from "drizzle-orm/cockroach-core";
export const onlineSales = cockroachTable('online_sales', {
transactionId: int4('transaction_id').primaryKey(),
productId: int4('product_id').unique(),
quantitySold: int4('quantity_sold'),
saleDate: timestamp('sale_date', { mode: 'date' }),
});
export const inStoreSales = cockroachTable('in_store_sales', {
transactionId: int4('transaction_id').primaryKey(),
productId: int4('product_id').unique(),
quantitySold: int4('quantity_sold'),
saleDate: timestamp('sale_date', { mode: 'date' }),
});
```
### Intersect
Combine only those rows which the results of two query blocks have in common, omitting any duplicates.
Suppose you have two tables that store information about students' course enrollments.
You want to find the courses that are common between two different departments,
but you want distinct course names, and you're not interested in counting multiple
enrollments of the same course by the same student.
In this scenario, you want to find courses that are common between the two departments but don't want
to count the same course multiple times even if multiple students from the same department are enrolled in it.
```typescript copy
import { intersect } from 'drizzle-orm/cockroach-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.courseName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.courseName }).from(depB);
const result = await intersect(departmentACourses, departmentBCourses);
```
```sql
(select "course_name" from "department_a_courses")
intersect
(select "course_name" from "department_b_courses")
```
```typescript copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.courseName })
.from(depA)
.intersect(db.select({ courseName: depB.courseName }).from(depB));
```
```sql
(select "course_name" from "department_a_courses")
intersect
(select "course_name" from "department_b_courses")
```
```typescript copy
import { int4, cockroachTable, varchar } from "drizzle-orm/cockroach-core";
export const depA = cockroachTable('department_a_courses', {
studentId: int4('student_id'),
courseName: varchar('course_name').notNull(),
});
export const depB = cockroachTable('department_b_courses', {
studentId: int4('student_id'),
courseName: varchar('course_name').notNull(),
});
```
### Intersect All
Combine only those rows which the results of two query blocks have in common, with duplicates.
Let's consider a scenario where you have two tables containing data about customer orders, and you want
to identify products that are ordered by both regular customers and VIP customers. In this case,
you want to keep track of the quantity of each product, even if it's ordered multiple times by
different customers.
In this scenario, you want to find products that are ordered by both regular customers and VIP customers,
but you want to retain the quantity information, even if the same product is ordered multiple
times by different customers.
```typescript copy
import { intersectAll } from 'drizzle-orm/cockroach-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const regularOrders = db.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);
const vipOrders = db.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);
const result = await intersectAll(regularOrders, vipOrders);
```
```sql
(select "product_id", "quantity_ordered" from "regular_customer_orders")
intersect all
(select "product_id", "quantity_ordered" from "vip_customer_orders")
```
```ts copy
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const result = await db
.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered,
})
.from(regularCustomerOrders)
.intersectAll(
db
.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered,
})
.from(vipCustomerOrders)
);
```
```sql
(select "product_id", "quantity_ordered" from "regular_customer_orders")
intersect all
(select "product_id", "quantity_ordered" from "vip_customer_orders")
```
```typescript copy
import { int4, cockroachTable } from "drizzle-orm/cockroach-core";
export const regularCustomerOrders = cockroachTable('regular_customer_orders', {
customerId: int4('customer_id').primaryKey(),
productId: int4('product_id').notNull(),
quantityOrdered: int4('quantity_ordered').notNull(),
});
export const vipCustomerOrders = cockroachTable('vip_customer_orders', {
customerId: int4('customer_id').primaryKey(),
productId: int4('product_id').notNull(),
quantityOrdered: int4('quantity_ordered').notNull(),
});
```
### Except
For two query blocks A and B, return all results from A which are not also present in B, omitting any duplicates.
Suppose you have two tables that store information about employees' project assignments.
You want to find the projects that are unique to one department
and not shared with another department, excluding duplicates.
In this scenario, you want to identify the projects that are exclusive to one department and
not shared with the other department. You don't want to count the same project
multiple times, even if multiple employees from the same department are assigned to it.
```typescript copy
import { except } from 'drizzle-orm/cockroach-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.projectsName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.projectsName }).from(depB);
const result = await except(departmentACourses, departmentBCourses);
```
```sql
(select "projects_name" from "department_a_projects")
except
(select "projects_name" from "department_b_projects")
```
```ts copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.projectsName })
.from(depA)
.except(db.select({ courseName: depB.projectsName }).from(depB));
```
```sql
(select "projects_name" from "department_a_projects")
except
(select "projects_name" from "department_b_projects")
```
```typescript copy
import { int4, cockroachTable, varchar } from "drizzle-orm/cockroach-core";
export const depA = cockroachTable('department_a_projects', {
employeeId: int4('employee_id'),
projectsName: varchar('projects_name').notNull(),
});
export const depB = cockroachTable('department_b_projects', {
employeeId: int4('employee_id'),
projectsName: varchar('projects_name').notNull(),
});
```
### Except All
For two query blocks A and B, return all results from A which are not also present in B, with duplicates.
Let's consider a scenario where you have two tables containing data about customer orders, and you want to
identify products that are exclusively ordered by regular customers (without VIP customers).
In this case, you want to keep track of the quantity of each product, even if it's ordered multiple times by different regular customers.
In this scenario, you want to find products that are exclusively ordered by regular customers and not
ordered by VIP customers. You want to retain the quantity information, even if the same product is ordered multiple times by different regular customers.
```typescript copy
import { exceptAll } from 'drizzle-orm/cockroach-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const regularOrders = db.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);
const vipOrders = db.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);
const result = await exceptAll(regularOrders, vipOrders);
```
```sql
(select "product_id", "quantity_ordered" from "regular_customer_orders")
except all
(select "product_id", "quantity_ordered" from "vip_customer_orders")
```
```ts copy
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const result = await db
.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered,
})
.from(regularCustomerOrders)
.exceptAll(
db
.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered,
})
.from(vipCustomerOrders)
);
```
```sql
(select "product_id", "quantity_ordered" from "regular_customer_orders")
except all
(select "product_id", "quantity_ordered" from "vip_customer_orders")
```
```typescript copy
import { int4, cockroachTable } from "drizzle-orm/cockroach-core";
export const regularCustomerOrders = cockroachTable('regular_customer_orders', {
customerId: int4('customer_id').primaryKey(),
productId: int4('product_id').notNull(),
quantityOrdered: int4('quantity_ordered').notNull(),
});
export const vipCustomerOrders = cockroachTable('vip_customer_orders', {
customerId: int4('customer_id').primaryKey(),
productId: int4('product_id').notNull(),
quantityOrdered: int4('quantity_ordered').notNull(),
});
```
Source: https://orm.drizzle.team/docs/cockroach/sql-schema-declaration
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle schema
Drizzle lets you define a schema in TypeScript with various models and properties supported by the underlying database.
When you define your schema, it serves as the source of truth for future modifications in queries (using Drizzle-ORM)
and migrations (using Drizzle-Kit).
If you are using Drizzle-Kit for the migration process, make sure to export all the models defined in your schema files so that Drizzle-Kit can import them and use them in the migration diff process.
```ts
import { int4, cockroachTable, varchar } from "drizzle-orm/cockroach-core";
export const usersTable = cockroachTable("users", {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: varchar().notNull(),
age: int4().notNull(),
email: varchar().notNull().unique(),
});
```
```ts
import { cockroachTable } from "drizzle-orm/cockroach-core";
export const usersTable = cockroachTable("users", (t) => ({
id: t.int4().primaryKey().generatedAlwaysAsIdentity(),
name: t.varchar().notNull(),
age: t.int4().notNull(),
email: t.varchar().notNull().unique(),
}));
```
```ts
import * as p from "drizzle-orm/cockroach-core";
export const usersTable = p.cockroachTable("users", {
id: p.int4().primaryKey().generatedAlwaysAsIdentity(),
name: p.varchar().notNull(),
age: p.int4().notNull(),
email: p.varchar().notNull().unique(),
});
```
## Organize your schema files
You can declare your SQL schema directly in TypeScript either in a single `schema.ts` file,
or you can spread them around â whichever you prefer, all the freedom!
#### Schema in 1 file
The most common way to declare your schema with Drizzle is to put all your tables into one `schema.ts` file.
> Note: You can name your schema file whatever you like. For example, it could be `models.ts`, or something else.
This approach works well if you don't have too many table models defined, or if you're okay with keeping them all in one file
Example:
```plaintext
đĻ
â đ src
â đ db
â đ schema.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema file. With this configuration, Drizzle will
read from the `schema.ts` file and use this information during the migration generation process. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/cockroach/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'cockroach',
schema: './src/db/schema.ts'
})
```
#### Schema in multiple files
You can place your Drizzle models â such as tables, enums, sequences, etc. â not only in one file but in any file you prefer.
The only thing you must ensure is that you export all the models from those files so that the Drizzle kit can import
them and use them in migrations.
One use case would be to separate each table into its own file.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ countries.ts
â đ cities.ts
â đ products.ts
â đ clients.ts
â đ etc.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema folder. With this configuration, Drizzle will
read from the `schema` folder and find all the files recursively and get all the drizzle tables from there. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/cockroach/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'cockroach',
schema: './src/db/schema'
})
```
You can also group them in any way you like, such as creating groups for user-related tables, messaging-related tables, product-related tables, etc.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ messaging.ts
â đ products.ts
```
## Shape your data schema
Drizzle schema consists of several model types from database you are using. With drizzle you can specify:
- Tables with columns, constraints, etc.
- Schemas
- Enums
- Sequences
- Views
- Materialized Views
- etc.
Let's go one by one and check how the schema should be defined with drizzle
#### **Tables and columns declaration**
A table in Drizzle should be defined with at least 1 column, the same as it should be done in database. There is one important thing to know,
there is no such thing as a common table object in drizzle. You need to choose a dialect you are using, Cockroach, MySQL, SQLite, etc.

```ts copy
import { cockroachTable, int4 } from "drizzle-orm/cockroach-core"
export const users = cockroachTable('users', {
id: int4()
});
```
By default, Drizzle will use the TypeScript key names for columns in database queries.
Therefore, the schema and query from the example will generate the SQL query shown below
This example uses a db object, whose initialization is not covered in this part of the documentation. To learn how to connect to the database, please refer to the [Connections Docs](/docs/cockroach/get-started-cockroach)
\
**TypeScript key = database key**
```ts
// schema.ts
import { int4, cockroachTable, varchar } from "drizzle-orm/cockroach-core";
export const users = cockroachTable('users', {
id: int4(),
first_name: varchar()
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT "id", "first_name" from users;
```
If you want to use different names in your TypeScript code and in the database, you can use column aliases
```ts
// schema.ts
import { int4, cockroachTable, varchar } from "drizzle-orm/cockroach-core";
export const users = cockroachTable('users', {
id: int4(),
firstName: varchar('first_name')
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT "id", "first_name" from users;
```
### Camel and Snake casing
Database model names often use `snake_case` conventions, while in TypeScript, it is common to use `camelCase` for naming models.
This can lead to a lot of alias definitions in the schema. To address this, Drizzle provides a way to automatically
map `camelCase` from TypeScript to `snake_case` in the database via a dedicated builder.
Use the `snakeCase` or `camelCase` builder from `drizzle-orm/cockroach-core` to declare a table whose column keys are
automatically mapped to the chosen database naming convention
```ts {2,4,11,12}
// schema.ts
import { snakeCase, camelCase } from 'drizzle-orm/cockroach-core';
export const users = snakeCase.table('users', {
id: int4().primaryKey(),
fullName: text(), // â full_name in DB
createdAt: timestamp(), // â created_at in DB
});
// Available on all entity types:
snakeCase.table / snakeCase.view / snakeCase.materializedView / snakeCase.schema
camelCase.table / camelCase.view / camelCase.materializedView / camelCase.schema
```
```ts
// db.ts
import { drizzle } from "drizzle-orm/cockroach";
const db = drizzle({ connection: process.env.DATABASE_URL })
```
```ts
// query.ts
await db.select().from(users);
```
```sql
select "id", "full_name", "created_at" from "users"
```
### Advanced
There are a few tricks you can use with Drizzle ORM. As long as Drizzle is entirely in TypeScript files,
you can essentially do anything you would in a simple TypeScript project with your code.
One common feature is to separate columns into different places and then reuse them.
For example, consider the `updated_at`, `created_at`, and `deleted_at` columns. Many tables/models may need these
three fields to track and analyze the creation, deletion, and updates of entities in a system
We can define those columns in a separate file and then import and spread them across all the table objects you have
```ts
// columns.helpers.ts
export const timestamps = {
updated_at: timestamp(),
created_at: timestamp().defaultNow().notNull(),
deleted_at: timestamp(),
}
```
```ts
// users.sql.ts
export const users = cockroachTable('users', {
id: int4(),
...timestamps
})
```
```ts
// posts.sql.ts
export const posts = cockroachTable('posts', {
id: int4(),
...timestamps
})
```
#### **Schemas**
\
In cockroach, there is an entity called a `schema` (which we believe should be called `folders`). This creates a structure in cockroach:

You can manage your cockroach schemas with `cockroachSchema` and place any other models inside it.
Define the schema you want to manage using Drizzle
```ts
import { cockroachSchema } from "drizzle-orm/cockroach-core"
export const customSchema = cockroachSchema('custom');
```
Then place the table inside the schema object
```ts {5-7}
import { int4, cockroachSchema } from "drizzle-orm/cockroach-core";
export const customSchema = cockroachSchema('custom');
export const users = customSchema.table('users', {
id: int4()
})
```
### Example
Once you know the basics, let's define a schema example for a real project to get a better view and understanding
> All examples will use `generateUniqueString`. The implementation for it will be provided after all the schema examples
```ts copy
import type { AnyCockroachColumn } from "drizzle-orm/cockroach-core";
import { cockroachEnum, cockroachTable as table } from "drizzle-orm/cockroach-core";
import * as t from "drizzle-orm/cockroach-core";
export const rolesEnum = cockroachEnum("roles", ["guest", "user", "admin"]);
export const users = table(
"users",
{
id: t.int4().primaryKey().generatedAlwaysAsIdentity(),
firstName: t.varchar("first_name", { length: 256 }),
lastName: t.varchar("last_name", { length: 256 }),
email: t.varchar().notNull(),
invitee: t.int4().references((): AnyCockroachColumn => users.id),
role: rolesEnum().default("guest"),
},
(table) => [
t.uniqueIndex("email_idx").on(table.email)
]
);
export const posts = table(
"posts",
{
id: t.int4().primaryKey().generatedAlwaysAsIdentity(),
slug: t.varchar().$default(() => generateUniqueString(16)),
title: t.varchar({ length: 256 }),
ownerId: t.int4("owner_id").references(() => users.id),
},
(table) => [
t.uniqueIndex("slug_idx").on(table.slug),
t.index("title_idx").on(table.title),
]
);
export const comments = table("comments", {
id: t.int4().primaryKey().generatedAlwaysAsIdentity(),
text: t.varchar({ length: 256 }),
postId: t.int4("post_id").references(() => posts.id),
ownerId: t.int4("owner_id").references(() => users.id),
});
```
**`generateUniqueString` implementation:**
```ts
function generateUniqueString(length: number = 12): string {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let uniqueString = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
uniqueString += characters[randomIndex];
}
return uniqueString;
}
```
#### What's next?
Source: https://orm.drizzle.team/docs/cockroach/sql
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Magical `sql` operator đĒ
When working with an ORM library, there may be cases where you find it challenging to write a
specific query using the provided ORM syntax. In such situations, you can resort to using
raw queries, which involve constructing a query as a raw string. However, raw queries often
lack the benefits of type safety and query parameterization.
To address this, many libraries have introduced the concept of an `sql` template. This template
allows you to write more type-safe and parameterized queries, enhancing the overall safety and
flexibility of your code. Drizzle, being a powerful ORM library, also supports the sql template.
With Drizzle's `sql` template, you can go even further in crafting queries. If you encounter
difficulties in writing an entire query using the library's query builder, you can selectively
use the `sql` template within specific sections of the Drizzle query. This flexibility enables you
to employ the sql template in partial SELECT statements, WHERE clauses, ORDER BY clauses, HAVING
clauses, GROUP BY clauses, and even in relational query builders.
By leveraging the capabilities of the sql template in Drizzle, you can maintain the advantages
of type safety and query parameterization while achieving the desired query structure and complexity.
This empowers you to create more robust and maintainable code within your application.
## sql`` template
One of the most common usages you may encounter in other ORMs as well
is the ability to use `sql` queries as-is for raw queries.
```typescript copy
import { sql } from 'drizzle-orm'
const id = 69;
await db.execute(sql`select * from ${usersTable} where ${usersTable.id} = ${id}`)
```
It will generate the current query
```sql
select * from "users" where "users"."id" = $1; --> [69]
```
Any tables and columns provided to the sql parameter are automatically mapped to their corresponding SQL
syntax with escaped names for tables, and the escaped table names are appended to column names.
Additionally, any dynamic parameters such as `${id}` will be mapped to the $1 placeholder,
and the corresponding values will be moved to an array of values that are passed separately to the database.
This approach effectively prevents any potential SQL Injection vulnerabilities.
## `sql`
Please note that `sql` does not perform any runtime mapping. The type you define using `sql` is
purely a helper for Drizzle. It is important to understand that there is no feasible way to
determine the exact type dynamically, as SQL queries can be highly versatile and customizable.
You can define a custom type in Drizzle to be used in places where fields require a specific type other than `unknown`.
This feature is particularly useful in partial select queries, ensuring consistent typing for selected fields:
```typescript
// without sql type defined
const response: { lowerName: unknown }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
// with sql type defined
const response: { lowerName: string }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
```
## `sql``.mapWith()`
For the cases you need to make a runtime mapping for values passed from database driver to drizzle you can use `.mapWith()`
This function accepts different values, that will map response in runtime.
You can replicate a specific column mapping strategy as long as
the interface inside mapWith is the same interface that is implemented by Column.
```typescript
const usersTable = cockroachTable('users', {
id: int4('id').primaryKey(),
name: string('name').notNull(),
});
// at runtime this values will be mapped same as `text` column is mapped in drizzle
sql`...`.mapWith(usersTable.name);
```
You can also pass your own implementation for the `DriverValueDecoder` interface:
```ts
sql``.mapWith({
mapFromDriverValue: (value: any) => {
const mappedValue = value;
// mapping you want to apply
return mappedValue;
},
});
// or
sql``.mapWith(Number);
```
## `sql``.as()`
In different cases, it can sometimes be challenging to determine how to name a custom field that you want to use.
You may encounter situations where you need to explicitly specify an alias for a
field that will be selected. This can be particularly useful when dealing with complex queries.
To address these scenarios, we have introduced a helpful `.as('alias_name')` helper, which allows
you to define an alias explicitly. By utilizing this feature, you can provide a clear and meaningful
name for the field, making your queries more intuitive and readable.
```typescript
sql`lower(${usersTable.name})`.as('lower_name')
```
```sql
... "users"."name" as lower_name ...
```
## `sql.raw()`
There are cases where you may not need to create parameterized values from input or map tables/columns to escaped ones.
Instead, you might simply want to generate queries as they are. For such situations, we provide the `sql.raw()` function.
The `sql.raw()` function allows you to include raw SQL statements within your queries without any additional processing or escaping.
This can be useful when you have pre-constructed SQL statements or when you need to incorporate complex or dynamic
SQL code directly into your queries.
```typescript
sql.raw(`select * from users where id = ${12}`);
// vs
sql`select * from users where id = ${12}`;
```
```sql
select * from users where id = 12;
--> vs
select * from users where id = $1; --> [12]
```
You can also utilize `sql.raw()` within the sql function, enabling you to include any raw string
without escaping it through the main `sql` template function.
By using `sql.raw()` inside the `sql` function, you can incorporate unescaped raw strings
directly into your queries. This can be particularly useful when you have specific
SQL code or expressions that should remain untouched by the template function's automatic escaping or modification.
```typescript
sql`select * from ${usersTable} where id = ${12}`;
// vs
sql`select * from ${usersTable} where id = ${sql.raw(12)}`;
```
```sql
select * from "users" where id = $1; --> [12]
--> vs
select * from "users" where id = 12;
```
## sql.fromList()
The `sql` template generates sql chunks, which are arrays of SQL parts that will be concatenated
into the query and params after applying the SQL to the database or query in Drizzle.
In certain scenarios, you may need to aggregate these chunks into an array using custom business
logic and then concatenate them into a single SQL statement that can be passed to the database or query.
For such cases, the fromList function can be quite useful.
The fromList function allows you to combine multiple SQL chunks into a single SQL statement.
You can use it to aggregate and concatenate the individual SQL parts according to your specific
requirements and then obtain a unified SQL query that can be executed.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql` or `);
}
const finalSql: SQL = sql.fromList(sqlChunks)
```
```sql
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]
```
## sql.join()
Indeed, the `sql.join` function serves a similar purpose to the fromList helper.
However, it provides additional flexibility when it comes to handling spaces between
SQL chunks or specifying custom separators for concatenating the SQL chunks.
With `sql.join`, you can concatenate SQL chunks together using a specified separator.
This separator can be any string or character that you want to insert between the chunks.
This is particularly useful when you have specific requirements for formatting or delimiting
the SQL chunks. By specifying a custom separator, you can achieve the desired structure and formatting
in the final SQL query.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql`where`);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql`or`);
}
const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
```
```sql
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]
```
## sql.append()
If you have already generated SQL using the `sql` template, you can achieve the same behavior as `fromList`
by using the append function to directly add a new chunk to the generated SQL.
By using the append function, you can dynamically add additional SQL chunks to the existing SQL string,
effectively concatenating them together. This allows you to incorporate custom logic or business
rules for aggregating the chunks into the final SQL query.
```typescript
const finalSql = sql`select * from users`;
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]
```
## sql.empty()
By using sql.empty(), you can start with a blank SQL object and then dynamically append SQL chunks to it as needed. This allows you to construct the SQL query incrementally, applying custom logic or conditions to determine the contents of each chunk.
Once you have initialized the SQL object using sql.empty(), you can take advantage of the full range
of sql template features such as parameterization, composition, and escaping.
This empowers you to construct the SQL query in a flexible and controlled manner,
adapting it to your specific requirements.
```typescript
const finalSql = sql.empty();
// some logic
finalSql.append(sql`select * from users`);
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]
```
## Convert `sql` to string and params
In all the previous examples, you observed the usage of SQL template syntax in TypeScript along with
the generated SQL output.
If you need to obtain the query string and corresponding parameters generated from the SQL template,
you must specify the database dialect you intend to generate the query for. Different databases have
varying syntax for parameterization and escaping, so selecting the appropriate dialect is crucial.
Once you have chosen the dialect, you can utilize the corresponding implementation's functionality
to convert the SQL template into the desired query string and parameter format. This ensures
compatibility with the specific database system you are working with.
```typescript copy
import { CockroachDialect } from 'drizzle-orm/cockroach-core';
const cockroachDialect = new CockroachDialect();
cockroachDialect.sqlToQuery(sql`select * from ${usersTable} where ${usersTable.id} = ${12}`);
```
```sql
select * from "users" where "users"."id" = $1; --> [ 12 ]
```
## `sql` select
You can use the sql functionality in partial select queries as well. Partial select queries allow you to
retrieve specific fields or columns from a table rather than fetching the entire row.
For more detailed information about partial select queries, you can refer to the Core API
documentation available at **[Core API docs](/docs/cockroach/select#basic-and-partial-select)**.
**Select different custom fields from table**
Here you can see a usage for **[`sql`](/docs/cockroach/sql#sqlt)**, **[`sql``.mapWith()`](/docs/cockroach/sql#sqlmapwith)**, **[`sql``.as()`](/docs/cockroach/sql#sqlast)**.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select({
id: usersTable.id,
lowerName: sql`lower(${usersTable.name})`,
aliasedName: sql`lower(${usersTable.name})`.as('aliased_column'),
count: sql`count(*)`.mapWith(Number)
}).from(usersTable)
```
```sql
select "id", lower("name"), lower("name") as "aliased_column", count(*) from "users";
```
## `sql` in where
Indeed, Drizzle provides a set of available expressions that you can use within the sql template.
However, it is true that databases often have a wider range of expressions available,
including those provided through extensions or other means.
To ensure flexibility and enable you to utilize any expressions that are not natively
supported by Drizzle, you have the freedom to write the SQL template
directly using the sql function. This allows you to leverage the full power of
SQL and incorporate any expressions or functionalities specific to your target database.
By using the sql template, you are not restricted to only the predefined expressions in Drizzle.
Instead, you can express complex queries and incorporate any supported expressions that
the underlying database system provides.
**Filtering by `id` but with sql**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
const id = 77
await db.select()
.from(usersTable)
.where(sql`${usersTable.id} = ${id}`)
```
```sql
select * from "users" where "users"."id" = $1; --> [ 77 ]
```
**Advanced fulltext search where statement**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
const searchParam = "Ale"
await db.select()
.from(usersTable)
.where(sql`to_tsvector('simple', ${usersTable.name}) @@ to_tsquery('simple', ${searchParam})`)
```
```sql
select * from "users" where to_tsvector('simple', "users"."name") @@ to_tsquery('simple', '$1'); --> [ "Ale" ]
```
## `sql` in orderBy
The `sql` template can indeed be used in the ORDER BY clause when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select().from(usersTable).orderBy(sql`${usersTable.id} desc nulls first`)
```
```sql
select * from "users" order by "users"."id" desc nulls first;
```
## `sql` in having and groupBy
The `sql` template can indeed be used in the HAVING and GROUP BY clauses when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select({
projectId: usersTable.projectId,
count: sql`count(${usersTable.id})`.mapWith(Number)
}).from(usersTable)
.groupBy(sql`${usersTable.projectId}`)
.having(sql`count(${usersTable.id}) > 300`)
```
```sql
select "project_id", count("id") from "users" group by "users"."project_id" having count("users"."id") > 300
```
Source: https://orm.drizzle.team/docs/cockroach/transactions
# Transactions
SQL transaction is a grouping of one or more SQL statements that interact with a database.
A transaction in its entirety can commit to a database as a single logical unit
or rollback (become undone) as a single logical unit.
Drizzle ORM provides APIs to run SQL statements in transactions:
```ts copy
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
Drizzle ORM supports `savepoints` with nested transactions API:
```ts copy {7-9}
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
await tx.transaction(async (tx2) => {
await tx2.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan"));
});
});
```
You can embed business logic to the transaction and rollback whenever needed:
```ts copy {7}
const db = drizzle(...)
await db.transaction(async (tx) => {
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
if (account.balance < 100) {
// This throws an exception that rollbacks the transaction.
tx.rollback()
}
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
You can return values from the transaction:
```ts copy {8}
const db = drizzle(...)
const newBalance: number = await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
return account.balance;
});
```
You can use transactions with **[relational queries](/docs/cockroach/rqb)**:
```ts
const db = drizzle({ schema })
await db.transaction(async (tx) => {
await tx._query.users.findMany({
with: {
accounts: true
}
});
});
```
We provide dialect-specific transaction configuration APIs:
```ts copy {6-8}
await db.transaction(
async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, "Dan"));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, "Andrew"));
}, {
isolationLevel: "read committed",
accessMode: "read write",
deferrable: true,
}
);
interface CockroachTransactionConfig {
isolationLevel?:
| "read uncommitted"
| "read committed"
| "repeatable read"
| "serializable";
accessMode?: "read only" | "read write";
deferrable?: boolean;
}
```
Source: https://orm.drizzle.team/docs/cockroach/typebox-legacy
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox-legacy
### Install the dependencies
drizzle-orm@rc @sinclair/typebox
### 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/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
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: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views and enums are also supported.
```ts copy
import { cockroachEnum, cockroachView } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const roles = cockroachEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: 'admin' | 'basic' = Value.Parse(rolesSchema, ...);
const usersView = cockroachView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = Value.Parse(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/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = Value.Parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = Value.Parse(userInsertSchema, user); // Will parse successfully
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/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = Value.Parse(userUpdateSchema, user); // Will parse successfully
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 Typebox schema will overwrite it.
```ts copy
import { int4, jsonb, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Type } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
bio: text(),
preferences: jsonb()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => Type.String({ ...schema, maxLength: 20 }), // Extends schema
bio: (schema) => Type.String({ ...schema, maxLength: 1000 }), // Extends schema before becoming nullable/optional
preferences: Type.Object({ theme: Type.String() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = Value.Parse(userSelectSchema, ...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Typebox instance**
```ts copy
import { int4, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSchemaFactory } from 'drizzle-orm/typebox';
import { t } from 'elysia'; // Extended Typebox instance
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const { createInsertSchema } = createSchemaFactory({ typeboxInstance: t });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => t.Number({ ...schema }, { error: '`name` must be a string' })
});
```
### Data type reference
CockroachDB data type mappings follow the CockroachDB column builders. See the [CockroachDB data types](/docs/cockroach/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/cockroach/typebox
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox
#### Install the dependencies
drizzle-orm@rc typebox
Allows you to generate [typebox](https://sinclairzx81.github.io/typebox/#/) schemas from Drizzle ORM schemas
**Features**
- Create a select schema for tables and enums.
- Create insert and update schemas for tables.
- Supported dialect: CockroachDB.
#### Usage
```ts
import { int4, cockroachTable, text, timestamp } from 'drizzle-orm/cockroach-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/typebox';
import { Type } from 'typebox';
import { Value } from 'typebox/value';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
email: text().notNull(),
role: text({ 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) => Type.Number({ ...schema, minimum: 0 }),
role: Type.String(),
});
// Usage
const isUserValid: boolean = Value.Check(insertUserSchema, {
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
```
Source: https://orm.drizzle.team/docs/cockroach/update
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# SQL Update
All the values provided to `.set()`are parameterized automatically.
For example, this query:
```ts
await db.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan"));
```
will be translated to:
```sql
update "users" set "name" = $1 where "users"."name" = $2; -- params: ['Mr. Dan', 'Dan']
```
The object that you pass to `update` should have keys that match column names in your database schema.
Values of `undefined` are ignored in the object: to set a column to `null`, pass `null`.
```typescript copy
await db.update(users)
.set({ name: 'Mr. Dan' })
.where(eq(users.name, 'Dan'));
```
```typescript copy
await db.update(users)
.set({ name: null })
.where(eq(users.name, 'Dan'));
```
You can pass SQL as a value to be used in the update object, like this:
```typescript copy
await db.update(users)
.set({ updatedAt: sql`NOW()` })
.where(eq(users.name, 'Dan'));
```
### Returning
You can update a row and get it back in CockroachDB:
```typescript copy
const updatedUserId = await db.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan")).returning({ updatedId: users.id });
// ^ { updatedId: number | null }[]
```
```sql
update "users" set "name" = 'Mr. Dan' where "users"."name" = 'Dan' returning "id";
```
## `with update` clause
Check how to use WITH statement with [select](/docs/cockroach/select#with-clause), [insert](/docs/cockroach/insert#with-insert-clause), [delete](/docs/cockroach/delete#with-delete-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 averagePrice = db.$with('average_price').as(
db.select({ value: sql`avg(${products.price})`.as('value') }).from(products)
);
const result = await db.with(averagePrice)
.update(products)
.set({
cheap: true
})
.where(lt(products.price, sql`(select * from ${averagePrice})`))
.returning({
id: products.id
});
```
```sql
with "average_price" as (select avg("price") as "value" from "products")
update "products" set "cheap" = true
where "products"."price" < (select * from "average_price")
returning "id"
```
## Update ... from
As CockroachDB documentation states:
> A table expression allowing columns from other tables to appear in the WHERE condition and update expressions
```ts
await db
.update(users)
.set({ cityId: cities.id })
.from(cities)
.where(and(eq(cities.name, 'Seattle'), eq(users.name, 'John')))
```
```sql
update "users" set "city_id" = "cities"."id"
from "cities"
where (("cities"."name" = 'Seattle') and ("users"."name" = 'John'))
```
You can also alias tables that are joined (you can also alias the updating table too).
```ts
const c = alias(cities, 'c');
await db
.update(users)
.set({ cityId: c.id })
.from(c);
```
```sql
update "users" set "city_id" = "c"."id"
from "cities" "c"
```
In Postgres, you can also return columns from the joined tables.
```ts
const updatedUsers = await db
.update(users)
.set({ cityId: cities.id })
.from(cities)
.returning({ id: users.id, cityName: cities.name });
```
```sql
update "users" set "city_id" = "cities"."id"
from "cities"
returning "users"."id", "cities"."name"
```
Source: https://orm.drizzle.team/docs/cockroach/valibot
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# valibot
### Install the dependencies
drizzle-orm@rc valibot
### 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/valibot';
import { parse } from 'valibot';
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: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views and enums are also supported.
```ts copy
import { cockroachEnum, cockroachView } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const roles = cockroachEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: 'admin' | 'basic' = parse(rolesSchema, ...);
const usersView = cockroachView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = parse(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/valibot';
import { parse } from 'valibot';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = parse(userInsertSchema, user); // Will parse successfully
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/valibot';
import { parse } from 'valibot';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = parse(userUpdateSchema, user); // Will parse successfully
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 Valibot schema will overwrite it.
```ts copy
import { int4, jsonb, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse, pipe, maxLength, object, string } from 'valibot';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
bio: text(),
preferences: jsonb()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => pipe(schema, maxLength(20)), // Extends schema
bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = parse(userSelectSchema, ...);
```
### Data type reference
CockroachDB data type mappings follow the CockroachDB column builders. See the [CockroachDB data types](/docs/cockroach/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/cockroach/views
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Views
There're several ways you can declare views with Drizzle ORM.
You can declare views that have to be created or you can declare views that already exist in the database.
You can declare views statements with an inline `query builder` syntax, with `standalone query builder` and with raw `sql` operators.
When views are created with either inlined or standalone query builders, view columns schema will be automatically inferred,
yet when you use `sql` you have to explicitly declare view columns schema.
### Declaring views
```ts filename="schema.ts" copy {14-15}
import { cockroachTable, cockroachView, int4, text, timestamp } from "drizzle-orm/cockroach-core";
import { eq } from "drizzle-orm";
export const user = cockroachTable("user", {
id: int4(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = cockroachView("user_view").as((qb) => qb.select().from(user));
export const customersView = cockroachView("customers_view").as((qb) => qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
CREATE VIEW "customers_view" AS (select ... from "user" where "user"."role" = 'customer');
CREATE VIEW "user_view" AS (select ... from "user");
```
You can also declare views using `standalone query builder`, it works exactly the same way:
```ts filename="schema.ts" copy {4,16-17}
import { cockroachTable, cockroachView, int4, text, timestamp, QueryBuilder} from "drizzle-orm/cockroach-core";
import { eq } from "drizzle-orm";
const qb = new QueryBuilder();
export const user = cockroachTable("user", {
id: int4(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = cockroachView("user_view").as(qb.select().from(user));
export const customersView = cockroachView("customers_view").as(qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
CREATE VIEW "user_view" AS (SELECT ... FROM "user");
CREATE VIEW "customers_view" AS (SELECT ... FROM "user" WHERE "user"."role" = 'customer');
```
### Declaring views with raw SQL
Whenever you need to declare view using a syntax that is not supported by the query builder,
you can directly use `sql` operator and explicitly specify view columns schema.
```ts copy
// regular view
export const newYorkers = cockroachView('new_yorkers', {
id: int4().primaryKey(),
name: text().notNull(),
cityId: integer('city_id').notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);
// materialized view
export const newYorkers = cockroachMaterializedView('new_yorkers', {
id: int4().primaryKey(),
name: text().notNull(),
cityId: integer('city_id').notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);
```
### Declaring existing views
When you're provided with a read only access to an existing view in the database you should use `.existing()` view configuration,
`drizzle-kit` will ignore and will not generate a `create view` statement in the generated migration.
```ts {16,23}
export const user = cockroachTable("user", {
id: int4(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
// regular view
export const trimmedUser = cockroachView("trimmed_user", {
id: int4("id"),
name: text("name"),
email: text("email"),
}).existing();
// materialized view won't make any difference, yet you can use it for consistency
export const trimmedUser = cockroachMaterializedView("trimmed_user", {
id: int4("id"),
name: text("name"),
email: text("email"),
}).existing();
```
### Materialized views
According to the official docs, CockroachDB have both **[`regular`](https://www.cockroachlabs.com/docs/v26.2/create-view.html#synopsis)**
and **[`materialized`](https://www.cockroachlabs.com/docs/v26.2/create-view.html#synopsis)** views.
Materialized views in CockroachDB use the rule system like views do, but persist the results in a table-like form.
{/* This means that when a query is executed against a materialized view, the results are returned directly from the materialized view,
like from a table, rather than being reconstructed by executing the query against the underlying base tables that make up the view. */}
```ts filename="schema.ts" copy
export const newYorkers = cockroachMaterializedView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));
```
```sql
CREATE MATERIALIZED VIEW "new_yorkers" AS (SELECT ... FROM "users" WHERE "users"."city_id" = 1);
```
You can then refresh materialized views in the application runtime:
```ts copy
await db.refreshMaterializedView(newYorkers);
await db.refreshMaterializedView(newYorkers).concurrently();
await db.refreshMaterializedView(newYorkers).withNoData();
```
### Extended example
All the parameters inside the query will be inlined, instead of replaced by `$1`, `$2`, etc.
```ts copy
export const newYorkers = cockroachView('new_yorkers').as((qb) => {
const sq = qb.$with('sq').as(
qb
.select({
userId: users.id.as('users_id'),
cityId: cities.id.as('cities_id'),
homeCity: users.homeCity,
})
.from(users)
.leftJoin(cities, eq(cities.id, users.homeCity))
.where(sql`${users.age1} > 18`),
);
return qb
.with(sq)
.select()
.from(sq)
.where(sql`${sq.homeCity} = 1`);
});
// materialized view
export const newYorkers2 = cockroachMaterializedView('new_yorkers_2')
.withNoData()
.as((qb) => {
const sq = qb.$with('sq').as(
qb
.select({
userId: users.id.as('users_id'),
cityId: cities.id.as('cities_id'),
homeCity: users.homeCity,
})
.from(users)
.leftJoin(cities, eq(cities.id, users.homeCity))
.where(sql`${users.age1} > 18`),
);
return qb
.with(sq)
.select()
.from(sq)
.where(sql`${sq.homeCity} = 1`);
});
```
```sql
...
CREATE VIEW "new_yorkers" AS (
WITH
"sq" AS (
SELECT
"users"."id" AS "users_id",
"cities"."id" AS "cities_id",
"users"."home_city"
FROM
"users"
LEFT JOIN "cities" ON "cities"."id" = "users"."home_city"
WHERE
"users"."age" > 18
)
SELECT
"users_id",
"cities_id",
"home_city"
FROM
"sq"
WHERE
"sq"."home_city" = 1
);
CREATE MATERIALIZED VIEW "new_yorkers_2" AS (
WITH
"sq" AS (
SELECT
"users"."id" AS "users_id",
"cities"."id" AS "cities_id",
"users"."home_city"
FROM
"users"
LEFT JOIN "cities" ON "cities"."id" = "users"."home_city"
WHERE
"users"."age" > 18
)
SELECT
"users_id",
"cities_id",
"home_city"
FROM
"sq"
WHERE
"sq"."home_city" = 1
)
WITH
NO DATA;
```
Source: https://orm.drizzle.team/docs/cockroach/zod
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# zod
### Install the dependencies
drizzle-orm@rc zod
### 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/zod';
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: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Will parse successfully
```
Enums are also supported.
```ts copy
import { cockroachEnum } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/zod';
const roles = cockroachEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: 'admin' | 'basic' = rolesSchema.parse(...);
```
### 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/zod';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Will parse successfully
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/zod';
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema.parse(user); // Will parse successfully
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 Zod schema will overwrite it.
```ts copy
import { int4, jsonb, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSelectSchema } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = cockroachTable('users', {
id: int4().primaryKey(),
name: text().notNull(),
bio: text(),
preferences: jsonb()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => schema.max(20), // Extends schema
bio: (schema) => schema.max(1000), // Extends schema before becoming nullable/optional
preferences: z.object({ theme: z.string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = userSelectSchema.parse(...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Zod instance**
```ts copy
import { int4, cockroachTable, text } from 'drizzle-orm/cockroach-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from '@hono/zod-openapi'; // Extended Zod instance
const users = cockroachTable('users', {
id: int4().primaryKey().generatedAlwaysAsIdentity(),
name: text().notNull(),
age: int4().notNull()
});
const { createInsertSchema } = createSchemaFactory({ zodInstance: z });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => schema.openapi({ example: 'John' })
});
```
**Use case: Type coercion**
```ts copy
import { cockroachTable, timestamp } from 'drizzle-orm/cockroach-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = cockroachTable('users', {
...,
createdAt: timestamp().notNull()
});
const { createInsertSchema } = createSchemaFactory({
// This configuration will only coerce dates. Set `coerce` to `true` to coerce all data types or specify others
coerce: {
date: true
}
});
const userInsertSchema = createInsertSchema(users);
// The above is the same as this:
const userInsertSchema = z.object({
...,
createdAt: z.coerce.date()
});
```
### Data type reference
CockroachDB data type mappings follow the CockroachDB column builders. See the [CockroachDB data types](/docs/cockroach/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/column-types/cockroach
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Npm from '@mdx/Npm.astro';
This page explains concepts available on drizzle versions `1.0.0-beta.2` and higher.
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](/docs/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](/docs/sql-schema-declaration#shape-your-data-schema)
### 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 { bigint, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
bigint: bigint({ mode: 'number' })
});
// will be inferred as `number`
bigint: bigint({ mode: 'number' })
// will be inferred as `bigint`
bigint: bigint({ mode: 'bigint' })
```
```sql
CREATE TABLE "table" (
"bigint" bigint
);
```
```typescript
import { sql } from "drizzle-orm";
import { bigint, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
bigint1: bigint().default(10),
bigint2: bigint().default(sql`'10'::bigint`)
});
```
```sql
CREATE TABLE "table" (
"bigint1" bigint DEFAULT 10,
"bigint2" bigint DEFAULT '10'::bigint
);
```
### smallint
`smallint` `int2`
Small-range signed 2-byte integer
```typescript
import { smallint, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
smallint: smallint()
});
```
```sql
CREATE TABLE "table" (
"smallint" smallint
);
```
```typescript
import { sql } from "drizzle-orm";
import { smallint, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
smallint1: smallint().default(10),
smallint2: smallint().default(sql`'10'::smallint`)
});
```
```sql
CREATE TABLE "table" (
"smallint1" smallint DEFAULT 10,
"smallint2" smallint DEFAULT '10'::smallint
);
```
### int4
Signed 4-byte integer
```typescript
import { int4, cockroachTable } from "drizzle-orm/cockroach-core";
export const table = cockroachTable('table', {
int4: int4()
});
```
```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),
int2: int4().default(sql`'10'::int4`)
});
```
```sql
CREATE TABLE "table" (
"int1" int4 DEFAULT 10,
"int2" int4 DEFAULT '10'::int4
);
```
### int8
An alias of **[bigint.](#bigint)**
### int2
An alias of **[smallint.](#smallint)**
### ---
### bool
Cockroach provides the standard SQL type bool.
For more info please refer to the official Cockroach **[docs.](https://www.cockroachlabs.com/docs/stable/bool)**
```typescript
import { bool, cockroachTable } from "drizzle-orm/cockroach-core";
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.
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.](https://www.cockroachlabs.com/docs/stable/string)**
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.](https://www.cockroachlabs.com/docs/stable/string)**
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" text
);
```
### varchar
`character varying(n)` `varchar(n)`
`STRING` alias used to stay compatible with PostgreSQL
For more info please refer to the official CockroachDB **[docs.](https://www.cockroachlabs.com/docs/stable/string)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to PostgreSQL docs.
```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.](https://www.cockroachlabs.com/docs/stable/string)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to PostgreSQL docs.
```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.](https://www.cockroachlabs.com/docs/stable/decimal)**
```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' }),
});
```
```sql
CREATE TABLE "table" (
"decimal1" decimal,
"decimal2" decimal(100),
"decimal3" decimal(100, 20),
"decimalNum" decimal,
"decimalBig" decimal
);
```
### numeric
An alias of **[decimal.](#decimal)**
### float
`float` `float8` `double precision`
For more info please refer to the official CockroachDB **[docs.](https://www.cockroachlabs.com/docs/stable/float)**
```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`),
});
```
```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.](https://www.cockroachlabs.com/docs/stable/float)**
```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`),
});
```
```sql
CREATE TABLE "table" (
"real1" real,
"real2" real default 10.10,
"real3" real default '10.10'::real
);
```
### double precision
An alias of **[float.](#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.](https://www.cockroachlabs.com/docs/stable/jsonb)**
```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();
// won't compile
jsonb: jsonb().$type().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.](https://www.cockroachlabs.com/docs/stable/bit)**
```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.](https://www.cockroachlabs.com/docs/stable/bit)**
```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.](https://www.cockroachlabs.com/docs/stable/uuid)**
```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.](https://www.cockroachlabs.com/docs/stable/time)**
```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 })
});
```
```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.](https://www.cockroachlabs.com/docs/stable/timestamp)**
```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.](https://www.cockroachlabs.com/docs/stable/date)**
```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" }),
```
### 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)**
```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.](https://www.cockroachlabs.com/docs/stable/enum)**
```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
);
```
## ---
### 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: int().$type().primaryKey(),
jsonField: json().$type(),
});
```
### Identity Columns
To use this feature you would need to have `drizzle-orm@0.32.0` or higher and `drizzle-kit@0.23.0` or higher
PostgreSQL and CockroachDB supports identity columns as a way to automatically generate unique integer 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 { pgTable, integer, text } from 'drizzle-orm/pg-core'
export const ingredients = pgTable("ingredients", {
id: integer().primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }),
name: text().notNull(),
description: text(),
});
```
You can specify all properties available for sequences in the `.generatedAlwaysAsIdentity()` function. Additionally, you can specify custom names for these sequences
PostgreSQL docs [reference](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-IDENTITY).
### 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 { integer, pgTable, uuid } from "drizzle-orm/pg-core";
const table = pgTable('table', {
integer1: integer().default(42),
integer2: integer().default(sql`'42'::integer`),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sql`gen_random_uuid()`),
});
```
```sql
CREATE TABLE IF NOT EXISTS "table" (
"integer1" integer DEFAULT 42,
"integer2" integer DEFAULT '42'::integer,
"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, pgTable } from "drizzle-orm/pg-core";
import { createId } from '@paralleldrive/cuid2';
const table = pgTable('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 { integer, timestamp, text, pgTable } from "drizzle-orm/pg-core";
const table = pgTable('table', {
updateCounter: integer().default(sql`1`).$onUpdateFn((): SQL => sql`${table.update_counter} + 1`),
updatedAt: timestamp({ mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text().$type().$onUpdate(() => null),
});
```
### Not null
`NOT NULL` constraint dictates that the associated column may not contain a `NULL` value.
```typescript
import { integer, pgTable } from "drizzle-orm/pg-core";
const table = pgTable('table', {
integer: integer().notNull(),
});
```
```sql
CREATE TABLE IF NOT EXISTS "table" (
"integer" integer 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 { serial, pgTable } from "drizzle-orm/pg-core";
const table = pgTable('table', {
id: serial().primaryKey(),
});
```
```sql
CREATE TABLE IF NOT EXISTS "table" (
"id" serial PRIMARY KEY NOT NULL
);
```
Source: https://orm.drizzle.team/docs/column-types/mssql
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Npm from '@mdx/Npm.astro';
This page explains concepts available on drizzle versions `1.0.0-beta.2` and higher.
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](/docs/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](/docs/sql-schema-declaration#shape-your-data-schema)
### int
Signed 4-byte integer
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int: int()
});
```
```sql
CREATE TABLE [table] (
[int] int
);
```
```typescript
import { sql } from "drizzle-orm";
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = pgTable('table', {
int1: int().default(10),
});
```
```sql
CREATE TABLE [table] (
[int1] int DEFAULT 10
);
```
### smallint
`smallint`
Small-range signed 2-byte integer
```typescript
import { smallint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
smallint: smallint()
});
```
```sql
CREATE TABLE [table] (
[smallint] smallint
);
```
```typescript
import { sql } from "drizzle-orm";
import { smallint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
smallint1: smallint().default(10),
});
```
```sql
CREATE TABLE [table] (
[smallint1] smallint DEFAULT 10
);
```
### tinyint
`tinyint`
Small-range signed 1-byte integer
```typescript
import { tinyint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
tinyint: tinyint()
});
```
```sql
CREATE TABLE [table] (
[tinyint] tinyint
);
```
```typescript
import { sql } from "drizzle-orm";
import { tinyint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
tinyint1: tinyint().default(10),
});
```
```sql
CREATE TABLE [table] (
[tinyint1] tinyint DEFAULT 10
);
```
### bigint
`bigint`
Signed 8-byte integer
If you're expecting values above 2^31 but below 2^53, you can utilise `mode: 'number'` and deal with javascript number as opposed to bigint.
```typescript
import { bigint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bigint: bigint({ mode: 'number' })
});
// will be inferred as `number`
bigint: bigint({ mode: 'number' })
// will be inferred as `bigint`
bigint: bigint({ mode: 'bigint' })
// will be inferred as `string`
bigint: bigint({ mode: 'string' })
```
```sql
CREATE TABLE [table] (
[bigint] bigint
);
```
```typescript
import { sql } from "drizzle-orm";
import { bigint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bigint1: bigint({ mode: 'number' }).default(10)
});
```
```sql
CREATE TABLE [table] (
[bigint1] bigint DEFAULT 10
);
```
## ---
### bit
An integer data type that can take a value of `1`, `0`, or `NULL`
Drizzle will accept `true` or `false` as values instead of `1` and `0`
```typescript
import { bit, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bit: bit()
});
```
```sql
CREATE TABLE [table] (
[bit] bit
);
```
## ---
### text
`text`
Variable-length non-Unicode data in the code page of the server and with a maximum string length
of 2^31 - 1 (2,147,483,647)
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/ntext-text-and-image-transact-sql?view=sql-server-ver17#text)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { text, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
text: text()
});
// will be inferred as text: "value1" | "value2" | null
text: text({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE [table] (
[text] text
);
```
### ntext
`text`
Variable-length Unicode data with a maximum string length of 2^30 - 1 (1,073,741,823).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/ntext-text-and-image-transact-sql?view=sql-server-ver17#text)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { text, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
ntext: ntext()
});
// will be inferred as text: "value1" | "value2" | null
ntext: ntext({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE [table] (
[ntext] ntext
);
```
### varchar
`varchar(n|max)`
Variable-size string data. Use n to define the string size in bytes and can be a value from 1 through 8,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver17#varchar---n--max--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { varchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
varchar1: varchar(),
varchar2: varchar({ length: 256 }),
varchar3: varchar({ length: 'max' })
});
// will be inferred as text: "value1" | "value2" | null
varchar: varchar({ enum: ["value1", "value2"] }),
```
```sql
CREATE TABLE [table] (
[varchar1] varchar,
[varchar2] varchar(256),
[varchar3] varchar(max)
);
```
### nvarchar
`nvarchar(n|max)`
Variable-size string data. The value of n defines the string size in byte-pairs
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver17#nvarchar---n--max--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { nvarchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
nvarchar1: nvarchar(),
nvarchar2: nvarchar({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
nvarchar: nvarchar({ enum: ["value1", "value2"] }),
// will be inferred as `json`
nvarchar: nvarchar({ mode: 'json' })
```
```sql
CREATE TABLE [table] (
[nvarchar1] nvarchar,
[nvarchar2] nvarchar(256)
);
```
### char
`char(n)`
Fixed-size string data. n defines the string size in bytes and must be a value from 1 through 8,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver17#char---n--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { char, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('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)
);
```
### nchar
`nchar(n)`
Fixed-size string data. n defines the string size in byte-pairs, and must be a value from 1 through 4,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver17#nchar---n--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { nchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
nchar1: nchar(),
nchar2: nchar({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
nchar: nchar({ enum: ["value1", "value2"] }),
```
```sql
CREATE TABLE [table] (
[nchar1] nchar,
[nchar2] nchar(256)
);
```
## ---
### binary
Fixed-length binary data with a length of n bytes, where n is a value from 1 through 8,000. The storage size is n bytes.
```typescript
import { binary, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
binary: binary(),
binary1: binary({ length: 256 })
});
```
```sql
CREATE TABLE [table] (
[binary] binary,
[binary1] binary(256)
);
```
### varbinary
Variable-length binary data. n can be a value from 1 through 8,000. max indicates that the maximum storage size is 2^31-1 bytes
```typescript
import { varbinary, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
varbinary: varbinary(),
varbinary1: varbinary({ length: 256 }),
varbinary2: varbinary({ length: 'max' })
});
```
```sql
CREATE TABLE [table] (
[varbinary] varbinary,
[varbinary1] varbinary(256),
[varbinary2] varbinary(max)
);
```
## ---
### numeric
`numeric`
Fixed precision and scale numbers. When maximum precision is used, valid values are from -10^38 + 1 through 10^38 - 1
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver17#decimal---p---s----and-numeric---p---s---)**
```typescript
import { numeric, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
numeric1: numeric(),
numeric2: numeric({ precision: 100 }),
numeric3: numeric({ precision: 100, scale: 20 })
// numericNum: numeric({ mode: 'number' }),
// numericBig: numeric({ mode: 'bigint' }),
});
```
```sql
CREATE TABLE [table] (
[numeric1] numeric,
[numeric2] numeric(100),
[numeric3] numeric(100, 20)
);
```
### decimal
An alias of **[numeric.](#numeric)**
### real
The ISO synonym for real is float(24).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-ver17)**
```typescript
import { sql } from "drizzle-orm";
import { real, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
real1: real(),
real2: real().default(10.10)
});
```
```sql
CREATE TABLE [table] (
[real1] real,
[real2] real default 10.10
);
```
### float
float [ (n) ] Where n is the number of bits that are used to store the mantissa of the float number in scientific notation and, therefore, dictates the precision and storage size. If n is specified, it must be a value between 1 and 53. The default value of n is 53.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-ver17#syntax)**
```typescript
import { sql } from "drizzle-orm";
import { float, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
float1: float(),
float1: float({ precision: 16 })
});
```
```sql
CREATE TABLE [table] (
[float1] float,
[float2] float(16)
);
```
## ---
### time
`time`
Defines a time of a day. The time is without time zone awareness and is based on a 24-hour clock.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql?view=sql-server-ver17#time-description)**
```typescript
import { time, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
time1: time(),
time2: time({ mode: 'string' }),
time3: time({ precision: 6 }),
time4: time({ precision: 6, mode: 'date' })
});
```
```sql
CREATE TABLE [table] (
[time1] time,
[time2] time,
[time3] time(6),
[time4] time(6)
);
```
### date
`date`
Calendar date (year, month, day)
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/date-transact-sql?view=sql-server-ver17#date-description)**
```typescript
import { date, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('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" }),
```
### datetime
`datetime`
Defines a date that is combined with a time of day with fractional seconds that is based on a 24-hour clock.
Avoid using datetime for new work. Instead, use the time, date, datetime2, and datetimeoffset data types. These types align with the SQL Standard, and are more portable. time, datetime2 and datetimeoffset provide more seconds precision. datetimeoffset provides time zone support for globally deployed applications.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetime-transact-sql?view=sql-server-ver17#description)**
```typescript
import { datetime, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
datetime: datetime(),
});
```
```sql
CREATE TABLE [table] (
[datetime] datetime
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetime: datetime({ mode: "date" }),
// will infer as string
datetime: datetime({ mode: "string" }),
```
### datetime2
`datetime2`
Defines a date that is combined with a time of day that is based on 24-hour clock. datetime2 can be considered as an extension of the existing datetime type that has a larger date range, a larger default fractional precision, and optional user-specified precision.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql?view=sql-server-ver17#datetime2-description)**
```typescript
import { datetime2, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
datetime2: datetime2(),
});
```
```sql
CREATE TABLE [table] (
[datetime2] datetime2
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetime2: datetime2({ mode: "date" }),
// will infer as string
datetime2: datetime2({ mode: "string" }),
```
### datetimeoffset
`datetimeoffset`
Defines a date that is combined with a time of a day based on a 24-hour clock like datetime2, and adds time zone awareness based on Coordinated Universal Time (UTC).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql?view=sql-server-ver17#datetimeoffset-description)**
```typescript
import { datetimeoffset, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
datetimeoffset: datetimeoffset(),
});
```
```sql
CREATE TABLE [table] (
[datetimeoffset] datetimeoffset
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetimeoffset: datetimeoffset({ mode: "date" }),
// will infer as string
datetimeoffset: datetimeoffset({ mode: "string" }),
```
## ---
### 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 = mssqlTable('users', {
id: int().$type().primaryKey(),
jsonField: json().$type(),
});
```
### 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 { int, mssqlTable, text } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
integer: integer().default(42),
text: text().default('text'),
});
```
```sql
CREATE TABLE [table] (
[integer1] integer DEFAULT 42,
[text] text DEFAULT 'text',
);
```
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.
Note: This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`
```ts
import { text, mssqlTable } from "drizzle-orm/mssql-core";
import { createId } from '@paralleldrive/cuid2';
const table = mssqlTable('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 { int, datetime2, text, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
updateCounter: int().default(sql`1`).$onUpdateFn((): SQL => sql`${table.updateCounter} + 1`),
updatedAt: datetime2({ mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text().$type().$onUpdate(() => null),
});
```
### Not null
`NOT NULL` constraint dictates that the associated column may not contain a `NULL` value.
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
const table = mssqlTable('table', {
int: int().notNull(),
});
```
```sql
CREATE TABLE [table] (
[int] int 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 { int, mssqlTable } from "drizzle-orm/mssql-core";
const table = pgTable('table', {
id: int().primaryKey(),
});
```
```sql
CREATE TABLE [table] (
[id] int PRIMARY KEY
);
```
Source: https://orm.drizzle.team/docs/data-querying
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/sql-schema-declaration)
- How to connect to the database - [Connection Fundamentals](/docs/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.
```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
```ts
await db.insert(users).values({ email: 'user@gmail.com' })
```
```sql
INSERT INTO users (email) VALUES ('user@gmail.com')
```
```ts
await db.update(users)
.set({ email: 'user@gmail.com' })
.where(eq(users.id, 1))
```
```sql
UPDATE users
SET email = 'user@gmail.com'
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?
Source: https://orm.drizzle.team/docs/drizzle-config-file
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](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-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: "postgresql",
schema: "./src/schema.ts",
out: "./drizzle",
});
```
```js
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
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: "postgresql",
schema: "./src/schema.ts",
driver: "pglite",
dbCredentials: {
url: "./database/",
},
extensionsFilters: ["postgis"],
schemaFilter: "public",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "public",
},
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: "postgresql",
schema: "./src/schema/*",
out: "./drizzle",
});
```
## ---
### `dialect`
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export`, |
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
});
```
### `schema`
[`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 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",
});
```
### `driver`
Drizzle Kit automatically picks available database driver from your current project based on the provided `dialect`,
yet some vendor specific databases require a different subset of connection params.
`driver` option let's you explicitely pick those exceptions drivers.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `migrate`, `pull`, `studio` |
## ---
### `dbCredentials`
Database connection credentials in a form of `url`,
`user:password@host:port/db` params or exceptions drivers() specific connection options.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
url: "postgres://user:password@host:port/db",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
// via connection params
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
host: "host",
port: 5432,
user: "user",
password: "password",
database: "dbname",
ssl: true, // can be boolean | "require" | "allow" | "prefer" | "verify-full" | options from node:tls
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "mysql",
dbCredentials: {
url: "mysql://user:password@host:port/db",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
// via connection params
export default defineConfig({
dialect: "mysql",
dbCredentials: {
host: "host",
port: 5432,
user: "user",
password: "password",
database: "dbname",
ssl: "...", // can be: string | SslOptions (ssl options from mysql2 package)
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "sqlite",
dbCredentials: {
url: ":memory:", // inmemory database
// or
url: "sqlite.db",
// or
url: "file:sqlite.db" // file: prefix is required by libsql
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "mssql",
dbCredentials: {
url: "mssql://username:password@host:port/database",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
// via connection params
export default defineConfig({
dialect: "mssql",
dbCredentials: {
database: "database",
password: "password",
port: 1433,
server: "server",
user: "user",
options: {
encrypt: true, // boolean
trustServerCertificate: false, // boolean
},
},
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "turso",
dbCredentials: {
url: "libsql://acme.turso.io" // remote Turso database url
authToken: "...",
// or if you need local db
url: ":memory:", // inmemory database
// or
url: "file:sqlite.db", // file: prefix is required by libsql
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "sqlite",
driver: "d1-http",
dbCredentials: {
accountId: "",
databaseId: "",
token: "",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
driver: "aws-data-api",
dbCredentials: {
database: "database",
resourceArn: "resourceArn",
secretArn: "secretArn",
},
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
driver: "pglite",
dbCredentials: {
url: "./database/", // database folder path
}
});
```
### `migrations`
When running `drizzle-kit migrate` - drizzle will records about
successfully applied migrations in your database in log table named `__drizzle_migrations` in `public` schema (schema is used in PostgreSQL only).
`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: "postgresql",
schema: "./src/schema.ts",
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
schema: 'public', // used in PostgreSQL only, `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` |
```ts
import * as p from "drizzle-orm/pg-core"
export const users = p.pgTable("users", {
id: p.serial(),
firstName: p.text("first-name"),
lastName: p.text("LastName"),
email: p.text(),
phoneNumber: p.text("phone_number"),
});
```
```sql
SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) as data_type FROM pg_catalog.pg_attribute a;
```
```
column_name | data_type
---------------+------------------------
id | serial
first-name | text
LastName | text
email | text
phone_number | text
```
```ts
import * as p from "drizzle-orm/pg-core"
export const users = p.pgTable("users", {
id: p.serial(),
"first-name": p.text("first-name"),
LastName: p.text("LastName"),
email: p.text(),
phone_number: p.text("phone_number"),
});
```
```sql
SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) as data_type FROM pg_catalog.pg_attribute a;
```
```
column_name | data_type
---------------+------------------------
id | serial
first-name | text
LastName | text
email | text
phone_number | text
```
## ---
### `tablesFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/goodies#multi-project-schema).
`drizzle-kit push` and `drizzle-kit pull` will by default manage all tables in `public` schema.
You can configure list of tables, schemas and extensions via `tablesFilters`, `schemaFilter` and `extensionFilters` options.
`tablesFilter` 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 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: "postgresql",
tablesFilter: ["users", "posts", "project1_*"],
});
```
### `schemaFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/goodies#multi-project-schema).
`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` |
```ts
export default defineConfig({
dialect: "postgresql",
schemaFilter: ["public", "schema1", "schema2"],
});
```
### `extensionsFilters`
Some extensions like [`postgis`](https://postgis.net/), when installed on the database, create its own tables in public schema.
Those tables have to be ignored by `drizzle-kit push` or `drizzle-kit pull`.
`extensionsFilters` option lets you declare list of installed extensions for drizzle kit to ignore their tables in the schema.
| | |
| :------------ | :----------------- |
| type | `["postgis"]` |
| default | `[]` |
| commands | `push` `pull` |
```ts
export default defineConfig({
dialect: "postgresql",
extensionsFilters: ["postgis"],
});
```
## ---
### `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` `generate` |
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: "postgresql",
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 | `true` |
| commands | `generate` `pull` |
```ts
export default defineConfig({
dialect: "postgresql",
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: "postgresql",
breakpoints: false,
});
```
Source: https://orm.drizzle.team/docs/effect-schema
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
Drizzle+Effect Schema integration is available starting from `drizzle-orm@1.0.0-beta.15`
# effect-schema
Allows you to generate [effect](https://effect.website/) schemas from Drizzle ORM schemas.
**Features**
- Create a select schema for tables, views and enums.
- Create insert and update schemas for tables.
- Supported dialects: CockroachDB, MSSQL, MySQL, PostgreSQL, SingleStore, SQLite.
#### Usage
```ts
import { pgEnum, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/effect-schema';
import { Schema } from 'effect';
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 UserInsert = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const UserUpdate = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const UserSelect = createSelectSchema(users);
// Overriding the fields
const UserInsert = createInsertSchema(users, {
role: Schema.String,
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const UserInsert = createInsertSchema(users, {
id: (schema) => schema.pipe(Schema.greaterThanOrEqualTo(0)),
role: Schema.String,
});
// Usage
const program = Effect.gen(function*() {
const parsedUser = yield* Schema.validate(UserInsert)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
});
```
Source: https://orm.drizzle.team/docs/eslint-plugin
import Tabs from '@mdx/Tabs.astro';
import Tab from '@mdx/Tab.astro';
import Npm from '@mdx/Npm.astro';
# ESLint Drizzle Plugin
For cases where it's impossible to perform type checks for specific scenarios, or where it's possible but error messages would be challenging to understand, we've decided to create an ESLint package with recommended rules. This package aims to assist developers in handling crucial scenarios during development
## Install
eslint-plugin-drizzle
@typescript-eslint/eslint-plugin @typescript-eslint/parser
## Usage
**`.eslintrc.yml` example**
```yml
root: true
parser: '@typescript-eslint/parser'
parserOptions:
project: './tsconfig.json'
plugins:
- drizzle
rules:
'drizzle/enforce-delete-with-where': "error"
'drizzle/enforce-update-with-where': "error"
```
**All config**
This plugin exports an `all` that makes use of all rules (except for deprecated ones).
```yml
root: true
extends:
- "plugin:drizzle/all"
parser: '@typescript-eslint/parser'
parserOptions:
project: './tsconfig.json'
plugins:
- drizzle
```
**Recommended config**
At the moment, `all` is equivalent to `recommended`
```yml
root: true
extends:
- "plugin:drizzle/recommended"
parser: '@typescript-eslint/parser'
parserOptions:
project: './tsconfig.json'
plugins:
- drizzle
```
## Rules
### **enforce-delete-with-where**
Enforce using `delete` with the`.where()` clause in the `.delete()` statement. Most of the time,
you don't need to delete all rows in the table and require some kind of `WHERE` statements.
Optionally, you can define a `drizzleObjectName` in the plugin options that accept a `string` or
`string[]`. This is useful when you have objects or classes with a delete method that's not from
Drizzle. Such a `delete` method will trigger the ESLint rule. To avoid that, you can define the
name of the Drizzle object that you use in your codebase (like db) so that the rule would only
trigger if the delete method comes from this object:
Example, config 1:
```yml
rules:
'drizzle/enforce-delete-with-where': "error"
```
```ts
class MyClass {
public delete() {
return {}
}
}
const myClassObj = new MyClass();
// ---> Will be triggered by ESLint Rule
myClassObj.delete()
const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.delete()
```
Example, config 2:
```yml
rules:
'drizzle/enforce-delete-with-where':
- "error"
- "drizzleObjectName":
- "db"
```
```ts
class MyClass {
public delete() {
return {}
}
}
const myClassObj = new MyClass();
// ---> Will NOT be triggered by ESLint Rule
myClassObj.delete()
const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.delete()
```
### **enforce-update-with-where**:
Enforce using `update` with the`.where()` clause in the `.update()` statement.
Most of the time, you don't need to update all rows in the table and require
some kind of `WHERE` statements.
Optionally, you can define a `drizzleObjectName` in the plugin options that accept
a `string` or `string[]`. This is useful when you have objects or classes with a delete
method that's not from Drizzle. Such as `update` method will trigger the ESLint rule. To
avoid that, you can define the name of the Drizzle object that you use in your codebase (like db)
so that the rule would only trigger if the delete method comes from this object:
Example, config 1:
```yml
rules:
'drizzle/enforce-update-with-where': "error"
```
```ts
class MyClass {
public update() {
return {}
}
}
const myClassObj = new MyClass();
// ---> Will be triggered by ESLint Rule
myClassObj.update()
const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.update()
```
Example, config 2:
```yml
rules:
'drizzle/enforce-update-with-where':
- "error"
- "drizzleObjectName":
- "db"
```
```ts
class MyClass {
public update() {
return {}
}
}
const myClassObj = new MyClass();
// ---> Will NOT be triggered by ESLint Rule
myClassObj.update()
const db = drizzle(...)
// ---> Will be triggered by ESLint Rule
db.update()
```
Source: https://orm.drizzle.team/docs/faq
import Callout from '@mdx/Callout.astro';
# FAQ & Troubleshooting
## **Should I use `generate` or `push`?**
Those are logically 2 different commands. `generate` is used to create an sql file together with additional
information needed for `drizzle-kit` (or any other migration tool).
After generating those migrations, they won't be applied to a database.
You need to do it in the next step. You can read more about it **[here](/docs/migrations)**
On the other hand, `push` doesn't need any migrations to be generated. It will
simply sync your schema with the database schema. Please be careful when using it;
we recommend it only for local development and local databases. To read more about it, check out **[`drizzle-kit push`](/docs/drizzle-kit-push)**
## How `push` and `generate` works for PostgreSQL indexes
### Limitations
1. **You should specify a name for your index manually if you have an index on at least one expression**
Example
```ts
index().on(table.id, table.email) // will work well and name will be autogeneretaed
index('my_name').on(table.id, table.email) // will work well
// but
index().on(sql`lower(${table.email})`) // error
index('my_name').on(sql`lower(${table.email})`) // will work well
```
2. **Push won't generate statements if these fields(list below) were changed in an existing index:**
- expressions inside `.on()` and `.using()`
- `.where()` statements
- operator classes `.op()` on columns
If you are using `push` workflows and want to change these fields in the index, you would need to:
1. Comment out the index
2. Push
3. Uncomment the index and change those fields
4. Push again
For the `generate` command, `drizzle-kit` will be triggered by any changes in the index for any property in the new drizzle indexes API, so there are no limitations here.
Source: https://orm.drizzle.team/docs/get-started-gel
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Gel
- Database [connection basics](/docs/connect-overview) with Drizzle
- gel-js [basics](https://github.com/geldata/gel-js)
Drizzle has native support for Gel connections with the `gel-js` client.
#### Step 1 - Install packages
drizzle-orm@rc gel
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
// Make sure to install the 'gel' package
import { drizzle } from 'drizzle-orm/gel';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript copy
// Make sure to install the 'gel' package
import { drizzle } from "drizzle-orm/gel";
// You can specify any property from the gel connection options
const db = drizzle({
connection: {
dsn: process.env.DATABASE_URL,
tlsSecurity: "default",
},
});
const result = await db.execute("select 1");
```
If you need to provide your existing driver:
```typescript copy
// Make sure to install the 'gel' package
import { drizzle } from "drizzle-orm/gel";
import { createClient } from "gel";
const gelClient = createClient();
const db = drizzle({ client: gelClient });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/get-started
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro';
# Get started with Drizzle
Source: https://orm.drizzle.team/docs/get-started/bun-sql-existing
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import ConnectBun from '@mdx/get-started/postgresql/ConnectBun.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and SQLite in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **bun** - javaScript all-in-one toolkit - [read here](https://bun.sh/)
- **Bun SQL** - native bindings for working with PostgreSQL databases - [read here](https://bun.sh/docs/api/sql)
In version `1.2.0`, Bun has issues with executing concurrent statements, which may lead to errors if you try to run several queries simultaneously.
We've created a [github issue](https://github.com/oven-sh/bun/issues/16774) that you can track. Once it's fixed, you should no longer encounter any such errors on Bun's SQL side
#### Step 1 - Install required packages
drizzle-orm@rc dotenv
-D drizzle-kit@rc @types/bun
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
To run a script with `bun`, use the following command:
```bash copy
bun src/index.ts
```
#### Step 9 - Update your table schema (optional)
#### Step 9 - Applying changes to the database (optional)
#### Step 10 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/bun-sql-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx';
import ConnectBun from '@mdx/get-started/postgresql/ConnectBun.mdx';
# Get Started with Drizzle and Bun:SQLite
- **bun** - javaScript all-in-one toolkit - [read here](https://bun.sh/)
- **Bun SQL** - native bindings for working with PostgreSQL databases - [read here](https://bun.sh/docs/api/sql)
In version `1.2.0`, Bun has issues with executing concurrent statements, which may lead to errors if you try to run several queries simultaneously.
We've created a [github issue](https://github.com/oven-sh/bun/issues/16774) that you can track. Once it's fixed, you should no longer encounter any such errors on Bun's SQL side
#### Step 1 - Install required packages
drizzle-orm@rc
-D drizzle-kit@rc @types/bun
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
To run a script with `bun`, use the following command:
```bash copy
bun src/index.ts
```
Source: https://orm.drizzle.team/docs/get-started/bun-sqlite-existing
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import IntrospectSqlite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import ConnectBun from '@mdx/get-started/sqlite/ConnectBun.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and Bun:SQLite in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **bun** - javaScript all-in-one toolkit - [read here](https://bun.sh/)
- **bun:sqlite** - native implementation of a high-performance SQLite3 driver - [read here](https://bun.sh/docs/api/sqlite)
#### Step 1 - Install required packages
drizzle-orm@rc dotenv
-D drizzle-kit@rc tsx @types/bun
#### Step 2 - Setup connection variables
For example, if you have an SQLite database file in the root of your project, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
To run a script with `bun`, use the following command:
```bash copy
bun src/index.ts
```
#### Step 9 - Update your table schema (optional)
#### Step 9 - Applying changes to the database (optional)
#### Step 10 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/bun-sqlite-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectBun from '@mdx/get-started/sqlite/ConnectBun.mdx';
# Get Started with Drizzle and Bun:SQLite
- **bun** - javaScript all-in-one toolkit - [read here](https://bun.sh/)
- **bun:sqlite** - native implementation of a high-performance SQLite3 driver - [read here](https://bun.sh/docs/api/sqlite)
#### Step 1 - Install required packages
drizzle-orm@rc
-D drizzle-kit@rc @types/bun
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
To run a script with `bun`, use the following command:
```bash copy
bun src/index.ts
```
Source: https://orm.drizzle.team/docs/get-started/cockroach-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectCockroach from '@mdx/get-started/cockroach/IntrospectCockroach.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/cockroach/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/cockroach/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectCockroach from '@mdx/get-started/cockroach/ConnectCockroach.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/cockroach/UpdateSchema.mdx';
# Get Started with Drizzle and CockroachDB in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-postgres** - package for querying your PostgreSQL database - [read here](https://node-postgres.com/)
#### Step 1 - Install **node-postgres** package
drizzle-orm@rc pg dotenv
-D drizzle-kit@rc tsx @types/pg
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/cockroach-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectCockroach from '@mdx/get-started/cockroach/ConnectCockroach.mdx'
import CreateTable from '@mdx/get-started/cockroach/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/cockroach/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/cockroach/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and CockroachDB
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-postgres** - package for querying your Cockroach database - [read here](https://node-postgres.com/)
Drizzle has native support for CockroachDB connections with the `node-postgres` and `postgres.js` drivers.
We will use `node-postgres` for this get started example. But if you want to find more ways to connect to postgresql check
our [CockroachDB Connection](/docs/cockroach/get-started-cockroach) page
#### Step 1 - Install **node-postgres** package
drizzle-orm@rc pg dotenv
-D drizzle-kit@rc tsx @types/pg
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/d1-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectSQLite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectSQLiteCloud from '@mdx/get-started/sqlite/ConnectSQLiteCloud.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and D1 in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Cloudflare D1** - Serverless SQL database to query from your Workers and Pages projects - [read here](https://developers.cloudflare.com/d1/)
- **wrangler** - Cloudflare Developer Platform command-line interface - [read here](https://developers.cloudflare.com/workers/wrangler)
#### Step 1 - Install required package
#### Step 2 - Setup wrangler.toml
You would need to have a `wrangler.toml` file for D1 database and will look something like this:
```toml
name = "YOUR PROJECT NAME"
main = "src/index.ts"
compatibility_date = "2022-11-07"
node_compat = true
[[ d1_databases ]]
binding = "DB"
database_name = "YOUR DB NAME"
database_id = "YOUR DB ID"
migrations_dir = "drizzle"
```
#### Step 3 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
});
```
You can check [our tutorial](/docs/guides/d1-http-with-drizzle-kit) on how to get env variables from CloudFlare
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
```typescript copy
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
: D1Database;
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.);
},
};
```
#### Step 7 - Query the database
```typescript copy
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
: D1Database;
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.);
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!');
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!');
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!');
return Response.json(users);
},
};
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/sqlite-cloud';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle(env.);
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
await db.insert(usersTable).values(user);
console.log('New user created!');
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!');
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!');
return Response.json(users);
}
main();
```
Source: https://orm.drizzle.team/docs/get-started/d1-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryTurso from '@mdx/get-started/sqlite/QueryTurso.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
# Get Started with Drizzle and D1
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Cloudflare D1** - Serverless SQL database to query from your Workers and Pages projects - [read here](https://developers.cloudflare.com/d1/)
- **wrangler** - Cloudflare Developer Platform command-line interface - [read here](https://developers.cloudflare.com/workers/wrangler)
#### Step 1 - Install required packages
#### Step 2 - Setup wrangler.toml
You would need to have a `wrangler.toml` file for D1 database and will look something like this:
```toml
name = "YOUR PROJECT NAME"
main = "src/index.ts"
compatibility_date = "2022-11-07"
node_compat = true
[[ d1_databases ]]
binding = "DB"
database_name = "YOUR DB NAME"
database_id = "YOUR DB ID"
migrations_dir = "drizzle"
```
#### Step 3 - Connect Drizzle ORM to the database
```typescript copy
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
: D1Database;
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.);
},
};
```
#### Step 4 - Generate wrangler types
wrangler types
The output of this command will be a `worker-configuration.d.ts` file.
#### Step 5 - Create a table
#### Step 6 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
});
```
You can check [our tutorial](/docs/guides/d1-http-with-drizzle-kit) on how to get env variables from CloudFlare
#### Step 7 - Applying changes to the database
#### Step 8 - Seed and Query the database
```typescript copy
import { drizzle } from 'drizzle-orm/d1';
export interface Env {
: D1Database;
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.);
const result = await db.select().from(users).all()
return Response.json(result);
},
};
```
#### Step 9 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/do-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectSQLite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectSQLiteCloud from '@mdx/get-started/sqlite/ConnectSQLiteCloud.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and SQLite Durable Objects in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Cloudflare SQLite Durable Objects** - SQLite database embedded within a Durable Object - [read here](https://developers.cloudflare.com/durable-objects/api/sql-storage/)
- **wrangler** - Cloudflare Developer Platform command-line interface - [read here](https://developers.cloudflare.com/workers/wrangler)
#### Step 1 - Install required package
#### Step 2 - Setup wrangler.toml
You would need to have a `wrangler.toml` file for D1 database and will look something like this:
```toml
#:schema node_modules/wrangler/config-schema.json
name = "sqlite-durable-objects"
main = "src/index.ts"
compatibility_date = "2024-11-12"
compatibility_flags = [ "nodejs_compat" ]
# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#durable-objects
[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"
# Durable Object migrations.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#migrations
[[migrations]]
tag = "v1"
new_sqlite_classes = ["MyDurableObject"]
# We need rules so we can import migrations in the next steps
[[rules]]
type = "Text"
globs = ["**/*.sql"]
fallthrough = true
```
#### Step 3 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'sqlite',
driver: 'durable-sqlite',
});
```
You can check [our tutorial](/docs/guides/d1-http-with-drizzle-kit) on how to get env variables from CloudFlare
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
```typescript copy
import { drizzle, type DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
}
}
```
#### Step 7 - Query the database
```typescript copy
import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
import { usersTable } from './db/schema';
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
// Make sure all migrations complete before accepting queries.
// Otherwise you will need to run `this.migrate()` in any function
// that accesses the Drizzle database `this.db`.
ctx.blockConcurrencyWhile(async () => {
await this._migrate();
});
}
async insertAndList(user: typeof usersTable.$inferInsert) {
await this.insert(user);
return this.select();
}
async insert(user: typeof usersTable.$inferInsert) {
await this.db.insert(usersTable).values(user);
}
async select() {
return this.db.select().from(usersTable);
}
async _migrate() {
migrate(this.db, migrations);
}
}
export default {
/**
* This is the standard fetch handler for a Cloudflare Worker
*
* @param request - The request submitted to the Worker from the client
* @param env - The interface to reference bindings declared in wrangler.toml
* @param ctx - The execution context of the Worker
* @returns The response to be sent back to the client
*/
async fetch(request: Request, env: Env): Promise {
const id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName('durable-object');
const stub = env.MY_DURABLE_OBJECT.get(id);
// Option A - Maximum performance.
// Prefer to bundle all the database interaction within a single Durable Object call
// for maximum performance, since database access is fast within a DO.
const usersAll = await stub.insertAndList({
name: 'John',
age: 30,
email: 'john@example.com',
});
console.log('New user created. Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
// Option B - Slow but maybe useful sometimes for debugging.
// You can also directly call individual Drizzle queries if they are exposed
// but keep in mind every query is a round-trip to the Durable Object instance.
await stub.insert({
name: 'John',
age: 30,
email: 'john@example.com',
});
console.log('New user created!');
const users = await stub.select();
console.log('Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
return Response.json(users);
}
}
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
import { usersTable } from './db/schema';
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
// Make sure all migrations complete before accepting queries.
// Otherwise you will need to run `this.migrate()` in any function
// that accesses the Drizzle database `this.db`.
ctx.blockConcurrencyWhile(async () => {
await this._migrate();
});
}
async insertAndList(user: typeof usersTable.$inferInsert) {
await this.insert(user);
return this.select();
}
async insert(user: typeof usersTable.$inferInsert) {
await this.db.insert(usersTable).values(user);
}
async select() {
return this.db.select().from(usersTable);
}
async _migrate() {
migrate(this.db, migrations);
}
}
export default {
/**
* This is the standard fetch handler for a Cloudflare Worker
*
* @param request - The request submitted to the Worker from the client
* @param env - The interface to reference bindings declared in wrangler.toml
* @param ctx - The execution context of the Worker
* @returns The response to be sent back to the client
*/
async fetch(request: Request, env: Env): Promise {
const id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName('durable-object');
const stub = env.MY_DURABLE_OBJECT.get(id);
// Option A - Maximum performance.
// Prefer to bundle all the database interaction within a single Durable Object call
// for maximum performance, since database access is fast within a DO.
const usersAll = await stub.insertAndList({
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
});
console.log('New user created. Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
// Option B - Slow but maybe useful sometimes for debugging.
// You can also directly call individual Drizzle queries if they are exposed
// but keep in mind every query is a round-trip to the Durable Object instance.
await stub.insert({
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
});
console.log('New user created!');
const users = await stub.select();
console.log('Getting all users from the database: ', users);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
return Response.json(users);
}
}
```
Source: https://orm.drizzle.team/docs/get-started/do-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryTurso from '@mdx/get-started/sqlite/QueryTurso.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
# Get Started with Drizzle and SQLite Durable Objects
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Cloudflare SQLite Durable Objects** - SQLite database embedded within a Durable Object - [read here](https://developers.cloudflare.com/durable-objects/api/sql-storage/)
- **wrangler** - Cloudflare Developer Platform command-line interface - [read here](https://developers.cloudflare.com/workers/wrangler)
#### Step 1 - Install required packages
#### Step 2 - Setup wrangler.toml
You would need to have a `wrangler.toml` file for D1 database and will look something like this:
```toml
#:schema node_modules/wrangler/config-schema.json
name = "sqlite-durable-objects"
main = "src/index.ts"
compatibility_date = "2024-11-12"
compatibility_flags = [ "nodejs_compat" ]
# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#durable-objects
[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"
# Durable Object migrations.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#migrations
[[migrations]]
tag = "v1"
new_sqlite_classes = ["MyDurableObject"]
# We need rules so we can import migrations in the next steps
[[rules]]
type = "Text"
globs = ["**/*.sql"]
fallthrough = true
```
#### Step 3 - Connect Drizzle ORM to the database
```ts
import { drizzle, type DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
}
}
```
#### Step 4 - Generate wrangler types
wrangler types
The output of this command will be a `worker-configuration.d.ts` file.
#### Step 5 - Create a table
#### Step 6 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'sqlite',
driver: 'durable-sqlite',
});
```
#### Step 7 - Applying changes to the database
Generate migrations:
```bash copy
npx drizzle-kit generate
```
You can apply migrations only from Cloudflare Workers.
To achieve this, let's define the migrate functionality in MyDurableObject:
```ts copy {4-5,17-19}
import { drizzle, type DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
}
async migrate() {
migrate(this.db, migrations);
}
}
```
#### Step 8 - Migrate and Query the database
```typescript copy
import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
import { usersTable } from './db/schema';
export class MyDurableObject extends DurableObject {
storage: DurableObjectStorage;
db: DrizzleSqliteDODatabase;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.storage = ctx.storage;
this.db = drizzle(this.storage, { logger: false });
// Make sure all migrations complete before accepting queries.
// Otherwise you will need to run `this.migrate()` in any function
// that accesses the Drizzle database `this.db`.
ctx.blockConcurrencyWhile(async () => {
await this._migrate();
});
}
async insertAndList(user: typeof usersTable.$inferInsert) {
await this.insert(user);
return this.select();
}
async insert(user: typeof usersTable.$inferInsert) {
await this.db.insert(usersTable).values(user);
}
async select() {
return this.db.select().from(usersTable);
}
async _migrate() {
migrate(this.db, migrations);
}
}
export default {
/**
* This is the standard fetch handler for a Cloudflare Worker
*
* @param request - The request submitted to the Worker from the client
* @param env - The interface to reference bindings declared in wrangler.toml
* @param ctx - The execution context of the Worker
* @returns The response to be sent back to the client
*/
async fetch(request: Request, env: Env): Promise {
const id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName('durable-object');
const stub = env.MY_DURABLE_OBJECT.get(id);
// Option A - Maximum performance.
// Prefer to bundle all the database interaction within a single Durable Object call
// for maximum performance, since database access is fast within a DO.
const usersAll = await stub.insertAndList({
name: 'John',
age: 30,
email: 'john@example.com',
});
console.log('New user created. Getting all users from the database: ', users);
// Option B - Slow but maybe useful sometimes for debugging.
// You can also directly call individual Drizzle queries if they are exposed
// but keep in mind every query is a round-trip to the Durable Object instance.
await stub.insert({
name: 'John',
age: 30,
email: 'john@example.com',
});
console.log('New user created!');
const users = await stub.select();
console.log('Getting all users from the database: ', users);
return Response.json(users);
}
}
```
Source: https://orm.drizzle.team/docs/get-started/effect-postgresql-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectEffect from '@mdx/get-started/postgresql/ConnectEffect.mdx'
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Effect PostgreSQL in existing project
- **Effect** - Effect is a powerful TS library designed to help developers easily create complex, synchronous, and asynchronous programs. - [read more](https://effect.website/docs)
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **@effect/sql-pg** - A PostgreSQL toolkit for Effect - [read here](https://effect-ts.github.io/effect/docs/sql-pg)
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
If you don't have a PostgreSQL database yet and want to create one for testing, you can use our guide on how to set up PostgreSQL in Docker.
The PostgreSQL in Docker guide is available [here](/docs/guides/postgresql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
```ts
import 'dotenv/config';
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { PgClient } from '@effect/sql-pg';
import * as Effect from 'effect/Effect';
import * as Redacted from 'effect/Redacted';
import { types } from 'pg';
import { eq } from 'drizzle-orm';
import { usersTable } from './db/schema';
const PgClientLive = PgClient.layer({
url: Redacted.make(process.env.DATABASE_URL!),
types: {
getTypeParser: (typeId, format) => {
if ([1184, 1114, 1082, 1186, 1231, 1115, 1185, 1187, 1182].includes(typeId)) {
return (val: any) => val;
}
return types.getTypeParser(typeId, format);
},
},
});
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.makeWithDefaults();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
yield* db.insert(usersTable).values(user);
console.log('New user created!')
const users = yield* db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
yield* db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
yield* db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
});
Effect.runPromise(program.pipe(Effect.provide(PgClientLive)));
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```ts
import 'dotenv/config';
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { PgClient } from '@effect/sql-pg';
import * as Effect from 'effect/Effect';
import * as Redacted from 'effect/Redacted';
import { types } from 'pg';
import { eq } from 'drizzle-orm';
import { usersTable } from './db/schema';
const PgClientLive = PgClient.layer({
url: Redacted.make(process.env.DATABASE_URL!),
types: {
getTypeParser: (typeId, format) => {
if ([1184, 1114, 1082, 1186, 1231, 1115, 1185, 1187, 1182].includes(typeId)) {
return (val: any) => val;
}
return types.getTypeParser(typeId, format);
},
},
});
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.makeWithDefaults();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
yield* db.insert(usersTable).values(user);
console.log('New user created!')
const users = yield* db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
yield* db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
yield* db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
});
Effect.runPromise(program.pipe(Effect.provide(PgClientLive)));
```
Source: https://orm.drizzle.team/docs/get-started/effect-postgresql-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectEffect from '@mdx/get-started/postgresql/ConnectEffect.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Effect PostgreSQL
- **Effect** - Effect is a powerful TS library designed to help developers easily create complex, synchronous, and asynchronous programs. - [read more](https://effect.website/docs)
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **@effect/sql-pg** - A PostgreSQL toolkit for Effect - [read here](https://effect-ts.github.io/effect/docs/sql-pg)
Drizzle has native support for Effect PostgreSQL connections with the `@effect/sql-pg` driver.
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
If you don't have a PostgreSQL database yet and want to create one for testing, you can use our guide on how to set up PostgreSQL in Docker.
The PostgreSQL in Docker guide is available [here](/docs/guides/postgresql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
Let's **update** the `src/index.ts` file with queries to create, read, update, and delete users
```ts
import 'dotenv/config';
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { PgClient } from '@effect/sql-pg';
import * as Effect from 'effect/Effect';
import * as Redacted from 'effect/Redacted';
import { types } from 'pg';
import { eq } from 'drizzle-orm';
import { usersTable } from './db/schema';
const PgClientLive = PgClient.layer({
url: Redacted.make(process.env.DATABASE_URL!),
types: {
getTypeParser: (typeId, format) => {
if ([1184, 1114, 1082, 1186, 1231, 1115, 1185, 1187, 1182].includes(typeId)) {
return (val: any) => val;
}
return types.getTypeParser(typeId, format);
},
},
});
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.makeWithDefaults();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
yield* db.insert(usersTable).values(user);
console.log('New user created!')
const users = yield* db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
yield* db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
yield* db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
});
Effect.runPromise(program.pipe(Effect.provide(PgClientLive)));
```
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/expo-existing
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
# Get Started with Drizzle and Expo in existing project
We don't have a proper guide for getting started with Expo in an existing project, and we believe that
all the information from this [getting started guide](/docs/get-started/expo-new) should be sufficient
Source: https://orm.drizzle.team/docs/get-started/expo-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
# Get Started with Drizzle and Expo
- **Expo SQLite** - A library that provides access to a database that can be queried through a SQLite API - [read here](https://docs.expo.dev/versions/latest/sdk/sqlite/)
#### Step 1 - Setup a project from Expo Template
create expo-app --template blank-typescript
You can read more about this template [here](https://docs.expo.dev/more/create-expo/#create-a-new-project).
#### Basic file structure
After installing the template and adding the `db` folder, you'll find the following content: In the `db/schema.ts` file with drizzle table definitions. The `drizzle` folder contains SQL migration files and snapshots
```plaintext
đĻ
â đ assets
â đ drizzle
â đ db
â â đ schema.ts
â đ .gitignore
â đ .npmrc
â đ app.json
â đ App.tsx
â đ babel.config.ts
â đ drizzle.config.ts
â đ package.json
â đ tsconfig.json
```
#### Step 2 - Install expo-sqlite package
expo install expo-sqlite
#### Step 3 - Install required packages
drizzle-orm@rc
-D drizzle-kit@rc
#### Step 4 - Connect Drizzle ORM to the database
Create a `App.tsx` file in the root directory and initialize the connection:
```ts
import * as SQLite from 'expo-sqlite';
import { drizzle } from 'drizzle-orm/expo-sqlite';
const expo = SQLite.openDatabaseSync('db.db');
const db = drizzle(expo);
```
#### Step 4 - Create a table
Create a `schema.ts` file in the `db` directory and declare your table:
```typescript copy filename="src/db/schema.ts"
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const usersTable = sqliteTable("users_table", {
id: int().primaryKey({ autoIncrement: true }),
name: text().notNull(),
age: int().notNull(),
email: text().notNull().unique(),
});
```
#### Step 5 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'sqlite',
driver: 'expo',
schema: './db/schema.ts',
out: './drizzle',
});
```
#### Step 6 - Setup `metro` config
Create a file `metro.config.js` in root folder and add this code inside:
```js copy filename="metro.config.js"
const { getDefaultConfig } = require('expo/metro-config');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.resolver.sourceExts.push('sql');
module.exports = config;
```
#### Step 7 - Update `babel` config
```js copy filename="babel.config.js"
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [["inline-import", { "extensions": [".sql"] }]] // <-- add this
};
};
```
#### Step 8 - Applying changes to the database
With Expo, you would need to generate migrations using the `drizzle-kit generate` command and then apply them at runtime using the `drizzle-orm` `migrate()` function
Generate migrations:
```bash copy
npx drizzle-kit generate
```
#### Step 9 - Apply migrations and query your db:
Let's **App.tsx** file with migrations and queries to create, read, update, and delete users
```ts copy
import { Text, View } from 'react-native';
import * as SQLite from 'expo-sqlite';
import { useEffect, useState } from 'react';
import { drizzle } from 'drizzle-orm/expo-sqlite';
import { usersTable } from './db/schema';
import { useMigrations } from 'drizzle-orm/expo-sqlite/migrator';
import migrations from './drizzle/migrations';
const expo = SQLite.openDatabaseSync('db.db');
const db = drizzle(expo);
export default function App() {
const { success, error } = useMigrations(db, migrations);
const [items, setItems] = useState(null);
useEffect(() => {
if (!success) return;
(async () => {
await db.delete(usersTable);
await db.insert(usersTable).values([
{
name: 'John',
age: 30,
email: 'john@example.com',
},
]);
const users = await db.select().from(usersTable);
setItems(users);
})();
}, [success]);
if (error) {
return (
Migration error: {error.message}
);
}
if (!success) {
return (
Migration is in progress...
);
}
if (items === null || items.length === 0) {
return (
Empty
);
}
return (
{items.map((item) => (
{item.email}
))}
);
}
```
#### Step 10 - Prebuild and run expo app
```bash copy
npx expo run:ios
```
```bash copy
yarn expo run:ios
```
```bash copy
pnpm expo run:ios
```
```bash copy
bun expo run:ios
```
Source: https://orm.drizzle.team/docs/get-started/gel-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectPostgreSQL from '@mdx/get-started/postgresql/ConnectPostgreSQL.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Gel in existing project
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **gel-js** - package for querying your Gel database - [read here](https://github.com/geldata/gel-js)
Drizzle has native support for Gel connections with the `gel` client.
This is the basic file structure of the project. In the `src` directory, we have table definition in `index.ts`. In `drizzle` folder there are generated Gel to Drizzle schema
```plaintext
đĻ
â đ drizzle
â đ dbschema
â â đ migrations
â â đ default.esdl
â â đ scoping.esdl
â đ src
â â đ index.ts
â đ drizzle.config.ts
â đ edgedb.toml
â đ package.json
â đ tsconfig.json
```
#### Step 1 - Install required packages
drizzle-orm@rc gel
-D drizzle-kit@rc tsx
#### Step 2 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'gel',
});
```
#### Step 3 - Pull Gel types to Drizzle schema
Pull your database schema:
drizzle-kit pull
Here is an example of the generated schema.ts file:
```typescript filename="drizzle/schema.ts"
import { gelTable, uniqueIndex, uuid, smallint, text } from "drizzle-orm/gel-core"
import { sql } from "drizzle-orm"
export const users = gelTable("users", {
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
age: smallint(),
email: text().notNull(),
name: text(),
}, (table) => [
uniqueIndex("a8c6061c-f37f-11ef-9249-0d78f6c1807b;schemaconstr").using("btree", table.id.asc().nullsLast().op("uuid_ops")),
]);
```
#### Step 4 - Connect Drizzle ORM to the database
Create a `index.ts` file in the `src` directory and initialize the connection:
```typescript copy filename="src/index.ts"
import { drizzle } from "drizzle-orm/gel";
import { createClient } from "gel";
const gelClient = createClient();
const db = drizzle({ client: gelClient });
```
#### Step 5 - Query the database
```typescript copy filename="src/index.ts"
import { eq } from "drizzle-orm";
import { drizzle } from "drizzle-orm/gel";
import { createClient } from "gel";
import { users } from "../drizzle/schema";
const gelClient = createClient();
const db = drizzle({ client: gelClient });
async function main() {
const user: typeof users.$inferInsert = {
name: "John",
age: 30,
email: "john@example.com",
};
await db.insert(users).values(user);
console.log("New user created!");
const usersResponse = await db.select().from(users);
console.log("Getting all users from the database: ", usersResponse);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(users)
.set({
age: 31,
})
.where(eq(users.email, user.email));
console.log("User info updated!");
await db.delete(users).where(eq(users.email, user.email));
console.log("User deleted!");
}
main();
```
#### Step 6 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/gel-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectPostgreSQL from '@mdx/get-started/postgresql/ConnectPostgreSQL.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Gel
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **gel-js** - package for querying your Gel database - [read here](https://github.com/geldata/gel-js)
Drizzle has native support for Gel connections with the `gel` client.
This is the basic file structure of the project. In the `src` directory, we have table definition in `index.ts`. In `drizzle` folder there are generated Gel to Drizzle schema
```plaintext
đĻ
â đ drizzle
â đ src
â â đ index.ts
â đ drizzle.config.ts
â đ package.json
â đ tsconfig.json
```
#### Step 1 - Install and init **Gel** project
gel project init
#### Step 2 - Define basic Gel schema
In `dbschema/default.esdl` file add a basic Gel schema
```esdl
module default {
type user {
name: str;
required email: str;
age: int16;
}
}
```
#### Step 3 - Push Gel schema to the database
Generate Gel migration file:
```bash
gel migration create
```
Apply Gel migrations to the database
```bash
gel migration apply
```
Now you should have this file structure
```plaintext
đĻ
â đ dbschema
â â đ migrations
â â đ default.esdl
â â đ scoping.esdl
â đ src
â â đ index.ts
â đ drizzle.config.ts
â đ edgedb.toml
â đ package.json
â đ tsconfig.json
```
#### Step 4 - Install required packages
drizzle-orm@rc gel
-D drizzle-kit@rc tsx
#### Step 5 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'gel',
});
```
#### Step 6 - Pull Gel types to Drizzle schema
Pull your database schema:
drizzle-kit pull
Here is an example of the generated schema.ts file:
```typescript filename="drizzle/schema.ts"
import { gelTable, uniqueIndex, uuid, smallint, text } from "drizzle-orm/gel-core"
import { sql } from "drizzle-orm"
export const users = gelTable("users", {
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
age: smallint(),
email: text().notNull(),
name: text(),
}, (table) => [
uniqueIndex("a8c6061c-f37f-11ef-9249-0d78f6c1807b;schemaconstr").using("btree", table.id.asc().nullsLast().op("uuid_ops")),
]);
```
#### Step 7 - Connect Drizzle ORM to the database
Create a `index.ts` file in the `src` directory and initialize the connection:
```typescript copy filename="src/index.ts"
import { drizzle } from "drizzle-orm/gel";
import { createClient } from "gel";
const gelClient = createClient();
const db = drizzle({ client: gelClient });
```
#### Step 8 - Query the database
```typescript copy filename="src/index.ts"
import { eq } from "drizzle-orm";
import { drizzle } from "drizzle-orm/gel";
import { createClient } from "gel";
import { users } from "../drizzle/schema";
const gelClient = createClient();
const db = drizzle({ client: gelClient });
async function main() {
const user: typeof users.$inferInsert = {
name: "John",
age: 30,
email: "john@example.com",
};
await db.insert(users).values(user);
console.log("New user created!");
const usersResponse = await db.select().from(users);
console.log("Getting all users from the database: ", usersResponse);
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(users)
.set({
age: 31,
})
.where(eq(users.email, user.email));
console.log("User info updated!");
await db.delete(users).where(eq(users.email, user.email));
console.log("User deleted!");
}
main();
```
#### Step 9 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/mssql-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectMSSQL from '@mdx/get-started/mssql/IntrospectMSSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/mssql/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/mssql/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/mssql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectMSSQL from '@mdx/get-started/mssql/ConnectMSSQL.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/mssql/UpdateSchema.mdx';
# Get Started with Drizzle and MSSQL in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-mssql** - package for querying your MSSQL database - [read here](https://github.com/tediousjs/node-mssql)
#### Step 1 - Install **mssql** package
drizzle-orm@rc mssql dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/mssql-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectMSSQL from '@mdx/get-started/mssql/ConnectMSSQL.mdx'
import CreateTable from '@mdx/get-started/mssql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/mssql/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/mssql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and MSSQL
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-mssql** - package for querying your MSSQL database - [read here](https://github.com/tediousjs/node-mssql)
Drizzle has native support for PostgreSQL connections with the `node-mssql` driver.
#### Step 1 - Install **mssql** package
drizzle-orm@rc mssql dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
{/*
If you don't have a PostgreSQL database yet and want to create one for testing, you can use our guide on how to set up PostgreSQL in Docker.
The PostgreSQL in Docker guide is available [here](/docs/guides/postgresql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
*/}
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/mysql-existing
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectMySQL from '@mdx/get-started/mysql/ConnectMySQL.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import UpdateSchema from '@mdx/get-started/mysql/UpdateSchema.mdx';
import IntrospectMySQL from '@mdx/get-started/mysql/IntrospectMySQL.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
# Get Started with Drizzle and MySQL in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **mysql2** - package for querying your MySQL database - [read here](https://github.com/sidorares/node-mysql2)
#### Step 1 - Install the `mysql2` package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/mysql-new
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectMySQL from '@mdx/get-started/mysql/ConnectMySQL.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
# Get Started with Drizzle and MySQL
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **mysql2** - package for querying your MySQL database - [read here](https://github.com/sidorares/node-mysql2)
To use Drizzle with a MySQL database, you should use the `mysql2` driver
According to the **[official website](https://github.com/sidorares/node-mysql2)**,
`mysql2` is a MySQL client for Node.js with focus on performance.
Drizzle ORM natively supports `mysql2` with `drizzle-orm/mysql2` package.
#### Step 1 - Install **mysql2** package
#### Step 2 - Setup connection variables
If you don't have a MySQL database yet and want to create one for testing, you can use our guide on how to set up MySQL in Docker.
The MySQL in Docker guide is available [here](/docs/guides/mysql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/neon-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectNeon from '@mdx/get-started/postgresql/ConnectNeon.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Neon in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Neon** - serverless Postgres platform - [read here](https://neon.tech/docs/introduction)
#### Step 1 - Install **@neondatabase/serverless** package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/neon-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectNeon from '@mdx/get-started/postgresql/ConnectNeon.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Neon
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Neon** - serverless Postgres platform - [read here](https://neon.tech/docs/introduction)
Drizzle has native support for Neon connections with the `neon-http` and `neon-websockets` drivers. These use the **neon-serverless** driver under the hood.
With the `neon-http` and `neon-websockets` drivers, you can access a Neon database from serverless environments over HTTP or WebSockets instead of TCP. Querying over HTTP is faster for single, non-interactive transactions.
If you need session or interactive transaction support, or a fully compatible drop-in replacement for the `pg` driver, you can use the WebSocket-based `neon-serverless` driver. You can connect to a Neon database directly using [Postgres](/docs/get-started/postgresql-new)
#### Step 1 - Install **@neondatabase/serverless** package
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/nile-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectNile from '@mdx/get-started/postgresql/ConnectNile.mdx'
import QueryNile from '@mdx/get-started/postgresql/QueryNile.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Nile in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Nile** - PostgreSQL re-engineered for multi-tenant apps - [read here](https://thenile.dev/)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
Drizzle Kit provides a CLI command to introspect your database and generate a schema file with migrations. The schema file contains all the information about your database tables, columns, relations, and indices.
For example, you have such table in your database:
```sql copy
CREATE TABLE IF NOT EXISTS "todos" (
"id" uuid DEFAULT gen_random_uuid(),
"tenant_id" uuid,
"title" varchar(256),
"estimate" varchar(256),
"embedding" vector(3),
"complete" boolean
);
```
Pull your database schema:
```bash copy
npx drizzle-kit pull
```
The result of introspection will be a `schema.ts` file, `meta` folder with snapshots of your database schema, sql file with the migration and `relations.ts` file for [relational queries](/docs/rqb).
Nile has several built-in tables that are part of every database. When you introspect a Nile database, the built-in tables will be included.
For example, the `tenants` table that you see in the example below. This will allow you to easily create new tenants, list tenants and other operations.
Here is an example of the generated `schema.ts` file:
```typescript copy filename="src/db/schema.ts"
// table schema generated by introspection
import { pgTable, uuid, text, timestamp, varchar, vector, boolean } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
export const tenants = pgTable("tenants", {
id: uuid().default(sql`public.uuid_generate_v7()`).primaryKey().notNull(),
name: text(),
created: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
updated: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
deleted: timestamp({ mode: 'string' }),
});
export const todos = pgTable("todos", {
id: uuid().defaultRandom(),
tenantId: uuid("tenant_id"),
title: varchar({ length: 256 }),
estimate: varchar({ length: 256 }),
embedding: vector({ dimensions: 3 }),
complete: boolean(),
});
```
Learn more about introspection in the [documentation](/docs/drizzle-kit-pull).
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
If you want to update your table schema, you can do it in the `schema.ts` file. For example, let's add a new column `deadline` to the `todos` table`:
```typescript copy filename="src/db/schema.ts" {19}
import { pgTable, uuid, text, timestamp, varchar, vector, boolean } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
export const tenants = pgTable("tenants", {
id: uuid().default(sql`public.uuid_generate_v7()`).primaryKey().notNull(),
name: text(),
created: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
updated: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
deleted: timestamp({ mode: 'string' }),
});
export const todos = pgTable("todos", {
id: uuid().defaultRandom(),
tenantId: uuid("tenant_id"),
title: varchar({ length: 256 }),
estimate: varchar({ length: 256 }),
embedding: vector({ dimensions: 3 }),
complete: boolean(),
deadline: timestamp({ mode: 'string' })
});
```
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
If you run the `index.ts` file again, you'll be able to see the new field that you've just added.
The field will be `null` since we did not populate deadlines when inserting todos previously.
Source: https://orm.drizzle.team/docs/get-started/nile-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectNile from '@mdx/get-started/postgresql/ConnectNile.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryNile from '@mdx/get-started/postgresql/QueryNile.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Nile
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Nile** - PostgreSQL re-engineered for multi-tenant apps - [read here](https://thenile.dev/)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
Create a `schema.ts` file in the `src/db` directory and declare your tables. Since Nile is Postgres for multi-tenant apps, our schema includes a table for tenants and a todos table with a `tenant_id` column (we refer to those as tenant-aware tables):
```typescript copy filename="src/db/schema.ts"
import { pgTable, uuid, text, timestamp, varchar, vector, boolean } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
export const tenantsTable = pgTable("tenants", {
id: uuid().default(sql`public.uuid_generate_v7()`).primaryKey().notNull(),
name: text(),
created: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
updated: timestamp({ mode: 'string' }).default(sql`LOCALTIMESTAMP`).notNull(),
deleted: timestamp({ mode: 'string' }),
});
export const todos = pgTable("todos", {
id: uuid().defaultRandom(),
tenantId: uuid("tenant_id"),
title: varchar({ length: 256 }),
estimate: varchar({ length: 256 }),
embedding: vector({ dimensions: 3 }),
complete: boolean(),
});
```
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/node-sqlite-existing
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import IntrospectSqlite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import ConnectNode from '@mdx/get-started/sqlite/ConnectNode.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and Node:SQLite in existing project
- **node v22.5.0+** - JavaScript runtime - [read here](https://nodejs.org/)
- **node:sqlite** - native implementation of a high-performance SQLite3 driver - [read here](https://nodejs.org/api/sqlite.html)
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.is/)
#### Step 1 - Install required packages
drizzle-orm@rc dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
For example, if you have an SQLite database file in the root of your project, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 9 - Applying changes to the database (optional)
#### Step 10 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/node-sqlite-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectNode from '@mdx/get-started/sqlite/ConnectNode.mdx';
# Get Started with Drizzle and Node:SQLite
- **node v22.5.0+** - JavaScript runtime - [read here](https://nodejs.org/)
- **node:sqlite** - native implementation of a high-performance SQLite3 driver - [read here](https://nodejs.org/api/sqlite.html)
#### Step 1 - Install required packages
drizzle-orm@rc dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/op-sqlite-existing
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
# Get Started with Drizzle and OP-SQLite in existing project
We don't have a proper guide for getting started with OP-SQLite in an existing project, and we believe that
all the information from this [getting started guide](/docs/get-started/op-sqlite-new) should be sufficient
Source: https://orm.drizzle.team/docs/get-started/op-sqlite-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
# Get Started with Drizzle and OP-SQLite
- **OP-SQLite** - SQLite library for react-native - [read here](https://github.com/OP-Engineering/op-sqlite)
#### Step 1 - Setup a project from Expo Template
create expo-app --template blank-typescript
You can read more about this template [here](https://docs.expo.dev/more/create-expo/#create-a-new-project).
#### Basic file structure
After installing the template and adding the `db` folder, you'll find the following content: In the `db/schema.ts` file with drizzle table definitions. The `drizzle` folder contains SQL migration files and snapshots
```plaintext
đĻ
â đ assets
â đ drizzle
â đ db
â â đ schema.ts
â đ .gitignore
â đ .npmrc
â đ app.json
â đ App.tsx
â đ babel.config.ts
â đ drizzle.config.ts
â đ package.json
â đ tsconfig.json
```
#### Step 2 - Install required packages
drizzle-orm@rc @op-engineering/op-sqlite
-D drizzle-kit@rc
#### Step 3 - Connect Drizzle ORM to the database
Create a `App.tsx` file in the root directory and initialize the connection:
```ts
import { open } from '@op-engineering/op-sqlite';
import { drizzle } from 'drizzle-orm/op-sqlite';
const opsqliteDb = open({
name: 'db',
});
const db = drizzle(opsqliteDb);
```
#### Step 4 - Create a table
Create a `schema.ts` file in the `db` directory and declare your table:
```typescript copy filename="src/db/schema.ts"
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const usersTable = sqliteTable("users_table", {
id: int().primaryKey({ autoIncrement: true }),
name: text().notNull(),
age: int().notNull(),
email: text().notNull().unique(),
});
```
#### Step 5 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'sqlite',
driver: 'expo',
schema: './db/schema.ts',
out: './drizzle',
});
```
#### Step 6 - Setup `metro` config
Create a file `metro.config.js` in root folder and add this code inside:
```js copy filename="metro.config.js"
const { getDefaultConfig } = require('expo/metro-config');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.resolver.sourceExts.push('sql');
module.exports = config;
```
#### Step 7 - Update `babel` config
```js copy filename="babel.config.js"
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [["inline-import", { "extensions": [".sql"] }]] // <-- add this
};
};
```
#### Step 8 - Applying changes to the database
With Expo, you would need to generate migrations using the `drizzle-kit generate` command and then apply them at runtime using the `drizzle-orm` `migrate()` function
Generate migrations:
```bash copy
npx drizzle-kit generate
```
#### Step 9 - Apply migrations and query your db:
Let's **App.tsx** file with migrations and queries to create, read, update, and delete users
```ts copy
import { Text, View } from 'react-native';
import { open } from '@op-engineering/op-sqlite';
import { useEffect, useState } from 'react';
import { drizzle } from 'drizzle-orm/op-sqlite';
import { usersTable } from './db/schema';
import { useMigrations } from 'drizzle-orm/op-sqlite/migrator';
import migrations from './drizzle/migrations';
const opsqliteDb = open({
name: 'db',
});
const db = drizzle(opsqliteDb);
export default function App() {
const { success, error } = useMigrations(db, migrations);
const [items, setItems] = useState(null);
useEffect(() => {
if (!success) return;
(async () => {
await db.delete(usersTable);
await db.insert(usersTable).values([
{
name: 'John',
age: 30,
email: 'john@example.com',
},
]);
const users = await db.select().from(usersTable);
setItems(users);
})();
}, [success]);
if (error) {
return (
Migration error: {error.message}
);
}
if (!success) {
return (
Migration is in progress...
);
}
if (items === null || items.length === 0) {
return (
Empty
);
}
return (
{items.map((item) => (
{item.email}
))}
);
}
```
#### Step 10 - Prebuild and run expo app
```bash copy
npx expo run:ios
```
```bash copy
yarn expo run:ios
```
```bash copy
pnpm expo run:ios
```
```bash copy
bun expo run:ios
```
Source: https://orm.drizzle.team/docs/get-started/pglite-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectPgLite from '@mdx/get-started/postgresql/ConnectPgLite.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and PGLite in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **ElectricSQL** - [website](https://electric-sql.com/)
- **pglite driver** - [docs](https://pglite.dev/) & [GitHub](https://github.com/electric-sql/pglite)
#### Step 1 - Install all needed packages
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/pglite-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectPgLite from '@mdx/get-started/postgresql/ConnectPgLite.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and PGlite
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **ElectricSQL** - [website](https://electric-sql.com/)
- **pglite driver** - [docs](https://pglite.dev/) & [GitHub](https://github.com/electric-sql/pglite)
#### Step 1 - Install all needed packages
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/planetscale-existing
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectPlanetScale from '@mdx/get-started/mysql/ConnectPlanetScale.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import UpdateSchema from '@mdx/get-started/mysql/UpdateSchema.mdx';
import IntrospectMySQL from '@mdx/get-started/mysql/IntrospectMySQL.mdx';
import QueryPlanetScale from '@mdx/get-started/mysql/QueryPlanetScale.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
# Get Started with Drizzle and PlanetScale in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io)
- **PlanetScale** - MySQL database platform - [read here](https://planetscale.com)
- **database-js** - PlanetScale serverless driver - [read here](https://github.com/planetscale/database-js)
Looking for PostgreSQL? Check out our [PlanetScale Postgres guide](/docs/get-started/planetscale-postgres-existing)
For this tutorial, we will use the `database-js` driver to make **HTTP** calls to the PlanetScale database. If you need to
connect to PlanetScale through TCP, you can refer to our [MySQL Get Started](/docs/get-started/mysql-new) page
#### Step 1 - Install **@planetscale/database** package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/planetscale-serverless';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle({ connection: {
host: process.env.DATABASE_HOST!,
username: process.env.DATABASE_USERNAME!,
password: process.env.DATABASE_PASSWORD!,
}});
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
Source: https://orm.drizzle.team/docs/get-started/planetscale-new
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectPlanetScale from '@mdx/get-started/mysql/ConnectPlanetScale.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import QueryPlanetScale from '@mdx/get-started/mysql/QueryPlanetScale.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
# Get Started with Drizzle and PlanetScale
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io)
- **PlanetScale** - MySQL database platform - [read here](https://planetscale.com)
- **database-js** - PlanetScale serverless driver - [read here](https://github.com/planetscale/database-js)
Looking for PostgreSQL? Check out our [PlanetScale Postgres guide](/docs/get-started/planetscale-postgres-new)
For this tutorial, we will use the `database-js` driver to make **HTTP** calls to the PlanetScale database. If you need to
connect to PlanetScale through TCP, you can refer to our [MySQL Get Started](/docs/get-started/mysql-new) page
#### Step 1 - Install **@planetscale/database** package
#### Step 2 - Setup connection variables
Create a `.env` file in the root of your project and add your database connection variable:
```plaintext copy
DATABASE_HOST=
DATABASE_USERNAME=
DATABASE_PASSWORD=
```
To get all the necessary environment variables to connect through the `database-js` driver, you can check the [PlanetScale docs](https://planetscale.com/docs/tutorials/planetscale-serverless-driver-node-example#use-the-sample-repository)
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/planetscale-postgres-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectPlanetScalePostgres from '@mdx/get-started/postgresql/ConnectPlanetScalePostgres.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and PlanetScale Postgres in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **PlanetScale Postgres** - PostgreSQL database platform - [read here](https://planetscale.com/docs/postgres)
- **node-postgres** - package for querying your PostgreSQL database - [read here](https://node-postgres.com/)
Looking for MySQL? Check out our [PlanetScale MySQL guide](/docs/get-started/planetscale-existing)
PlanetScale offers both MySQL (Vitess) and PostgreSQL databases. This guide covers connecting to PlanetScale Postgres using the standard `node-postgres` driver.
For detailed instructions on creating a PlanetScale Postgres database and obtaining credentials, see the [PlanetScale Postgres documentation](https://planetscale.com/docs/postgres/tutorials/planetscale-postgres-drizzle).
#### Step 1 - Install **node-postgres** package
#### Step 2 - Setup connection variables
Create a `.env` file in the root of your project and add your database connection variable:
```plaintext copy
DATABASE_URL=postgresql://{username}:{password}@{host}:{port}/postgres?sslmode=verify-full
```
You can obtain your connection credentials from the PlanetScale dashboard by navigating to your database, clicking **"Connect"**, and creating a **"Default role"**. See the [PlanetScale connection guide](https://planetscale.com/docs/postgres/tutorials/planetscale-postgres-drizzle#create-credentials-and-connect) for detailed steps.
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/planetscale-postgres-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectPlanetScalePostgres from '@mdx/get-started/postgresql/ConnectPlanetScalePostgres.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and PlanetScale Postgres
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **PlanetScale Postgres** - PostgreSQL database platform - [read here](https://planetscale.com/docs/postgres)
- **node-postgres** - package for querying your PostgreSQL database - [read here](https://node-postgres.com/)
Looking for MySQL? Check out our [PlanetScale MySQL guide](/docs/get-started/planetscale-new)
PlanetScale offers both MySQL (Vitess) and PostgreSQL databases. This guide covers connecting to PlanetScale Postgres using the standard `node-postgres` driver.
For detailed instructions on creating a PlanetScale Postgres database and obtaining credentials, see the [PlanetScale Postgres documentation](https://planetscale.com/docs/postgres/tutorials/planetscale-postgres-drizzle).
#### Step 1 - Install **node-postgres** package
#### Step 2 - Setup connection variables
Create a `.env` file in the root of your project and add your database connection variable:
```plaintext copy
DATABASE_URL=postgresql://{username}:{password}@{host}:{port}/postgres?sslmode=verify-full
```
You can obtain your connection credentials from the PlanetScale dashboard by navigating to your database, clicking **"Connect"**, and creating a **"Default role"**. See the [PlanetScale connection guide](https://planetscale.com/docs/postgres/tutorials/planetscale-postgres-drizzle#create-credentials-and-connect) for detailed steps.
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/postgresql-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectPostgreSQL from '@mdx/get-started/postgresql/ConnectPostgreSQL.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and PostgreSQL in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-postgres** - package for querying your PostgreSQL database - [read here](https://node-postgres.com/)
#### Step 1 - Install **node-postgres** package
#### Step 2 - Setup connection variables
If you don't have a PostgreSQL database yet and want to create one for testing, you can use our guide on how to set up PostgreSQL in Docker.
The PostgreSQL in Docker guide is available [here](/docs/guides/postgresql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/postgresql-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectPostgreSQL from '@mdx/get-started/postgresql/ConnectPostgreSQL.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and PostgreSQL
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **node-postgres** - package for querying your PostgreSQL database - [read here](https://node-postgres.com/)
Drizzle has native support for PostgreSQL connections with the `node-postgres` and `postgres.js` drivers.
We will use `node-postgres` for this get started example. But if you want to find more ways to connect to postgresql check
our [PostgreSQL Connection](/docs/get-started-postgresql) page
#### Step 1 - Install **node-postgres** package
#### Step 2 - Setup connection variables
If you don't have a PostgreSQL database yet and want to create one for testing, you can use our guide on how to set up PostgreSQL in Docker.
The PostgreSQL in Docker guide is available [here](/docs/guides/postgresql-local-setup). Go set it up, generate a database URL (explained in the guide), and come back for the next steps
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/singlestore-existing
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectSingleStore from '@mdx/get-started/singlestore/ConnectSingleStore.mdx';
import UpdateSchema from '@mdx/get-started/singlestore/UpdateSchema.mdx';
import IntrospectSingleStore from '@mdx/get-started/singlestore/IntrospectSingleStore.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import ApplyChanges from '@mdx/get-started/singlestore/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/singlestore/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
# Get Started with Drizzle and SingleStore in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **mysql2** - package for querying your SingleStore database - [read here](https://github.com/sidorares/node-mysql2)
#### Step 1 - Install the `mysql2` package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/singlestore-new
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectSingleStore from '@mdx/get-started/singlestore/ConnectSingleStore.mdx';
import CreateSingleStoreTable from '@mdx/get-started/singlestore/CreateSingleStoreTable.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import ApplyChanges from '@mdx/get-started/singlestore/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/singlestore/SetupConfig.mdx';
# Get Started with Drizzle and SingleStore
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **mysql2** - package for querying your MySQL database - [read here](https://github.com/sidorares/node-mysql2)
To use Drizzle with a SingleStore database, you should use the `singlestore` driver
According to the **[official website](https://github.com/sidorares/node-mysql2)**,
`mysql2` is a MySQL client for Node.js with focus on performance.
Drizzle ORM natively supports `mysql2` with `drizzle-orm/singlestore` package for SingleStore database.
#### Step 1 - Install **mysql2** package
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/sqlite-cloud-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectSQLite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectSQLiteCloud from '@mdx/get-started/sqlite/ConnectSQLiteCloud.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and SQLite Cloud in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **SQLite Cloud database** - [read here](https://docs.sqlitecloud.io/docs/overview)
- **SQLite Cloud driver** - [read here](https://docs.sqlitecloud.io/docs/sdk-js-introduction) & [GitHub](https://github.com/sqlitecloud/sqlitecloud-js)
#### Step 1 - Install required package
drizzle-orm@rc @sqlitecloud/drivers dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/sqlite-cloud';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/sqlite-cloud';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
Source: https://orm.drizzle.team/docs/get-started/sqlite-cloud-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectSQLiteCloud from '@mdx/get-started/sqlite/ConnectSQLiteCloud.mdx'
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and SQLite Cloud
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **SQLite Cloud database** - [read here](https://docs.sqlitecloud.io/docs/overview)
- **SQLite Cloud driver** - [read here](https://docs.sqlitecloud.io/docs/sdk-js-introduction) & [GitHub](https://github.com/sqlitecloud/sqlitecloud-js)
#### Step 1 - Install required package
drizzle-orm@rc @sqlitecloud/drivers dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/sqlite-cloud';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/sqlite-existing
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import IntrospectSqlite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and SQLite in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **libsql** - a fork of SQLite optimized for low query latency, making it suitable for global applications - [read here](https://docs.turso.tech/libsql)
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you need to use `file:` before the actual filename, as this is the format required by `LibSQL`, like this:
```plaintext copy
DB_FILE_NAME=file:local.db
```
You can check the **[LibSQL docs](https://docs.turso.tech/sdk/ts/reference#local-development)** for more info.
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 9 - Applying changes to the database (optional)
#### Step 10 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/sqlite-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
# Get Started with Drizzle and SQLite
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **libsql** - a fork of SQLite optimized for low query latency, making it suitable for global applications - [read here](https://docs.turso.tech/libsql)
Drizzle has native support for SQLite connections with the `libsql`, `node:sqlite` and `better-sqlite3` drivers.
We will use `libsql` for this get started example. But if you want to find more ways to connect to SQLite check
our [SQLite Connection](/docs/sqlite/get-started-sqlite) page
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you need to use `file:` before the actual filename, as this is the format required by `LibSQL`, like this:
```plaintext copy
DB_FILE_NAME=file:local.db
```
You can check the **[LibSQL docs](https://docs.turso.tech/sdk/ts/reference#local-development)** for more info.
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/supabase-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectSupabase from '@mdx/get-started/postgresql/ConnectSupabase.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Supabase in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Supabase** - open source Firebase alternative - [read here](https://supabase.com/)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/supabase-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectSupabase from '@mdx/get-started/postgresql/ConnectSupabase.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Supabase
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Supabase** - open source Firebase alternative - [read here](https://supabase.com/)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/tidb-existing
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectTiDB from '@mdx/get-started/mysql/ConnectTiDB.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import UpdateSchema from '@mdx/get-started/mysql/UpdateSchema.mdx';
import IntrospectMySQL from '@mdx/get-started/mysql/IntrospectMySQL.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
# Get Started with Drizzle and TiDB in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **TiDB** - The Distributed SQL Database by PingCAP - [read here](https://www.pingcap.com/)
- **serverless-js** - package for serverless and edge compute platforms that require HTTP external connections - [read here](https://github.com/tidbcloud/serverless-js)
#### Step 1 - Install **@tidbcloud/serverless** package
#### Step 2 - Setup connection variables
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/tidb-new
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import ConnectTiDB from '@mdx/get-started/mysql/ConnectTiDB.mdx';
import CreateTable from '@mdx/get-started/mysql/CreateTable.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import ApplyChanges from '@mdx/get-started/mysql/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import SetupConfig from '@mdx/get-started/mysql/SetupConfig.mdx';
# Get Started with Drizzle and TiDB
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **TiDB** - The Distributed SQL Database by PingCAP - [read here](https://www.pingcap.com/)
- **serverless-js** - package for serverless and edge compute platforms that require HTTP external connections - [read here](https://github.com/tidbcloud/serverless-js)
For this tutorial, we will use the `@tidbcloud/serverless` driver to make **HTTP** calls. If you need to
connect to TiDB through TCP, you can refer to our [MySQL Get Started](/docs/get-started/mysql-new) page
#### Step 1 - Install **@tidbcloud/serverless** package
#### Step 2 - Setup connection variables
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/turso-database-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectSQLite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectTursoDatabase from '@mdx/get-started/sqlite/ConnectTursoDatabase.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
# Get Started with Drizzle and Turso Database in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- Turso Database - [website](https://docs.turso.tech/introduction)
- Turso Database driver - [website](https://docs.turso.tech/connect/javascript) & [GitHub](https://github.com/tursodatabase/turso/tree/main/bindings/javascript)
#### Step 1 - Install required package
drizzle-orm@rc @tursodatabase/database dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/tursodatabase/database';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/tursodatabase/database';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
Source: https://orm.drizzle.team/docs/get-started/turso-database-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectTursoDatabase from '@mdx/get-started/sqlite/ConnectTursoDatabase.mdx'
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Turso Database
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- Turso Database - [website](https://docs.turso.tech/introduction)
- Turso Database driver - [website](https://docs.turso.tech/connect/javascript) & [GitHub](https://github.com/tursodatabase/turso/tree/main/bindings/javascript)
#### Step 1 - Install required package
drizzle-orm@rc @tursodatabase/database dotenv
-D drizzle-kit@rc tsx
#### Step 2 - Setup connection variables
For example, if you want to create an SQLite database file in the root of your project for testing purposes, you can use this example:
```plaintext copy
DB_FILE_NAME=mydb.sqlite
```
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/tursodatabase/database';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/turso-existing
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import IntrospectSqlite from '@mdx/get-started/sqlite/IntrospectSqlite.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
import UpdateSchema from '@mdx/get-started/sqlite/UpdateSchema.mdx';
import QueryTurso from '@mdx/get-started/sqlite/QueryTurso.mdx';
import QueryTursoUpdated from '@mdx/get-started/sqlite/QueryTursoUpdated.mdx';
import LibsqlTable from '@mdx/LibsqlTable.mdx';
import LibsqlTabs from '@mdx/LibsqlTabs.mdx';
# Get Started with Drizzle and Turso Cloud in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **turso** - SQLite for Production - [read here](https://turso.tech/)
- **libsql** - a fork of SQLite optimized for low query latency, making it suitable for global applications - [read here](https://docs.turso.tech/libsql)
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
Create a `.env` file in the root of your project and add you Turso database url and auth token:
```plaintext copy
TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=
```
If you don't know your `TURSO_DATABASE_URL` and `TURSO_AUTH_TOKEN` values, you can refer to the LibSQL Driver SDK tutorial.
Check it out [here](https://docs.turso.tech/sdk/ts/quickstart), then return with all the values generated and added to the `.env` file
#### Step 3 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'turso',
dbCredentials: {
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
},
});
```
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
Drizzle has native support for all @libsql/client driver variations:
Create a `index.ts` file in the `src` directory and initialize the connection:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/libsql';
// You can specify any property from the libsql connection options
const db = drizzle({
connection: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!
}
});
```
If you need to provide your existing driver:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!
});
const db = drizzle({ client });
```
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 9 - Applying changes to the database (optional)
#### Step 10 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/turso-new
import Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/sqlite/SetupConfig.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import QueryTurso from '@mdx/get-started/sqlite/QueryTurso.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ApplyChanges from '@mdx/get-started/sqlite/ApplyChanges.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import CreateTable from '@mdx/get-started/sqlite/CreateTable.mdx';
import ConnectLibsql from '@mdx/get-started/sqlite/ConnectLibsql.mdx';
import LibsqlTable from '@mdx/LibsqlTable.mdx';
import LibsqlTabs from '@mdx/LibsqlTabs.mdx';
# Get Started with Drizzle and Turso Cloud
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **turso** - SQLite for Production - [read here](https://turso.tech/)
- **libsql** - a fork of SQLite optimized for low query latency, making it suitable for global applications - [read here](https://docs.turso.tech/libsql)
#### Step 1 - Install required packages
#### Step 2 - Setup connection variables
Create a `.env` file in the root of your project and add you Turso database url and auth token:
```plaintext copy
TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=
```
If you don't know your `TURSO_DATABASE_URL` and `TURSO_AUTH_TOKEN` values, you can refer to the LibSQL Driver SDK tutorial.
Check it out [here](https://docs.turso.tech/sdk/ts/quickstart), then return with all the values generated and added to the `.env` file
#### Step 3 - Connect Drizzle ORM to the database
Drizzle has native support for all @libsql/client driver variations:
Create a `index.ts` file in the `src` directory and initialize the connection:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/libsql';
// You can specify any property from the libsql connection options
const db = drizzle({
connection: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!
}
});
```
If you need to provide your existing driver:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!
});
const db = drizzle({ client });
```
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'turso',
dbCredentials: {
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
},
});
```
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/vercel-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectVercel from '@mdx/get-started/postgresql/ConnectVercel.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Vercel Postgres in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Vercel Postgres database** - [read here](https://vercel.com/docs/storage/vercel-postgres)
- **Vercel Postgres driver** - [read here](https://vercel.com/docs/storage/vercel-postgres/sdk) & [GitHub](https://github.com/vercel/storage/tree/main/packages/postgres)
#### Step 1 - Install required package
#### Step 2 - Setup connection variables
It's important to name the variable `POSTGRES_URL` for Vercel Postgres.
In the Vercel Postgres storage tab, you can find the `.env.local` tab and copy the `POSTGRES_URL` variable
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
phone: string | null;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
Source: https://orm.drizzle.team/docs/get-started/vercel-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectVercel from '@mdx/get-started/postgresql/ConnectVercel.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Vercel Postgres
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Vercel Postgres database** - [read here](https://vercel.com/docs/storage/vercel-postgres)
- **Vercel Postgres driver** - [read here](https://vercel.com/docs/storage/vercel-postgres/sdk) & [GitHub](https://github.com/vercel/storage/tree/main/packages/postgres)
#### Step 1 - Install required package
#### Step 2 - Setup connection variables
It's important to name the variable `POSTGRES_URL` for Vercel Postgres.
In the Vercel Postgres storage tab, you can find the `.env.local` tab and copy the `POSTGRES_URL` variable
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
```typescript copy filename="src/index.ts"
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { usersTable } from './db/schema';
async function main() {
const db = drizzle();
const user: typeof usersTable.$inferInsert = {
name: 'John',
age: 30,
email: 'john@example.com',
};
await db.insert(usersTable).values(user);
console.log('New user created!')
const users = await db.select().from(usersTable);
console.log('Getting all users from the database: ', users)
/*
const users: {
id: number;
name: string;
age: number;
email: string;
}[]
*/
await db
.update(usersTable)
.set({
age: 31,
})
.where(eq(usersTable.email, user.email));
console.log('User info updated!')
await db.delete(usersTable).where(eq(usersTable.email, user.email));
console.log('User deleted!')
}
main();
```
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/get-started/xata-existing
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IntrospectPostgreSQL from '@mdx/get-started/postgresql/IntrospectPostgreSQL.mdx';
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
import TransferCode from '@mdx/get-started/TransferCode.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import ConnectXata from '@mdx/get-started/postgresql/ConnectXata.mdx'
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import QueryDatabaseUpdated from '@mdx/get-started/QueryDatabaseUpdated.mdx';
import UpdateSchema from '@mdx/get-started/postgresql/UpdateSchema.mdx';
# Get Started with Drizzle and Xata in existing project
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Xata Postgres database** - [read here](https://xata.io/documentation)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
You can obtain a connection string by following the [Xata documentation](https://xata.io/documentation/getting-started).
#### Step 3 - Setup Drizzle config file
#### Step 4 - Introspect your database
#### Step 5 - Transfer code to your actual schema file
#### Step 6 - Connect Drizzle ORM to the database
#### Step 7 - Query the database
#### Step 8 - Run index.ts file
#### Step 9 - Update your table schema (optional)
#### Step 10 - Applying changes to the database (optional)
#### Step 11 - Query the database with a new field (optional)
Source: https://orm.drizzle.team/docs/get-started/xata-new
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Breadcrumbs from '@mdx/Breadcrumbs.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import FileStructure from '@mdx/get-started/FileStructure.mdx';
import InstallPackages from '@mdx/get-started/InstallPackages.mdx';
import ConnectXata from '@mdx/get-started/postgresql/ConnectXata.mdx'
import CreateTable from '@mdx/get-started/postgresql/CreateTable.mdx'
import SetupConfig from '@mdx/get-started/SetupConfig.mdx';
import ApplyChanges from '@mdx/get-started/ApplyChanges.mdx';
import RunFile from '@mdx/get-started/RunFile.mdx';
import QueryDatabase from '@mdx/get-started/QueryDatabase.mdx';
import SetupEnv from '@mdx/get-started/SetupEnv.mdx';
# Get Started with Drizzle and Xata
- **dotenv** - package for managing environment variables - [read here](https://www.npmjs.com/package/dotenv)
- **tsx** - package for running TypeScript files - [read here](https://tsx.hirok.io/)
- **Xata Postgres database** - [read here](https://xata.io/documentation)
#### Step 1 - Install **postgres** package
#### Step 2 - Setup connection variables
You can obtain a connection string by following the [Xata documentation](https://xata.io/docs/quickstart).
#### Step 3 - Connect Drizzle ORM to the database
#### Step 4 - Create a table
#### Step 5 - Setup Drizzle config file
#### Step 6 - Applying changes to the database
#### Step 7 - Seed and Query the database
#### Step 8 - Run index.ts file
Source: https://orm.drizzle.team/docs/gotchas
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";
# Drizzle gotchas
This will be a library of `gotchas` with Drizzle use cases
Source: https://orm.drizzle.team/docs/graphql
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Callout from '@mdx/Callout.astro';
import CodeTab from '@mdx/CodeTab.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import Npm from '@mdx/Npm.astro';
# drizzle-graphql
Create a GraphQL server from a Drizzle schema in one line, and easily enhance it with custom queries and mutations.
## Quick start
Make sure your `drizzle-orm` version is at least `0.30.9`, and update if needed:
drizzle-orm@latest
### Apollo Server
drizzle-graphql @apollo/server graphql
```ts copy {1, 10}
import { buildSchema } from 'drizzle-graphql';
import { drizzle } from 'drizzle-orm/...';
import client from './db';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import * as dbSchema from './schema';
const db = drizzle({ client, schema: dbSchema });
const { schema } = buildSchema(db);
const server = new ApolloServer({ schema });
const { url } = await startStandaloneServer(server);
console.log(`đ Server ready at ${url}`);
```
```typescript copy
import { integer, serial, text, pgTable } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
authorId: integer('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```
### GraphQL Yoga
drizzle-graphql graphql-yoga graphql
```ts copy {1, 10}
import { buildSchema } from 'drizzle-graphql';
import { drizzle } from 'drizzle-orm/...';
import { createYoga } from 'graphql-yoga';
import { createServer } from 'node:http';
import * as dbSchema from './schema';
const db = drizzle({ schema: dbSchema });
const { schema } = buildSchema(db);
const yoga = createYoga({ schema });
const server = createServer(yoga);
server.listen(4000, () => {
console.info('Server is running on http://localhost:4000/graphql');
});
```
```typescript copy
import { integer, serial, text, pgTable } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
authorId: integer('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```
## Customizing schema
`buildSchema()` produces schema and types using standard `graphql` SDK, so its output is compatible with any library that supports it.
If you want to customize your schema, you can use `entities` object to build your own new schema:
```ts {1, 11}
import { buildSchema } from 'drizzle-graphql';
import { drizzle } from 'drizzle-orm/...';
import { GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema } from 'graphql';
import { createYoga } from 'graphql-yoga';
import { createServer } from 'node:http';
import * as dbSchema from './schema';
const db = drizzle({ schema: dbSchema });
const { entities } = buildSchema(db);
// You can customize which parts of queries or mutations you want
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
// Select only wanted queries out of all generated
users: entities.queries.users,
customer: entities.queries.customersSingle,
// Create a custom one
customUsers: {
// You can reuse and customize types from original schema
type: new GraphQLList(new GraphQLNonNull(entities.types.UsersItem)),
args: {
// You can reuse inputs as well
where: {
type: entities.inputs.UsersFilters
},
},
resolve: async (source, args, context, info) => {
// Your custom logic goes here...
const result = await db.select(schema.users).where()...
return result;
},
},
},
}),
// Same rules apply to mutations
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: entities.mutations,
}),
// In case you need types inside your schema
types: [...Object.values(entities.types), ...Object.values(entities.inputs)],
});
const yoga = createYoga({
schema,
});
const server = createServer(yoga);
server.listen(4000, () => {
console.info('Server is running on http://localhost:4000/graphql');
})
```
```typescript copy
import { integer, serial, text, pgTable } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
authorId: integer('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```
Source: https://orm.drizzle.team/docs/guides
import Guides from "@components/Guides.astro";
Source: https://orm.drizzle.team/docs/guides/conditional-filters-in-query
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach);
- [Select statement](/docs/select);
- [Filtering](/docs/select#filters) and [Filter operators](/docs/operators);
To pass a conditional filter in query you can use `.where()` method and logical operator like below:
```ts copy {9}
import { ilike } from 'drizzle-orm';
const db = drizzle(...)
const searchPosts = async (term?: string) => {
await db
.select()
.from(posts)
.where(term ? ilike(posts.title, term) : undefined);
};
await searchPosts();
await searchPosts('AI');
```
```sql
select * from posts;
select * from posts where title ilike 'AI';
```
To combine conditional filters you can use `and()` or `or()` operators like below:
```ts copy {7,8,9,10,11,12,13}
import { and, gt, ilike, inArray } from 'drizzle-orm';
const searchPosts = async (term?: string, categories: string[] = [], views = 0) => {
await db
.select()
.from(posts)
.where(
and(
term ? ilike(posts.title, term) : undefined,
categories.length > 0 ? inArray(posts.category, categories) : undefined,
views > 100 ? gt(posts.views, views) : undefined,
),
);
};
await searchPosts();
await searchPosts('AI', ['Tech', 'Art', 'Science'], 200);
```
```sql
select * from posts;
select * from posts
where (
title ilike 'AI'
and category in ('Tech', 'Science', 'Art')
and views > 200
);
```
If you need to combine conditional filters in different part of the project you can create a variable, push filters and then use it in `.where()` method with `and()` or `or()` operators like below:
```ts copy {7,10}
import { SQL, ... } from 'drizzle-orm';
const searchPosts = async (filters: SQL[]) => {
await db
.select()
.from(posts)
.where(and(...filters));
};
const filters: SQL[] = [];
filters.push(ilike(posts.title, 'AI'));
filters.push(inArray(posts.category, ['Tech', 'Art', 'Science']));
filters.push(gt(posts.views, 200));
await searchPosts(filters);
```
Drizzle has useful and flexible API, which lets you create your custom solutions. This is how you can create a custom filter operator:
```ts copy {5,14}
import { AnyColumn, ... } from 'drizzle-orm';
// length less than
const lenlt = (column: AnyColumn, value: number) => {
return sql`length(${column}) < ${value}`;
};
const searchPosts = async (maxLen = 0, views = 0) => {
await db
.select()
.from(posts)
.where(
and(
maxLen ? lenlt(posts.title, maxLen) : undefined,
views > 100 ? gt(posts.views, views) : undefined,
),
);
};
await searchPosts(8);
await searchPosts(8, 200);
```
```sql
select * from posts where length(title) < 8;
select * from posts where (length(title) < 8 and views > 200);
```
Drizzle filter operators are just SQL expressions under the hood. This is example of how `lt` operator is implemented in Drizzle:
```js
const lt = (left, right) => {
return sql`${left} < ${bindIfParam(right, left)}`; // bindIfParam is internal magic function
};
```
Source: https://orm.drizzle.team/docs/guides/count-rows
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Select statement](/docs/select)
- [Filters](/docs/operators) and [sql operator](/docs/sql)
- [Aggregations](/docs/select#aggregations) and [Aggregation helpers](/docs/select#aggregations-helpers)
- [Joins](/docs/joins)
To count all rows in table you can use `count()` function or `sql` operator like below:
```ts copy {6,9}
import { count, sql } from 'drizzle-orm';
import { products } from './schema';
const db = drizzle(...);
await db.select({ count: count() }).from(products);
// Under the hood, the count() function casts its result to a number at runtime.
await db.select({ count: sql`count(*)`.mapWith(Number) }).from(products);
```
```ts
// result type
type Result = {
count: number;
}[];
```
```sql
select count(*) from products;
```
```ts
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const products = pgTable('products', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
discount: integer('discount'),
price: integer('price').notNull(),
});
```
To count rows where the specified column contains non-NULL values you can use `count()` function with a column:
```ts copy {1}
await db.select({ count: count(products.discount) }).from(products);
```
```ts
// result type
type Result = {
count: number;
}[];
```
```sql
select count("discount") from products;
```
Drizzle has simple and flexible API, which lets you create your custom solutions. In PostgreSQL, MySQL and Cockroach `count()` function returns bigint, which is interpreted as string by their drivers, so it should be casted to integer:
```ts copy {5,7,11,12}
import { AnyColumn, sql } from 'drizzle-orm';
const customCount = (column?: AnyColumn) => {
if (column) {
return sql`cast(count(${column}) as integer)`; // In MySQL cast to unsigned integer
} else {
return sql`cast(count(*) as integer)`; // In MySQL cast to unsigned integer
}
};
await db.select({ count: customCount() }).from(products);
await db.select({ count: customCount(products.discount) }).from(products);
```
```sql
select cast(count(*) as integer) from products;
select cast(count("discount") as integer) from products;
```
In SQLite and MSSQL, `count()` result returns as integer.
```ts copy {3,4}
import { sql } from 'drizzle-orm';
await db.select({ count: sql`count(*)` }).from(products);
await db.select({ count: sql`count(${products.discount})` }).from(products);
```
```sql
select count(*) from products;
select count("discount") from products;
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `number`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a number), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/sql#sqlmapwith) method.
To count rows that match a condition you can use `.where()` method:
```ts copy {4,6}
import { count, gt } from 'drizzle-orm';
await db
.select({ count: count() })
.from(products)
.where(gt(products.price, 100));
```
```sql
select count(*) from products where price > 100
```
This is how you can use `count()` function with joins and aggregations:
```ts copy {8,11,12,13}
import { count, eq } from 'drizzle-orm';
import { countries, cities } from './schema';
// Count cities in each country
await db
.select({
country: countries.name,
citiesCount: count(cities.id),
})
.from(countries)
.leftJoin(cities, eq(countries.id, cities.countryId))
.groupBy(countries.id)
.orderBy(countries.name);
```
```sql
select countries.name, count("cities"."id") from countries
left join cities on countries.id = cities.country_id
group by countries.id
order by countries.name;
```
```ts
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const countries = pgTable('countries', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
export const cities = pgTable('cities', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
countryId: integer('country_id').notNull().references(() => countries.id),
});
```
Source: https://orm.drizzle.team/docs/guides/cursor-based-pagination
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Select statement](/docs/select) with [order by clause](/docs/select#order-by)
- [Relational queries](/docs/rqb) with [order by clause](/docs/rqb#order-by)
- [Indices](/docs/indexes-constraints)
This guide demonstrates how to implement `cursor-based` pagination in Drizzle:
```ts copy {10,11,12}
import { asc, gt } from 'drizzle-orm';
import { users } from './schema';
const db = drizzle(...);
const nextUserPage = async (cursor?: number, pageSize = 3) => {
await db
.select()
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined) // if cursor is provided, get rows after it
.limit(pageSize) // the number of rows to return
.orderBy(asc(users.id)); // ordering
};
// pass the cursor of the last row of the previous page (id)
await nextUserPage(3);
```
```sql
select * from users order by id asc limit 3;
```
```ts
// next page, 4-6 rows returned
[
{
id: 4,
firstName: 'Brian',
lastName: 'Brown',
createdAt: 2024-03-08T12:34:55.182Z
},
{
id: 5,
firstName: 'Beth',
lastName: 'Davis',
createdAt: 2024-03-08T12:40:55.182Z
},
{
id: 6,
firstName: 'Charlie',
lastName: 'Miller',
createdAt: 2024-03-08T13:04:55.182Z
}
]
```
```ts copy
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
firstName: text('first_name').notNull(),
lastName: text('last_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
```
```plaintext
+----+------------+------------+----------------------------+
| id | first_name | last_name | created_at |
+----+------------+------------+----------------------------+
| 1 | Alice | Johnson | 2024-03-08 12:23:55.251797 |
+----+------------+------------+----------------------------+
| 2 | Alex | Smith | 2024-03-08 12:25:55.182 |
+----+------------+------------+----------------------------+
| 3 | Aaron | Williams | 2024-03-08 12:28:55.182 |
+----+------------+------------+----------------------------+
| 4 | Brian | Brown | 2024-03-08 12:34:55.182 |
+----+------------+------------+----------------------------+
| 5 | Beth | Davis | 2024-03-08 12:40:55.182 |
+----+------------+------------+----------------------------+
| 6 | Charlie | Miller | 2024-03-08 13:04:55.182 |
+----+------------+------------+----------------------------+
| 7 | Clara | Wilson | 2024-03-08 13:22:55.182 |
+----+------------+------------+----------------------------+
| 8 | David | Moore | 2024-03-08 13:34:55.182 |
+----+------------+------------+----------------------------+
| 9 | Aaron | Anderson | 2024-03-08 12:40:33.677235 |
+----+------------+------------+----------------------------+
```
If you need dynamic order by you can do like below:
```ts copy {6,8}
const nextUserPage = async (order: 'asc' | 'desc' = 'asc', cursor?: number, pageSize = 3) => {
await db
.select()
.from(users)
// cursor comparison
.where(cursor ? (order === 'asc' ? gt(users.id, cursor) : lt(users.id, cursor)) : undefined)
.limit(pageSize)
.orderBy(order === 'asc' ? asc(users.id) : desc(users.id));
};
await nextUserPage();
await nextUserPage('asc', 3);
// descending order
await nextUserPage('desc');
await nextUserPage('desc', 7);
```
The main idea of this pagination is to use cursor as a pointer to a specific row in a dataset, indicating the end of the previous page. For correct ordering and cursor comparison, cursor should be unique and sequential.
If you need to order by a non-unique and non-sequential column, you can use multiple columns for cursor. This is how you can do it:
```ts copy {14,15,16,17,18,19,22}
import { and, asc, eq, gt, or } from 'drizzle-orm';
const nextUserPage = async (
cursor?: {
id: number;
firstName: string;
},
pageSize = 3,
) => {
await db
.select()
.from(users)
.where(
cursor
? or(
gt(users.firstName, cursor.firstName),
and(eq(users.firstName, cursor.firstName), gt(users.id, cursor.id)),
)
: undefined,
)
.limit(pageSize)
.orderBy(asc(users.firstName), asc(users.id));
};
// pass the cursor from previous page (id & firstName)
await nextUserPage({
id: 2,
firstName: 'Alex',
});
```
```sql
select * from users
where (first_name > 'Alex' or (first_name = 'Alex' and id > 2))
order by first_name asc, id asc limit 3;
```
```ts
// next page, 4-6 rows returned
[
{
id: 1,
firstName: 'Alice',
lastName: 'Johnson',
createdAt: 2024-03-08T12:23:55.251Z
},
{
id: 5,
firstName: 'Beth',
lastName: 'Davis',
createdAt: 2024-03-08T12:40:55.182Z
},
{
id: 4,
firstName: 'Brian',
lastName: 'Brown',
createdAt: 2024-03-08T12:34:55.182Z
}
]
```
Make sure to create indices for the columns that you use for cursor to make query efficient.
```ts copy {7,8}
import { index, ...imports } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
// columns declaration
},
(t) => [
index('first_name_index').on(t.firstName).asc(),
index('first_name_and_id_index').on(t.firstName, t.id).asc(),
]);
```
```sql
-- As of now drizzle-kit only supports index name and on() param, so you have to add order manually
CREATE INDEX IF NOT EXISTS "first_name_index" ON "users" ("first_name" ASC);
CREATE INDEX IF NOT EXISTS "first_name_and_id_index" ON "users" ("first_name" ASC,"id" ASC);
```
If you are using primary key which is not sequential (e.g. `UUIDv4`), you should add sequential column (e.g. `created_at` column) and use multiple cursor.
This is how you can do it:
```ts copy {12,13,14,15,16,17,18,21}
const nextUserPage = async (
cursor?: {
id: string;
createdAt: Date;
},
pageSize = 3,
) => {
await db
.select()
.from(users)
.where(
// make sure to add indices for the columns that you use for cursor
cursor
? or(
gt(users.createdAt, cursor.createdAt),
and(eq(users.createdAt, cursor.createdAt), gt(users.id, cursor.id)),
)
: undefined,
)
.limit(pageSize)
.orderBy(asc(users.createdAt), asc(users.id));
};
// pass the cursor from previous page (id & createdAt)
await nextUserPage({
id: '66ed00a4-c020-4dfd-a1ca-5d2e4e54d174',
createdAt: new Date('2024-03-09T17:59:36.406Z'),
});
```
Drizzle has useful relational queries API, that lets you easily implement `cursor-based` pagination:
```ts copy {7,8,9}
import * as schema from './db/schema';
const db = drizzle(..., { schema });
const nextUserPage = async (cursor?: number, pageSize = 3) => {
await db.query.users.findMany({
where: (users, { gt }) => (cursor ? gt(users.id, cursor) : undefined),
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
});
};
// next page, cursor of last row of the first page (id = 3)
await nextUserPage(3);
```
**Benefits** of `cursor-based` pagination: consistent query results, with no skipped or duplicated rows due to insert or delete operations, and greater efficiency compared to `limit/offset` pagination because it does not need to scan and skip previous rows to access the next page.
**Drawbacks** of `cursor-based` pagination: the inability to directly navigate to a specific page and complexity of implementation. Since you add more columns to the sort order, you'll need to add more filters to the `where` clause for the cursor comparison to ensure consistent pagination.
So, if you need to directly navigate to a specific page or you need simpler implementation of pagination, you should consider using [offset/limit](/docs/guides/limit-offset-pagination) pagination instead.
Source: https://orm.drizzle.team/docs/guides/d1-http-with-drizzle-kit
import Prerequisites from "@mdx/Prerequisites.astro";
- [Drizzle Kit](/docs/kit-overview)
- [Drizzle Studio](/docs/kit-overview#drizzle-studio)
- [Drizzle Chrome Extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd)
- You should have installed `drizzle-kit@0.21.3` or higher
- You should have [Cloudflare account](https://dash.cloudflare.com/login), deployed [D1 database](https://developers.cloudflare.com/d1/) and token with D1 edit permissions
To use Drizzle kit with Cloudflare D1 HTTP API, you need to configure the `drizzle.config.ts` file like this:
```ts copy filename="drizzle.config.ts" {7, 9-11}
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema.ts',
out: './migrations',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
});
```
You can find `accountId`, `databaseId` and `token` in [Cloudflare dashboard](https://dash.cloudflare.com/login?).
1. To get `accountId` go to **Workers & Pages** -> **Overview** -> copy **Account ID** from the right sidebar.
2. To get `databaseId` open D1 database you want to connect to and copy **Database ID**.
3. To get `token` go to **My profile** -> **API Tokens** and create token with D1 edit permissions.
After you have configured `drizzle.config.ts` file, Drizzle Kit lets you run `migrate`, `push`, `introspect` and `studio` commands using Cloudflare D1 HTTP API.
You can also use [Drizzle Chrome Extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd) to browse Cloudflare D1 database directly in their admin panel.
Source: https://orm.drizzle.team/docs/guides/decrementing-a-value
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Update statement](/docs/update)
- [Filters](/docs/operators) and [sql operator](/docs/sql)
To decrement a column value you can use `update().set()` method like below:
```ts copy {8}
import { eq, sql } from 'drizzle-orm';
const db = drizzle(...)
await db
.update(table)
.set({
counter: sql`${table.counter} - 1`,
})
.where(eq(table.id, 1));
```
```sql
update "table" set "counter" = "counter" - 1 where "id" = 1;
```
Drizzle has simple and flexible API, which lets you easily create custom solutions. This is how you do custom decrement function:
```ts copy {4,10,11}
import { AnyColumn } from 'drizzle-orm';
const decrement = (column: AnyColumn, value = 1) => {
return sql`${column} - ${value}`;
};
await db
.update(table)
.set({
counter1: decrement(table.counter1),
counter2: decrement(table.counter2, 10),
})
.where(eq(table.id, 1));
```
Source: https://orm.drizzle.team/docs/guides/empty-array-default-value
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql) and [SQLite](/docs/sqlite/get-started-sqlite)
- Learn about column data types for [PostgreSQL](/docs/column-types), [MySQL](/docs/mysql/column-types) and [SQLite](/docs/sqlite/column-types)
- [sql operator](/docs/sql)
### PostgreSQL
To set an empty array as a default value in PostgreSQL, you can use `sql` operator with `'{}'` or `ARRAY[]` syntax:
```ts copy {10,14}
import { sql } from 'drizzle-orm';
import { pgTable, serial, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
tags1: text('tags1')
.array()
.notNull()
.default(sql`'{}'::text[]`),
tags2: text('tags2')
.array()
.notNull()
.default(sql`ARRAY[]::text[]`),
});
```
```sql
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"tags1" text[] DEFAULT '{}'::text[] NOT NULL,
"tags2" text[] DEFAULT ARRAY[]::text[] NOT NULL
);
```
### MySQL
MySQL doesn't have an array data type, but you can use `json` data type for the same purpose. To set an empty array as a default value in MySQL, you can use `JSON_ARRAY()` function or `sql` operator with `('[]')` syntax:
```ts copy {7,11,15}
import { sql } from 'drizzle-orm';
import { json, mysqlTable, serial, varchar } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
tags1: json('tags1').$type().notNull().default([]),
tags2: json('tags2')
.$type()
.notNull()
.default(sql`('[]')`), // the same as default([])
tags3: json('tags3')
.$type()
.notNull()
.default(sql`(JSON_ARRAY())`),
});
```
```sql
CREATE TABLE `users` (
`id` serial AUTO_INCREMENT NOT NULL,
`name` varchar(255) NOT NULL,
`tags1` json NOT NULL DEFAULT ('[]'),
`tags2` json NOT NULL DEFAULT ('[]'),
`tags3` json NOT NULL DEFAULT (JSON_ARRAY()),
CONSTRAINT `users_id` PRIMARY KEY(`id`)
);
```
The `mode` option defines how values are handled in the application. With `json` mode, values are treated as JSON object literal.
You can specify `.$type<..>()` for json object inference, it will not check runtime values. It provides compile time protection for default values, insert and select schemas.
### SQLite
SQLite doesn't have an array data type, but you can use `text` data type for the same purpose. To set an empty array as a default value in SQLite, you can use `json_array()` function or `sql` operator with `'[]'` syntax:
```ts copy {9,13}
import { sql } from 'drizzle-orm';
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
tags1: text('tags1', { mode: 'json' })
.notNull()
.$type()
.default(sql`(json_array())`),
tags2: text('tags2', { mode: 'json' })
.notNull()
.$type()
.default(sql`'[]'`),
});
```
```sql
CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`tags1` text DEFAULT (json_array()) NOT NULL,
`tags2` text DEFAULT '[]' NOT NULL
);
```
The `mode` option defines how values are handled in the application. With `json` mode, values are treated as JSON object literal.
You can specify `.$type<..>()` for json object inference, it will not check runtime values. It provides compile time protection for default values, insert and select schemas.
Source: https://orm.drizzle.team/docs/guides/full-text-search-with-generated-columns
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql)
- [Select statement](/docs/select)
- [Indexes](/docs/indexes-constraints#indexes)
- [sql operator](/docs/sql)
- [Full-text search](/learn/guides/postgresql-full-text-search)
- [Generated columns](/docs/generated-columns)
This guide demonstrates how to implement full-text search in PostgreSQL with Drizzle and generated columns. A generated column is a special column that is always computed from other columns. It is useful because you don't have to compute the value of the column every time you query the table:
```ts copy {18,19,20,23}
import { SQL, sql } from 'drizzle-orm';
import { index, pgTable, serial, text, customType } from 'drizzle-orm/pg-core';
export const tsvector = customType<{
data: string;
}>({
dataType() {
return `tsvector`;
},
});
export const posts = pgTable(
'posts',
{
id: serial('id').primaryKey(),
title: text('title').notNull(),
body: text('body').notNull(),
bodySearch: tsvector('body_search')
.notNull()
.generatedAlwaysAs((): SQL => sql`to_tsvector('english', ${posts.body})`),
},
(t) => [
index('idx_body_search').using('gin', t.bodySearch),
]
);
```
```sql
CREATE TABLE "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"body" text NOT NULL,
"body_search" "tsvector" GENERATED ALWAYS AS (to_tsvector('english', "posts"."body")) STORED NOT NULL
);
--> statement-breakpoint
CREATE INDEX "idx_body_search" ON "posts" USING gin ("body_search");
```
When you insert a row into a table, the value of a generated column is computed from an expression that you provide when you create the column:
```ts
import { posts } from './schema';
const db = drizzle(...);
const body = "Golden leaves cover the quiet streets as a crisp breeze fills the air, bringing the scent of rain and the promise of change"
await db.insert(posts).values({
body,
title: "The Beauty of Autumn",
}
).returning();
```
```json
[
{
id: 1,
title: 'The Beauty of Autumn',
body: 'Golden leaves cover the quiet streets as a crisp breeze fills the air, bringing the scent of rain and the promise of change',
bodySearch: "'air':13 'breez':10 'bring':14 'chang':23 'cover':3 'crisp':9 'fill':11 'golden':1 'leav':2 'promis':21 'quiet':5 'rain':18 'scent':16 'street':6"
}
]
```
This is how you can implement full-text search with generated columns in PostgreSQL with Drizzle ORM. The `@@` operator is used for direct matches:
```ts copy {6}
const searchParam = "bring";
await db
.select()
.from(posts)
.where(sql`${posts.bodySearch} @@ to_tsquery('english', ${searchParam})`);
```
```sql
select * from posts where body_search @@ to_tsquery('english', 'bring');
```
This is more advanced schema with a generated column. The `search` column is generated from the `title` and `body` columns and `setweight()` function is used to assign different weights to the columns for full-text search.
This is typically used to mark entries coming from different parts of a document, such as title versus body.
```ts copy {18,19,20,21,22,23,24,28}
import { SQL, sql } from 'drizzle-orm';
import { index, pgTable, serial, text, customType } from 'drizzle-orm/pg-core';
export const tsvector = customType<{
data: string;
}>({
dataType() {
return `tsvector`;
},
});
export const posts = pgTable(
'posts',
{
id: serial('id').primaryKey(),
title: text('title').notNull(),
body: text('body').notNull(),
search: tsvector('search')
.notNull()
.generatedAlwaysAs(
(): SQL =>
sql`setweight(to_tsvector('english', ${posts.title}), 'A')
||
setweight(to_tsvector('english', ${posts.body}), 'B')`,
),
},
(t) => [
index('idx_search').using('gin', t.search),
],
);
```
```sql
CREATE TABLE "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"body" text NOT NULL,
"search" "tsvector" GENERATED ALWAYS AS (setweight(to_tsvector('english', "posts"."title"), 'A')
||
setweight(to_tsvector('english', "posts"."body"), 'B')) STORED NOT NULL
);
--> statement-breakpoint
CREATE INDEX "idx_search" ON "posts" USING gin ("search");
```
This is how you can query the table with full-text search:
```ts copy {6}
const search = 'travel';
await db
.select()
.from(posts)
.where(sql`${posts.search} @@ to_tsquery('english', ${search})`);
```
```sql
select * from posts where search @@ to_tsquery('english', 'travel');
```
Source: https://orm.drizzle.team/docs/guides/gel-ext-auth
import Prerequisites from "@mdx/Prerequisites.astro";
import Callout from "@mdx/Callout.astro";
import Npx from "@mdx/Npx.astro";
- Get started with [Gel](/docs/get-started-gel)
- Using [drizzle-kit pull](/docs/drizzle-kit-pull)
#### Step 1 - Define Gel auth schema
In `dbschema/default.esdl` file add a Gel schema with an auth extension
```esdl
using extension auth;
module default {
global current_user := (
assert_single((
select User { id, username, email }
filter .identity = global ext::auth::ClientTokenIdentity
))
);
type User {
required identity: ext::auth::Identity;
required username: str;
required email: str;
}
}
```
#### Step 2 - Push Gel schema to the database
Generate Gel migration file:
```bash
gel migration create
```
Apply Gel migrations to the database
```bash
gel migration apply
```
#### Step 3 - Setup Drizzle config file
**Drizzle config** - a configuration file that is used by [Drizzle Kit](/docs/kit-overview) and contains all the information about your database connection, migration folder and schema files.
Create a `drizzle.config.ts` file in the root of your project and add the following content:
```typescript copy filename="drizzle.config.ts"
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'gel',
// Enable auth schema for drizzle-kit
schemaFilter: ['ext::auth', 'public']
});
```
#### Step 4 - Pull Gel types to Drizzle schema
Pull your database schema:
drizzle-kit pull
Here is an example of the generated schema.ts file:
You'll get more than just the `Identity` table from `ext::auth`. Drizzle will pull in all the
`auth` tables you can use. The example below showcases just one of them.
```ts
import { gelTable, uniqueIndex, uuid, text, gelSchema, timestamptz, foreignKey } from "drizzle-orm/gel-core"
import { sql } from "drizzle-orm"
export const extauth = gelSchema('ext::auth');
export const identityInExtauth = extauth.table('Identity', {
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
createdAt: timestamptz('created_at').default(sql`(clock_timestamp())`).notNull(),
issuer: text().notNull(),
modifiedAt: timestamptz('modified_at').notNull(),
subject: text().notNull(),
}, (table) => [
uniqueIndex('6bc2dd19-bce4-5810-bb1b-7007afe97a11;schemaconstr').using(
'btree',
table.id.asc().nullsLast().op('uuid_ops'),
),
]);
export const user = gelTable('User', {
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
email: text().notNull(),
identityId: uuid('identity_id').notNull(),
username: text().notNull(),
}, (table) => [
uniqueIndex('d504514c-26a7-11f0-b836-81aa188c0abe;schemaconstr').using(
'btree',
table.id.asc().nullsLast().op('uuid_ops'),
),
foreignKey({
columns: [table.identityId],
foreignColumns: [identityInExtauth.id],
name: 'User_fk_identity',
}),
]);
```
đ Now you can use the `auth` tables in your queries!
Source: https://orm.drizzle.team/docs/guides/include-or-exclude-columns
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from "@mdx/Callout.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Select statement](/docs/select)
- [Get typed table columns](/docs/goodies#get-typed-table-columns)
- [Joins](/docs/joins)
- [Relational queries](/docs/rqb)
- [Partial select with relational queries](/docs/rqb#partial-fields-select)
Drizzle has flexible API for including or excluding columns in queries. To include all columns you can use `.select()` method like this:
```ts copy {5}
import { posts } from './schema';
const db = drizzle(...);
await db.select().from(posts);
```
```ts
// result type
type Result = {
id: number;
title: string;
content: string;
views: number;
}[];
```
```ts copy
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
views: integer('views').notNull().default(0),
});
```
To include specific columns you can use `.select()` method like this:
```ts copy {1}
await db.select({ title: posts.title }).from(posts);
```
```ts
// result type
type Result = {
title: string;
}[];
```
To include all columns with extra columns you can use `getColumns()` utility function like this:
```ts copy {5,6}
import { getColumns, sql } from 'drizzle-orm';
await db
.select({
...getColumns(posts),
titleLength: sql`length(${posts.title})`,
})
.from(posts);
```
```ts
// result type
type Result = {
id: number;
title: string;
content: string;
views: number;
titleLength: number;
}[];
```
To exclude columns you can use `getColumns()` utility function like this:
```ts copy {3,5}
import { getColumns } from 'drizzle-orm';
const { content, ...rest } = getColumns(posts); // exclude "content" column
await db.select({ ...rest }).from(posts); // select all other columns
```
```ts
// result type
type Result = {
id: number;
title: string;
views: number;
}[];
```
This is how you can include or exclude columns with joins:
```ts copy {5,9,10,11}
import { eq, getColumns } from 'drizzle-orm';
import { comments, posts, users } from './db/schema';
// exclude "userId" and "postId" columns from "comments"
const { userId, postId, ...rest } = getColumns(comments);
await db
.select({
postId: posts.id, // include "id" column from "posts"
comment: { ...rest }, // include all other columns
user: users, // equivalent to getColumns(users)
})
.from(posts)
.leftJoin(comments, eq(posts.id, comments.postId))
.leftJoin(users, eq(users.id, posts.userId));
```
```ts
// result type
type Result = {
postId: number;
comment: {
id: number;
content: string;
createdAt: Date;
} | null;
user: {
id: number;
name: string;
email: string;
} | null;
}[];
```
```ts copy
import { integer, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
views: integer('views').notNull().default(0),
userId: integer('user_id').notNull().references(() => users.id),
});
export const comments = pgTable('comments', {
id: serial('id').primaryKey(),
postId: integer('post_id').notNull().references(() => posts.id),
userId: integer('user_id').notNull().references(() => users.id),
content: text('content').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
```
Drizzle has useful relational queries API, that lets you easily include or exclude columns in queries. This is how you can include all columns:
```ts copy {5,7,8,9,12,13,14,17,18,19,20,21,22}
import { relations } from './relations';
const db = drizzle(..., { relations });
await db.query.posts.findMany();
```
```ts
// result type
type Result = {
id: number;
title: string;
content: string;
views: number;
}[]
```
```ts copy
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
views: integer('views').notNull().default(0),
});
```
```ts copy
import { posts } from './schema.ts';
export const relations = defineRelations({ posts });
```
This is how you can include specific columns using relational queries:
```ts copy {2,3,4}
await db.query.posts.findMany({
columns: {
title: true,
},
});
```
```ts
// result type
type Result = {
title: string;
}[]
```
This is how you can include all columns with extra columns using relational queries:
```ts copy {4,5,6}
import { sql } from 'drizzle-orm';
await db.query.posts.findMany({
extras: {
titleLength: (t) => sql`length(${t.title})`.as("title_length"),
},
});
```
```ts
// result type
type Result = {
id: number;
title: string;
content: string;
views: number;
titleLength: number;
}[];
```
This is how you can exclude columns using relational queries:
```ts copy {2,3,4}
await db.query.posts.findMany({
columns: {
content: false,
},
});
```
```ts
// result type
type Result = {
id: number;
title: string;
views: number;
}[]
```
This is how you can include or exclude columns with relations using relational queries:
```ts copy {7,12,13,16}
import { relations } from './relations';
const db = drizzle(..., { relations });
await db.query.posts.findMany({
columns: {
id: true, // include "id" column
},
with: {
comments: {
columns: {
userId: false, // exclude "userId" column
postId: false, // exclude "postId" column
},
},
user: true, // include all columns from "users" table
},
});
```
```ts
// result type
type Result = {
id: number;
user: {
id: number;
name: string;
email: string;
};
comments: {
id: number;
content: string;
createdAt: Date;
}[];
}[]
```
```ts copy
import { integer, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
views: integer('views').notNull().default(0),
userId: integer('user_id').notNull().references(() => users.id),
});
export const comments = pgTable('comments', {
id: serial('id').primaryKey(),
postId: integer('post_id').notNull().references(() => posts.id),
userId: integer('user_id').notNull().references(() => users.id),
content: text('content').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
```
```ts copy
import { defineRelations } from "drizzle-orm";
import { comments, posts, users } from "./schema";
export const relations = defineRelations({ users, posts, comments }, (r) => ({
users: {
posts: r.many.posts(),
comments: r.many.comments(),
},
posts: {
user: r.one.users({
from: r.posts.userId,
to: r.users.id,
optional: false,
}),
comments: r.many.comments(),
},
comments: {
post: r.one.posts({
from: r.comments.postId,
to: r.posts.id,
}),
user: r.one.users({
from: r.comments.userId,
to: r.users.id,
}),
},
}));
```
This is how you can create custom solution for conditional select:
```ts copy {7}
import { posts } from './schema';
const searchPosts = async (withTitle = false) => {
return await db
.select({
id: posts.id,
...(withTitle && { title: posts.title }),
})
.from(posts);
};
await searchPosts();
await searchPosts(true);
```
```ts
// result type
type Result = {
id: number;
title?: string | undefined;
}[];
```
```ts copy
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
views: integer('views').notNull().default(0),
});
```
Source: https://orm.drizzle.team/docs/guides/incrementing-a-value
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Update statement](/docs/update)
- [Filters](/docs/operators) and [sql operator](/docs/sql)
To increment a column value you can use `update().set()` method like below:
```ts copy {8}
import { eq, sql } from 'drizzle-orm';
const db = drizzle(...)
await db
.update(table)
.set({
counter: sql`${table.counter} + 1`,
})
.where(eq(table.id, 1));
```
```sql
update "table" set "counter" = "counter" + 1 where "id" = 1;
```
Drizzle has simple and flexible API, which lets you easily create custom solutions. This is how you do custom increment function:
```ts copy {4,10,11}
import { AnyColumn } from 'drizzle-orm';
const increment = (column: AnyColumn, value = 1) => {
return sql`${column} + ${value}`;
};
await db
.update(table)
.set({
counter1: increment(table.counter1),
counter2: increment(table.counter2, 10),
})
.where(eq(table.id, 1));
```
Source: https://orm.drizzle.team/docs/guides/limit-offset-pagination
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Select statement](/docs/select) with [order by clause](/docs/select#order-by) and [limit & offset clauses](/docs/select#limit--offset)
- [Relational queries](/docs/rqb) with [order by clause](/docs/rqb#order-by) and [limit & offset clauses](/docs/rqb#limit--offset)
- [Dynamic query building](/docs/dynamic-query-building)
This guide demonstrates how to implement `limit/offset` pagination in Drizzle:
### PostgreSQL, MySQL, SQLite and Cockroach
To implement a PostgreSQL, MySQL, SQLite and Cockroach SQL Limit/Offset pagination (skip to [MSSQL](/docs/guides/limit-offset-pagination#mssql)) with Drizzle you can use following example:
```ts copy {9,10,11}
import { asc } from 'drizzle-orm';
import { users } from './schema';
const db = drizzle(...);
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.limit(4) // the number of rows to return
.offset(4); // the number of rows to skip
```
```sql
select * from users order by id asc limit 4 offset 4;
```
```ts
// 5-8 rows returned
[
{
id: 5,
firstName: 'Beth',
lastName: 'Davis',
createdAt: 2024-03-11T20:51:46.787Z
},
{
id: 6,
firstName: 'Charlie',
lastName: 'Miller',
createdAt: 2024-03-11T21:15:46.787Z
},
{
id: 7,
firstName: 'Clara',
lastName: 'Wilson',
createdAt: 2024-03-11T21:33:46.787Z
},
{
id: 8,
firstName: 'David',
lastName: 'Moore',
createdAt: 2024-03-11T21:45:46.787Z
}
]
```
```ts copy
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
firstName: text('first_name').notNull(),
lastName: text('last_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
```
```plaintext
+----+------------+-----------+----------------------------+
| id | first_name | last_name | created_at |
+----+------------+-----------+----------------------------+
| 1 | Alice | Johnson | 2024-03-08 12:23:55.251797 |
+----+------------+-----------+----------------------------+
| 2 | Alex | Smith | 2024-03-08 12:25:55.182 |
+----+------------+-----------+----------------------------+
| 3 | Aaron | Williams | 2024-03-08 12:28:55.182 |
+----+------------+-----------+----------------------------+
| 4 | Brian | Brown | 2024-03-08 12:34:55.182 |
+----+------------+-----------+----------------------------+
| 5 | Beth | Davis | 2024-03-08 12:40:55.182 |
+----+------------+-----------+----------------------------+
| 6 | Charlie | Miller | 2024-03-08 13:04:55.182 |
+----+------------+-----------+----------------------------+
| 7 | Clara | Wilson | 2024-03-08 13:22:55.182 |
+----+------------+-----------+----------------------------+
| 8 | David | Moore | 2024-03-08 13:34:55.182 |
+----+------------+-----------+----------------------------+
```
Limit is the number of rows to return `(page size)` and offset is the number of rows to skip `((page number - 1) * page size)`.
For consistent pagination, ensure ordering by a unique column. Otherwise, the results can be inconsistent.
If you need to order by a non-unique column, you should also append a unique column to the ordering.
This is how you can implement `limit/offset` pagination with 2 columns:
```ts copy {5}
const getUsers = async (page = 1, pageSize = 3) => {
await db
.select()
.from(users)
.orderBy(asc(users.firstName), asc(users.id)) // order by first_name (non-unique), id (pk)
.limit(pageSize)
.offset((page - 1) * pageSize);
}
await getUsers();
```
Drizzle has useful relational queries API, that lets you easily implement `limit/offset` pagination:
```ts copy {7,8,9}
import * as schema from './db/schema';
const db = drizzle({ schema });
const getUsers = async (page = 1, pageSize = 3) => {
await db.query.users.findMany({
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
offset: (page - 1) * pageSize,
});
};
await getUsers();
```
Drizzle has simple and flexible API, which lets you easily create custom solutions. This is how you can create custom function for pagination using `.$dynamic()` function:
```ts copy {11,12,13,16}
import { SQL, asc } from 'drizzle-orm';
import { PgColumn, PgSelect } from 'drizzle-orm/pg-core';
function withPagination(
qb: T,
orderByColumn: PgColumn | SQL | SQL.Aliased,
page = 1,
pageSize = 3,
) {
return qb
.orderBy(orderByColumn)
.limit(pageSize)
.offset((page - 1) * pageSize);
}
const query = db.select().from(users); // query that you want to execute with pagination
await withPagination(query.$dynamic(), asc(users.id));
```
You can improve performance of `limit/offset` pagination by using `deferred join` technique. This method performs the pagination on a subset of the data instead of the entire table.
To implement it you can do like this:
```ts copy {10}
const getUsers = async (page = 1, pageSize = 10) => {
const sq = db
.select({ id: users.id })
.from(users)
.orderBy(users.id)
.limit(pageSize)
.offset((page - 1) * pageSize)
.as('subquery');
await db.select().from(users).innerJoin(sq, eq(users.id, sq.id)).orderBy(users.id);
};
```
**Benefits** of `limit/offset` pagination: it's simple to implement and pages are easily reachable, which means that you can navigate to any page without having to save the state of the previous pages.
**Drawbacks** of `limit/offset` pagination: degradation in query performance with increasing offset because database has to scan all rows before the offset to skip them, and inconsistency due to data shifts, which can lead to the same row being returned on different pages or rows being skipped.
This is how it works:
```ts copy
const getUsers = async (page = 1, pageSize = 3) => {
await db
.select()
.from(users)
.orderBy(asc(users.id))
.limit(pageSize)
.offset((page - 1) * pageSize);
};
// user is browsing the first page
await getUsers();
```
```ts
// results for the first page
[
{
id: 1,
firstName: 'Alice',
lastName: 'Johnson',
createdAt: 2024-03-10T17:17:06.148Z
},
{
id: 2,
firstName: 'Alex',
lastName: 'Smith',
createdAt: 2024-03-10T17:19:06.147Z
},
{
id: 3,
firstName: 'Aaron',
lastName: 'Williams',
createdAt: 2024-03-10T17:22:06.147Z
}
]
```
```ts
// while user is browsing the first page, a row with id 2 is deleted
await db.delete(users).where(eq(users.id, 2));
// user navigates to the second page
await getUsers(2);
```
```ts
// second page, row with id 3 was skipped
[
{
id: 5,
firstName: 'Beth',
lastName: 'Davis',
createdAt: 2024-03-10T17:34:06.147Z
},
{
id: 6,
firstName: 'Charlie',
lastName: 'Miller',
createdAt: 2024-03-10T17:58:06.147Z
},
{
id: 7,
firstName: 'Clara',
lastName: 'Wilson',
createdAt: 2024-03-10T18:16:06.147Z
}
]
```
So, if your database experiences frequently insert and delete operations in real time or you need high performance to paginate large tables, you should consider using [cursor-based](/docs/guides/cursor-based-pagination) pagination instead.
To learn more about `deferred join` technique you should follow these guides: [Planetscale Pagination Guide](https://planetscale.com/blog/mysql-pagination) and [Efficient Pagination Guide by Aaron Francis](https://aaronfrancis.com/2022/efficient-pagination-using-deferred-joins).
{/*TODO finish this*/}
### MSSQL
To implement an MSSQL Limit/Offset pagination with Drizzle you can use following example:
```ts copy {9,10,11}
import { asc } from 'drizzle-orm';
import { users } from './schema';
const db = drizzle(...);
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.offset(4) // the number of rows to skip
.fetch(4) // the number of rows to fetch
```
```sql
select * from [users] order by [id] asc offset 5 rows fetch next 10 rows only;
```
```ts
// 5-8 rows returned
[
{
id: 5,
firstName: 'Beth',
lastName: 'Davis',
createdAt: 2024-03-11T20:51:46.787Z
},
{
id: 6,
firstName: 'Charlie',
lastName: 'Miller',
createdAt: 2024-03-11T21:15:46.787Z
},
{
id: 7,
firstName: 'Clara',
lastName: 'Wilson',
createdAt: 2024-03-11T21:33:46.787Z
},
{
id: 8,
firstName: 'David',
lastName: 'Moore',
createdAt: 2024-03-11T21:45:46.787Z
}
]
```
```ts copy
import { datetime2, int, mssqlTable, text } from "drizzle-orm/mssql-core";
export const users = mssqlTable("users", {
id: int("id").primaryKey(),
firstName: text("first_name").notNull(),
lastName: text("last_name").notNull(),
createdAt: datetime2("created_at", { mode: "date" }).notNull().defaultGetDate(),
});
```
```plaintext
+----+------------+-----------+----------------------------+
| id | first_name | last_name | created_at |
+----+------------+-----------+----------------------------+
| 1 | Alice | Johnson | 2024-03-08 12:23:55.251797 |
+----+------------+-----------+----------------------------+
| 2 | Alex | Smith | 2024-03-08 12:25:55.182 |
+----+------------+-----------+----------------------------+
| 3 | Aaron | Williams | 2024-03-08 12:28:55.182 |
+----+------------+-----------+----------------------------+
| 4 | Brian | Brown | 2024-03-08 12:34:55.182 |
+----+------------+-----------+----------------------------+
| 5 | Beth | Davis | 2024-03-08 12:40:55.182 |
+----+------------+-----------+----------------------------+
| 6 | Charlie | Miller | 2024-03-08 13:04:55.182 |
+----+------------+-----------+----------------------------+
| 7 | Clara | Wilson | 2024-03-08 13:22:55.182 |
+----+------------+-----------+----------------------------+
| 8 | David | Moore | 2024-03-08 13:34:55.182 |
+----+------------+-----------+----------------------------+
```
Source: https://orm.drizzle.team/docs/guides/mysql-local-setup
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Steps from '@mdx/Steps.astro';
- Install latest [Docker Desktop](https://www.docker.com/products/docker-desktop/). Follow the instructions for your operating system.
#### Pull the MySQL image
Pull the latest MySQL image from Docker Hub. In your terminal, run `docker pull mysql` to pull the latest MySQL version from Docker Hub:
```bash copy
docker pull mysql
```
Alternatively, you can pull preferred version with a specific tag:
```bash copy
docker pull mysql:8.2
```
When MySQL image is downloaded, you can check it in `Images` tab in Docker Desktop or by running `docker images`:
```bash copy
docker images
```
```plaintext
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest 4e8a34aea708 2 months ago 609MB
```
#### Start a MySQL instance
To start a new MySQL container, run the following command:
```bash copy
docker run --name drizzle-mysql -e MYSQL_ROOT_PASSWORD=mypassword -d -p 3306:3306 mysql
```
1. The `--name` option assigns the container the name `drizzle-mysql`.
2. The `-e MYSQL_ROOT_PASSWORD=` option sets the `MYSQL_ROOT_PASSWORD` environment variable with the specified value. This is password for the root user.
3. The `-d` flag runs the container in detached mode (in the background).
4. The `-p` option maps port `3306` on the container to port `3306` on your host machine, allowing MySQL to be accessed from your host system through this port.
5. The `mysql` argument specifies the image to use for the container. You can also specify other versions like `mysql:8.2`.
You can also specify other parameters like:
1. `-e MYSQL_DATABASE=` to create a new database when the container is created. Default is `mysql`.
2. `-e MYSQL_USER=` and `-e MYSQL_PASSWORD=` to create a new user with a password when the container is created. But you still need to specify `MYSQL_ROOT_PASSWORD` for `root` user.
To check if the container is running, check `Containers` tab in Docker Desktop or use the `docker ps` command:
```plaintext
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19506a8dc12b mysql "docker-entrypoint.sâĻ" 4 seconds ago Up 3 seconds 33060/tcp, 0.0.0.0:3306->3306/tcp drizzle-mysql
```
#### Configure database url
To connect to the MySQL database, you need to provide the database URL. The URL format is:
```plaintext
mysql://:@:/
```
You should replace placeholders with your actual values. For example, for created container the url will be:
```plaintext
mysql://root:mypassword@localhost:3306/mysql
```
Now you can connect to the database using the URL in your application.
Source: https://orm.drizzle.team/docs/guides/point-datatype-psql
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql)
- [Point datatype](/docs/column-types#point)
- [Filtering in select statement](/docs/select#filtering)
- [sql operator](/docs/sql)
PostgreSQL has a special datatype to store geometric data called `point`. It is used to represent a point in a two-dimensional space. The point datatype is represented as a pair of `(x, y)` coordinates.
The point expects to receive longitude first, followed by latitude.
```ts copy {6}
import { sql } from 'drizzle-orm';
const db = drizzle(...);
await db.execute(
sql`select point(-90.9, 18.7)`,
);
```
```json
[
{
point: '(-90.9,18.7)'
}
]
```
This is how you can create table with `point` datatype in Drizzle:
```ts {6}
import { pgTable, point, serial, text } from 'drizzle-orm/pg-core';
export const stores = pgTable('stores', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
location: point('location', { mode: 'xy' }).notNull(),
});
```
This is how you can insert point data into the table in Drizzle:
```ts {4, 10, 16}
// mode: 'xy'
await db.insert(stores).values({
name: 'Test',
location: { x: -90.9, y: 18.7 },
});
// mode: 'tuple'
await db.insert(stores).values({
name: 'Test',
location: [-90.9, 18.7],
});
// sql raw
await db.insert(stores).values({
name: 'Test',
location: sql`point(-90.9, 18.7)`,
});
```
To compute the distance between the objects you can use `<->` operator. This is how you can query for the nearest location by coordinates in Drizzle:
`getColumns` available starting from `drizzle-orm@1.0.0-beta.2`(read more [here](/docs/upgrade-v1))
If you are on pre-1 version(like `0.45.1`) then use `getTableColumns`
```ts {9, 14, 17}
import { getColumns, sql } from 'drizzle-orm';
import { stores } from './schema';
const point = {
x: -73.935_242,
y: 40.730_61,
};
const sqlDistance = sql`location <-> point(${point.x}, ${point.y})`;
await db
.select({
...getColumns(stores),
distance: sql`round((${sqlDistance})::numeric, 2)`,
})
.from(stores)
.orderBy(sqlDistance)
.limit(1);
```
```sql
select *, round((location <-> point(-73.935242, 40.73061))::numeric, 2)
from stores order by location <-> point(-73.935242, 40.73061)
limit 1;
```
To filter rows to include only those where a `point` type `location` falls within a specified rectangular boundary defined by two diagonal points you can user `<@` operator. It checks if the first object is contained in or on the second object:
```ts {12}
const point = {
x1: -88,
x2: -73,
y1: 40,
y2: 43,
};
await db
.select()
.from(stores)
.where(
sql`${stores.location} <@ box(point(${point.x1}, ${point.y1}), point(${point.x2}, ${point.y2}))`
);
```
```sql
select * from stores where location <@ box(point(-88, 40), point(-73, 43));
```
Source: https://orm.drizzle.team/docs/guides/postgis-geometry-point
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from "@mdx/Callout.astro";
import CodeTab from '@mdx/CodeTab.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql)
- [Postgis extension](/docs/extensions#postgis)
- [Indexes](/docs/indexes-constraints#indexes)
- [Filtering in select statement](/docs/select#filtering)
- [sql operator](/docs/sql)
`PostGIS` extends the capabilities of the PostgreSQL relational database by adding support for storing, indexing, and querying geospatial data.
As for now, Drizzle doesn't create extension automatically, so you need to create it manually. Create an empty migration file and add SQL query:
```bash
npx drizzle-kit generate --custom
```
```sql
CREATE EXTENSION postgis;
```
This is how you can create table with `geometry` datatype and spatial index in Drizzle:
```ts copy {8, 11}
import { geometry, index, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const stores = pgTable(
'stores',
{
id: serial('id').primaryKey(),
name: text('name').notNull(),
location: geometry('location', { type: 'point', mode: 'xy', srid: 4326 }).notNull(),
},
(t) => [
index('spatial_index').using('gist', t.location),
]
);
```
```sql
CREATE TABLE IF NOT EXISTS "stores" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"location" geometry(point) NOT NULL
);
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "spatial_index" ON "stores" USING gist ("location");
```
This is how you can insert `geometry` data into the table in Drizzle. `ST_MakePoint()` in PostGIS creates a geometric object of type `point` using the specified coordinates.
`ST_SetSRID()` sets the `SRID` (unique identifier associated with a specific coordinate system, tolerance, and resolution) on a geometry to a particular integer value:
```ts {4, 10, 16}
// mode: 'xy'
await db.insert(stores).values({
name: 'Test',
location: { x: -90.9, y: 18.7 },
});
// mode: 'tuple'
await db.insert(stores).values({
name: 'Test',
location: [-90.9, 18.7],
});
// sql raw
await db.insert(stores).values({
name: 'Test',
location: sql`ST_SetSRID(ST_MakePoint(-90.9, 18.7), 4326)`,
});
```
To compute the distance between the objects you can use `<->` operator and `ST_Distance()` function, which for `geometry types` returns the minimum planar distance between two geometries. This is how you can query for the nearest location by coordinates in Drizzle with PostGIS:
`getColumns` available starting from `drizzle-orm@1.0.0-beta.2`(read more [here](/docs/upgrade-v1))
If you are on pre-1 version(like `0.45.1`) then use `getTableColumns`
```ts copy {9, 14, 17}
import { getColumns, sql } from 'drizzle-orm';
import { stores } from './schema';
const point = {
x: -73.935_242,
y: 40.730_61,
};
const sqlPoint = sql`ST_SetSRID(ST_MakePoint(${point.x}, ${point.y}), 4326)`;
await db
.select({
...getColumns(stores),
distance: sql`ST_Distance(${stores.location}, ${sqlPoint})`,
})
.from(stores)
.orderBy(sql`${stores.location} <-> ${sqlPoint}`)
.limit(1);
```
```sql
select *, ST_Distance(location, ST_SetSRID(ST_MakePoint(-73.935_242, 40.730_61), 4326))
from stores order by location <-> ST_SetSRID(ST_MakePoint(-73.935_242, 40.730_61), 4326)
limit 1;
```
To filter stores located within a specified rectangular area, you can use `ST_MakeEnvelope()` and `ST_Within()` functions. `ST_MakeEnvelope()` creates a rectangular Polygon from the minimum and maximum values for X and Y. `ST_Within()` Returns TRUE if geometry A is within geometry B.
```ts copy {13}
const point = {
x1: -88,
x2: -73,
y1: 40,
y2: 43,
};
await db
.select()
.from(stores)
.where(
sql`ST_Within(
${stores.location}, ST_MakeEnvelope(${point.x1}, ${point.y1}, ${point.x2}, ${point.y2}, 4326)
)`,
);
```
```sql
select * from stores where ST_Within(location, ST_MakeEnvelope(-88, 40, -73, 43, 4326));
```
Source: https://orm.drizzle.team/docs/guides/postgresql-full-text-search
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql)
- [Select statement](/docs/select)
- [Indexes](/docs/indexes-constraints#indexes)
- [sql operator](/docs/sql)
- You should have `drizzle-orm@0.31.0` and `drizzle-kit@0.22.0` or higher.
This guide demonstrates how to implement full-text search in PostgreSQL with Drizzle ORM. Full-text search is a technique used to search for text within a document or a set of documents. A document is the unit of searching in a full text search system. PostgreSQL provides a set of functions to work with full-text search, such as `to_tsvector` and `to_tsquery`:
The `to_tsvector` function parses a textual document into tokens, reduces the tokens to lexemes, and returns a `tsvector` which lists the lexemes together with their positions in the document:
```ts copy {6}
import { sql } from 'drizzle-orm';
const db = drizzle(...);
await db.execute(
sql`select to_tsvector('english', 'Guide to PostgreSQL full-text search with Drizzle ORM')`,
);
```
```json
[
{
to_tsvector: "'drizzl':9 'full':5 'full-text':4
'guid':1 'orm':10 'postgresql':3 'search':7 'text':6"
}
]
```
The `to_tsquery` function converts a keyword to normalized tokens and returns a `tsquery` that matches the lexemes in a `tsvector`. The `@@` operator is used for direct matches:
```ts copy {2, 3}
await db.execute(
sql`select to_tsvector('english', 'Guide to PostgreSQL full-text search with Drizzle ORM')
@@ to_tsquery('english', 'Drizzle') as match`,
);
```
```json
[ { match: true } ]
```
As for now, Drizzle doesn't support `tsvector` type natively, so you need to convert your data in the `text` column on the fly. To enhance the performance, you can create a `GIN` index on your column like this:
```ts copy {10, 11}
import { index, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const posts = pgTable(
'posts',
{
id: serial('id').primaryKey(),
title: text('title').notNull(),
},
(table) => [
index('title_search_index').using('gin', sql`to_tsvector('english', ${table.title})`),
]
);
```
```sql
CREATE TABLE IF NOT EXISTS "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL
);
CREATE INDEX IF NOT EXISTS "title_search_index" ON "posts"
USING gin (to_tsvector('english', "title"));
```
```json
[
{ id: 1, title: 'Planning Your First Trip to Europe' },
{ id: 2, title: "Cultural Insights: Exploring Asia's Heritage" },
{ id: 3, title: 'Top 5 Destinations for a Family Trip' },
{ id: 4, title: 'Essential Hiking Gear for Mountain Enthusiasts' },
{ id: 5, title: 'Trip Planning: Choosing Your Next Destination' },
{ id: 6, title: 'Discovering Hidden Culinary Gems in Italy' },
{ id: 7, title: 'The Ultimate Road Trip Guide for Explorers' },
];
```
To implement full-text search in PostgreSQL with Drizzle ORM, you can use the `to_tsvector` and `to_tsquery` functions with `sql` operator:
```ts copy {9}
import { sql } from 'drizzle-orm';
import { posts } from './schema';
const title = 'trip';
await db
.select()
.from(posts)
.where(sql`to_tsvector('english', ${posts.title}) @@ to_tsquery('english', ${title})`);
```
```json
[
{ id: 1, title: 'Planning Your First Trip to Europe' },
{ id: 3, title: 'Top 5 Destinations for a Family Trip' },
{ id: 5, title: 'Trip Planning: Choosing Your Next Destination' },
{ id: 7, title: 'The Ultimate Road Trip Guide for Explorers' }
]
```
To match by any of the keywords, you can use the `|` operator:
```ts copy {6}
const title = 'Europe | Asia';
await db
.select()
.from(posts)
.where(sql`to_tsvector('english', ${posts.title}) @@ to_tsquery('english', ${title})`);
```
```json
[
{ id: 1, title: 'Planning Your First Trip to Europe' },
{ id: 2, title: "Cultural Insights: Exploring Asia's Heritage" }
]
```
To match multiple keywords, you can use the `plainto_tsquery` function:
```ts copy {7}
// 'discover & Italy'
const title = 'discover Italy';
await db
.select()
.from(posts)
.where(sql`to_tsvector('english', ${posts.title}) @@ plainto_tsquery('english', ${title})`);
```
```sql
select * from posts
where to_tsvector('english', title) @@ plainto_tsquery('english', 'discover Italy');
```
```json
[ { id: 6, title: 'Discovering Hidden Culinary Gems in Italy' } ]
```
To match a phrase, you can use the `phraseto_tsquery` function:
```ts copy {8}
// if you query by "trip family", it will not return any result
// 'family <-> trip'
const title = 'family trip';
await db
.select()
.from(posts)
.where(sql`to_tsvector('english', ${posts.title}) @@ phraseto_tsquery('english', ${title})`);
```
```sql
select * from posts
where to_tsvector('english', title) @@ phraseto_tsquery('english', 'family trip');
```
```json
[ { id: 3, title: 'Top 5 Destinations for a Family Trip' } ]
```
You can also use `websearch_to_tsquery` function which is a simplified version of `to_tsquery` with an alternative syntax, similar to the one used by web search engines:
```ts copy {7}
// 'family | first & trip & europ | asia'
const title = 'family or first trip Europe or Asia';
await db
.select()
.from(posts)
.where(sql`to_tsvector('english', ${posts.title}) @@ websearch_to_tsquery('english', ${title})`);
```
```sql
select * from posts
where to_tsvector('english', title)
@@ websearch_to_tsquery('english', 'family or first trip Europe or Asia');
```
```json
[
{ id: 1, title: 'Planning Your First Trip to Europe' },
{ id: 2, title: "Cultural Insights: Exploring Asia's Heritage" },
{ id: 3, title: 'Top 5 Destinations for a Family Trip' }
]
```
To implement full-text search on multiple columns, you can create index on multiple columns and concatenate the columns with `to_tsvector` function:
```ts copy {12-17}
import { sql } from 'drizzle-orm';
import { index, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const posts = pgTable(
'posts',
{
id: serial('id').primaryKey(),
title: text('title').notNull(),
description: text('description').notNull(),
},
(table) => [
index('search_index').using(
'gin',
sql`(
setweight(to_tsvector('english', ${table.title}), 'A') ||
setweight(to_tsvector('english', ${table.description}), 'B')
)`,
),
],
);
```
```sql
CREATE TABLE IF NOT EXISTS "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"description" text NOT NULL
);
CREATE INDEX IF NOT EXISTS "search_index" ON "posts"
USING gin ((setweight(to_tsvector('english', "title"), 'A') ||
setweight(to_tsvector('english', "description"), 'B')));
```
```json
[
{
id: 1,
title: 'Planning Your First Trip to Europe',
description:
'Get essential tips on budgeting, sightseeing, and cultural etiquette for your inaugural European adventure.',
},
{
id: 2,
title: "Cultural Insights: Exploring Asia's Heritage",
description:
'Dive deep into the rich history and traditions of Asia through immersive experiences and local interactions.',
},
{
id: 3,
title: 'Top 5 Destinations for a Family Trip',
description:
'Discover family-friendly destinations that offer fun, education, and relaxation for all ages.',
},
{
id: 4,
title: 'Essential Hiking Gear for Mountain Enthusiasts',
description:
'Equip yourself with the latest and most reliable gear for your next mountain hiking expedition.',
},
{
id: 5,
title: 'Trip Planning: Choosing Your Next Destination',
description:
'Learn how to select destinations that align with your travel goals, whether for leisure, adventure, or cultural exploration.',
},
{
id: 6,
title: 'Discovering Hidden Culinary Gems in Italy',
description:
"Unearth Italy's lesser-known eateries and food markets that offer authentic and traditional flavors.",
},
{
id: 7,
title: 'The Ultimate Road Trip Guide for Explorers',
description:
'Plan your next great road trip with tips on route planning, packing, and discovering off-the-beaten-path attractions.',
},
];
```
The `setweight` function is used to label the entries of a tsvector with a given weight, where a weight is one of the letters A, B, C, or D. This is typically used to mark entries coming from different parts of a document, such as title versus body.
This is how you can query on multiple columns:
```ts copy {5-7}
const title = 'plan';
await db.select().from(posts)
.where(sql`(
setweight(to_tsvector('english', ${posts.title}), 'A') ||
setweight(to_tsvector('english', ${posts.description}), 'B'))
@@ to_tsquery('english', ${title}
)`
);
```
```json
[
{
id: 1,
title: 'Planning Your First Trip to Europe',
description: 'Get essential tips on budgeting, sightseeing, and cultural etiquette for your inaugural European adventure.'
},
{
id: 5,
title: 'Trip Planning: Choosing Your Next Destination',
description: 'Learn how to select destinations that align with your travel goals, whether for leisure, adventure, or cultural exploration.'
},
{
id: 7,
title: 'The Ultimate Road Trip Guide for Explorers',
description: 'Plan your next great road trip with tips on route planning, packing, and discovering off-the-beaten-path attractions.'
}
]
```
To rank the search results, you can use the `ts_rank` or `ts_rank_cd` functions and `orderBy` method:
`getColumns` available starting from `drizzle-orm@1.0.0-beta.2`(read more [here](/docs/upgrade-v1))
If you are on pre-1 version(like `0.45.1`) then use `getTableColumns`
```ts copy {6,7,12,13,18-20}
import { desc, getColumns, sql } from 'drizzle-orm';
const search = 'culture | Europe | Italy | adventure';
const matchQuery = sql`(
setweight(to_tsvector('english', ${posts.title}), 'A') ||
setweight(to_tsvector('english', ${posts.description}), 'B')), to_tsquery('english', ${search})`;
await db
.select({
...getColumns(posts),
rank: sql`ts_rank(${matchQuery})`,
rankCd: sql`ts_rank_cd(${matchQuery})`,
})
.from(posts)
.where(
sql`(
setweight(to_tsvector('english', ${posts.title}), 'A') ||
setweight(to_tsvector('english', ${posts.description}), 'B')
) @@ to_tsquery('english', ${search})`,
)
.orderBy((t) => desc(t.rank));
```
```json
[
{
id: 1,
title: 'Planning Your First Trip to Europe',
description: 'Get essential tips on budgeting, sightseeing, and cultural etiquette for your inaugural European adventure.',
rank: 0.2735672,
rankCd: 1.8
},
{
id: 6,
title: 'Discovering Hidden Culinary Gems in Italy',
description: "Unearth Italy's lesser-known eateries and food markets that offer authentic and traditional flavors.",
rank: 0.16717994,
rankCd: 1.4
},
{
id: 2,
title: "Cultural Insights: Exploring Asia's Heritage",
description: 'Dive deep into the rich history and traditions of Asia through immersive experiences and local interactions.',
rank: 0.15198177,
rankCd: 1
},
{
id: 5,
title: 'Trip Planning: Choosing Your Next Destination',
description: 'Learn how to select destinations that align with your travel goals, whether for leisure, adventure, or cultural exploration.',
rank: 0.12158542,
rankCd: 0.8
}
]
```
The `ts_rank` function focuses on the frequency of query terms throughout the document. The `ts_rank_cd` function focuses on the proximity of query terms within the document.
Source: https://orm.drizzle.team/docs/guides/postgresql-local-setup
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Steps from '@mdx/Steps.astro';
- Install latest [Docker Desktop](https://www.docker.com/products/docker-desktop/). Follow the instructions for your operating system.
#### Pull the PostgreSQL image
Pull the latest PostgreSQL image from Docker Hub. In your terminal, run `docker pull postgres` to pull the latest Postgres version from Docker Hub:
```bash copy
docker pull postgres
```
Alternatively, you can pull preferred version with a specific tag:
```bash copy
docker pull postgres:15
```
When postgres image is downloaded, you can check it in `Images` tab in Docker Desktop or by running `docker images`:
```bash copy
docker images
```
```plaintext
REPOSITORY TAG IMAGE ID CREATED SIZE
postgres latest 75282fa229a1 6 weeks ago 453MB
```
#### Start a Postgres instance
To start a new PostgreSQL container, run the following command:
```bash copy
docker run --name drizzle-postgres -e POSTGRES_PASSWORD=mypassword -d -p 5432:5432 postgres
```
1. The `--name` option assigns the container the name `drizzle-postgres`.
2. The `-e POSTGRES_PASSWORD=` option sets the `POSTGRES_PASSWORD` environment variable with the specified value.
3. The `-d` flag runs the container in detached mode (in the background).
4. The `-p` option maps port `5432` on the container to port `5432` on your host machine, allowing PostgreSQL to be accessed from your host system through this port.
5. The `postgres` argument specifies the image to use for the container. You can also specify other versions like `postgres:15`.
You can also specify other parameters like:
1. The `-e POSTGRES_USER=` option sets the `POSTGRES_USER` environment variable with the specified value. Postgres uses the default user when this is empty. Most of the time, it is `postgres` and you can check it in the container logs in Docker Desktop or by running `docker logs `.
2. The `-e POSTGRES_DB=` option sets the `POSTGRES_DB` environment variable with the specified value. Defaults to the `POSTGRES_USER` value when is empty.
To check if the container is running, check `Containers` tab in Docker Desktop or use the `docker ps` command:
```plaintext
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df957c58a6a3 postgres "docker-entrypoint.sâĻ" 4 seconds ago Up 3 seconds 0.0.0.0:5432->5432/tcp drizzle-postgres
```
#### Configure database url
To connect to the PostgreSQL database, you need to provide the database URL. The URL format is:
```plaintext
postgres://:@:/
```
You should replace placeholders with your actual values. For example, for created container the url will be:
```plaintext
postgres://postgres:mypassword@localhost:5432/postgres
```
Now you can connect to the database using the URL in your application.
Source: https://orm.drizzle.team/docs/guides/seeding-using-with-option
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
import Section from "@mdx/Section.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- Get familiar with [One-to-many Relation](/docs/relations#one-to-many)
- Get familiar with [Drizzle Seed](/docs/seed-overview)
Using `with` implies tables to have a one-to-many relationship.
Therefore, if `one` user has `many` posts, you can use `with` as follows:
```ts
users: {
count: 2,
with: {
posts: 3,
},
},
```
## Example 1
```ts
import { users, posts } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users, posts }).refine(() => ({
users: {
count: 2,
with: {
posts: 3,
},
},
}));
}
main();
```
```ts
import { serial, pgTable, integer, text } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id').notNull(),
});
```
Running the seeding script above will cause an error.
```
Error: "posts" table doesn't have a reference to "users" table or
you didn't include your one-to-many relation in the seed function schema.
You can't specify "posts" as parameter in users.with object.
```
You will have several options to resolve an error:
- You can add reference to the `authorId` column in `posts` table in your schema
```ts
import { users, posts } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users, posts }).refine(() => ({
users: {
count: 2,
with: {
posts: 3,
},
},
}));
}
main();
// Running the seeding script above will fill you database with values shown below
```
```mdx
`users`
| id | name |
| -- | -------- |
| 1 | 'Melanny' |
| 2 | 'Elvera' |
`posts`
| id | content | author_id |
| -- | --------------------- | --------- |
| 1 | 'tf02gUXb0LZIdEg6SL' | 2 |
| 2 | 'j15YdT7Sma' | 2 |
| 3 | 'LwwvWtLLAZzIpk' | 1 |
| 4 | 'mgyUnBKSrQw' | 1 |
| 5 | 'CjAJByKIqilHcPjkvEw' | 2 |
| 6 | 'S5g0NzXs' | 1 |
```
```ts copy{11}
import { serial, pgTable, integer, text } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id').notNull().references(() => users.id),
});
```
- You can add one-to-many relation to your schema and include it in the seed function schema
```ts copy{1,5}
import { users, posts, postsRelations } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users, posts, postsRelations }).refine(() => ({
users: {
count: 2,
with: {
posts: 3,
},
},
}));
}
main();
// Running the seeding script above will fill you database with values shown below
```
```mdx
`users`
| id | name |
| -- | -------- |
| 1 | 'Melanny' |
| 2 | 'Elvera' |
`posts`
| id | content | author_id |
| -- | --------------------- | --------- |
| 1 | 'tf02gUXb0LZIdEg6SL' | 2 |
| 2 | 'j15YdT7Sma' | 2 |
| 3 | 'LwwvWtLLAZzIpk' | 1 |
| 4 | 'mgyUnBKSrQw' | 1 |
| 5 | 'CjAJByKIqilHcPjkvEw' | 2 |
| 6 | 'S5g0NzXs' | 1 |
```
```ts copy{2,15-20}
import { serial, pgTable, integer, text } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
## Example 2
```ts
import { users, posts } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users, posts }).refine(() => ({
posts: {
count: 2,
with: {
users: 3,
},
},
}));
}
main();
```
```ts
import { serial, pgTable, integer, text } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id').notNull().references(() => users.id),
});
```
Running the seeding script above will cause an error.
```
Error: "posts" table doesn't have a reference to "users" table or
you didn't include your one-to-many relation in the seed function schema.
You can't specify "posts" as parameter in users.with object.
```
You have a `posts` table referencing a `users` table in your schema,
```ts copy{7}
.
.
.
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id').notNull().references(() => users.id),
});
```
or in other words, you have one-to-many relation where `one` user can have `many` posts.
However, in your seeding script, you're attempting to generate 3 (`many`) users for `one` post.
```ts
posts: {
count: 2,
with: {
users: 3,
},
},
```
To resolve the error, you can modify your seeding script as follows:
```ts copy{6-9}
import { users, posts, postsRelations } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users, posts, postsRelations }).refine(() => ({
users: {
count: 2,
with: {
posts: 3,
},
},
}));
}
main();
// Running the seeding script above will fill you database with values shown below
```
```mdx
`users`
| id | name |
| -- | -------- |
| 1 | 'Melanny' |
| 2 | 'Elvera' |
`posts`
| id | content | author_id |
| -- | --------------------- | --------- |
| 1 | 'tf02gUXb0LZIdEg6SL' | 2 |
| 2 | 'j15YdT7Sma' | 2 |
| 3 | 'LwwvWtLLAZzIpk' | 1 |
| 4 | 'mgyUnBKSrQw' | 1 |
| 5 | 'CjAJByKIqilHcPjkvEw' | 2 |
| 6 | 'S5g0NzXs' | 1 |
```
## Example 3
```ts copy{6-9}
import { users } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { users }).refine(() => ({
users: {
count: 2,
with: {
users: 3,
},
},
}));
}
main();
```
```ts
import { serial, pgTable, integer, text } from "drizzle-orm/pg-core";
import type { AnyPgColumn } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
reportsTo: integer('reports_to').references((): AnyPgColumn => users.id),
});
```
Running the seeding script above will cause an error.
```
Error: "users" table has self reference.
You can't specify "users" as parameter in users.with object.
```
You have a `users` table referencing a `users` table in your schema,
```ts copy{7}
.
.
.
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
reportsTo: integer('reports_to').references((): AnyPgColumn => users.id),
});
```
or in other words, you have one-to-one relation where `one` user can have only `one` user.
However, in your seeding script, you're attempting to generate 3 (`many`) users for `one` user, which is impossible.
```ts
users: {
count: 2,
with: {
users: 3,
},
},
```
Source: https://orm.drizzle.team/docs/guides/seeding-with-partially-exposed-schema
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- Get familiar with [Drizzle Seed](/docs/seed-overview)
## Example 1
Let's assume you are trying to seed your database using the seeding script and schema shown below.
```ts
import { bloodPressure } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { bloodPressure });
}
main();
```
```ts copy {10}
import { serial, pgTable, integer, doublePrecision } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
});
export const bloodPressure = pgTable("bloodPressure", {
bloodPressureId: serial().primaryKey(),
pressure: doublePrecision(),
userId: integer().references(() => users.id).notNull(),
})
```
If the `bloodPressure` table has a not-null constraint on the `userId` column, running the seeding script will cause an error.
```
Error: Column 'userId' has not null constraint,
and you didn't specify a table for foreign key on column 'userId' in 'bloodPressure' table.
```
This means we can't fill the `userId` column with Null values due to the not-null constraint on that column.
Additionally, you didn't expose the `users` table to the `seed` function schema, so we can't generate `users.id` to populate the `userId` column with these values.
At this point, you have several options to resolve the error:
- You can remove the not-null constraint from the `userId` column;
- You can expose `users` table to `seed` function schema
```ts
await seed(db, { bloodPressure, users });
```
- You can [refine](/docs/guides/seeding-with-partially-exposed-schema#refining-the-userid-column-generator) the `userId` column generator;
## Example 2
```ts
import { bloodPressure } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { bloodPressure });
}
main();
```
```ts copy {10}
import { serial, pgTable, integer, doublePrecision } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
});
export const bloodPressure = pgTable("bloodPressure", {
bloodPressureId: serial().primaryKey(),
pressure: doublePrecision(),
userId: integer().references(() => users.id),
})
```
By running the seeding script above you will see a warning
```
Column 'userId' in 'bloodPressure' table will be filled with Null values
because you specified neither a table for foreign key on column 'userId'
nor a function for 'userId' column in refinements.
```
This means you neither provided the `users` table to the `seed` function schema nor refined the `userId` column generator.
As a result, the `userId` column will be filled with Null values.
Then you will have two choices:
- If you're okay with filling the `userId` column with Null values, you can ignore the warning;
- Otherwise, you can [refine](/docs/guides/seeding-with-partially-exposed-schema#refining-the-userid-column-generator) the `userId` column generator.
## Refining the `userId` column generator
Doing so requires the `users` table to already have IDs such as 1 and 2 in the database.
```ts copy {8}
import { bloodPressure } from './schema.ts';
async function main() {
const db = drizzle(...);
await seed(db, { bloodPressure }).refine((funcs) => ({
bloodPressure: {
columns: {
userId: funcs.valuesFromArray({ values: [1, 2] })
}
}
}));
}
main();
```
Source: https://orm.drizzle.team/docs/guides/select-parent-rows-with-at-least-one-related-child-row
import Prerequisites from "@mdx/Prerequisites.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from "@mdx/Section.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Select statement](/docs/select) and [select from subquery](/docs/select#select-from-subquery)
- [Inner join](/docs/joins#inner-join)
- [Filter operators](/docs/operators) and [exists function](/docs/operators#exists)
This guide demonstrates how to select parent rows with the condition of having at least one related child row. Below, there are examples of schema definitions and the corresponding database data:
```ts copy
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
userId: integer('user_id').notNull().references(() => users.id),
});
```
```plaintext
+----+------------+----------------------+
| id | name | email |
+----+------------+----------------------+
| 1 | John Doe | john_doe@email.com |
+----+------------+----------------------+
| 2 | Tom Brown | tom_brown@email.com |
+----+------------+----------------------+
| 3 | Nick Smith | nick_smith@email.com |
+----+------------+----------------------+
```
```plaintext
+----+--------+-----------------------------+---------+
| id | title | content | user_id |
+----+--------+-----------------------------+---------+
| 1 | Post 1 | This is the text of post 1 | 1 |
+----+--------+-----------------------------+---------+
| 2 | Post 2 | This is the text of post 2 | 1 |
+----+--------+-----------------------------+---------+
| 3 | Post 3 | This is the text of post 3 | 3 |
+----+--------+-----------------------------+---------+
```
To select parent rows with at least one related child row and retrieve child data you can use `.innerJoin()` method:
```ts copy {12}
import { eq } from 'drizzle-orm';
import { users, posts } from './schema';
const db = drizzle(...);
await db
.select({
user: users,
post: posts,
})
.from(users)
.innerJoin(posts, eq(users.id, posts.userId));
.orderBy(users.id);
```
```sql
select users.*, posts.* from users
inner join posts on users.id = posts.user_id
order by users.id;
```
```ts
// result data, there is no user with id 2 because he has no posts
[
{
user: { id: 1, name: 'John Doe', email: 'john_doe@email.com' },
post: {
id: 1,
title: 'Post 1',
content: 'This is the text of post 1',
userId: 1
}
},
{
user: { id: 1, name: 'John Doe', email: 'john_doe@email.com' },
post: {
id: 2,
title: 'Post 2',
content: 'This is the text of post 2',
userId: 1
}
},
{
user: { id: 3, name: 'Nick Smith', email: 'nick_smith@email.com' },
post: {
id: 3,
title: 'Post 3',
content: 'This is the text of post 3',
userId: 3
}
}
]
```
To only select parent rows with at least one related child row you can use subquery with `exists()` function like this:
```ts copy {8}
import { eq, exists, sql } from 'drizzle-orm';
const sq = db
.select({ id: sql`1` })
.from(posts)
.where(eq(posts.userId, users.id));
await db.select().from(users).where(exists(sq));
```
```sql
select * from users where exists (select 1 from posts where posts.user_id = users.id);
```
```ts
// result data, there is no user with id 2 because he has no posts
[
{ id: 1, name: 'John Doe', email: 'john_doe@email.com' },
{ id: 3, name: 'Nick Smith', email: 'nick_smith@email.com' }
]
```
Source: https://orm.drizzle.team/docs/guides/timestamp-default-value
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql) and [SQLite](/docs/sqlite/get-started-sqlite)
- Learn about column data types for [PostgreSQL](/docs/column-types), [MySQL](/docs/mysql/column-types) and [SQLite](/docs/sqlite/column-types)
- [sql operator](/docs/sql)
### PostgreSQL
To set current timestamp as a default value in PostgreSQL, you can use the `defaultNow()` method or `sql` operator with `now()` function which returns the current date and time with the time zone:
```ts copy {6,9}
import { sql } from 'drizzle-orm';
import { timestamp, pgTable, serial } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
timestamp1: timestamp('timestamp1').notNull().defaultNow(),
timestamp2: timestamp('timestamp2', { mode: 'string' })
.notNull()
.default(sql`now()`),
});
```
```sql
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"timestamp1" timestamp DEFAULT now() NOT NULL,
"timestamp2" timestamp DEFAULT now() NOT NULL
);
```
The `mode` option defines how values are handled in the application. Values with `string` mode are treated as `string` in the application, but stored as timestamps in the database.
```plaintext
// Data stored in the database
+----+----------------------------+----------------------------+
| id | timestamp1 | timestamp2 |
+----+----------------------------+----------------------------+
| 1 | 2024-04-11 14:14:28.038697 | 2024-04-11 14:14:28.038697 |
+----+----------------------------+----------------------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp1: 2024-04-11T14:14:28.038Z, // Date object
timestamp2: '2024-04-11 14:14:28.038697' // string
}
]
```
To set unix timestamp as a default value in PostgreSQL, you can use the `sql` operator and `extract(epoch from now())` function which returns the number of seconds since `1970-01-01 00:00:00 UTC`:
```ts copy {8}
import { sql } from 'drizzle-orm';
import { integer, pgTable, serial } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
timestamp: integer('timestamp')
.notNull()
.default(sql`extract(epoch from now())`),
});
```
```sql
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"timestamp" integer DEFAULT extract(epoch from now()) NOT NULL
);
```
```plaintext
// Data stored in the database
+----+------------+
| id | timestamp |
+----+------------+
| 1 | 1712846784 |
+----+------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp: 1712846784 // number
}
]
```
### MySQL
To set current timestamp as a default value in MySQL, you can use the `defaultNow()` method or `sql` operator with `now()` function which returns the current date and time `(YYYY-MM-DD HH-MM-SS)`:
```ts copy {6,9,12}
import { sql } from 'drizzle-orm';
import { mysqlTable, serial, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: serial('id').primaryKey(),
timestamp1: timestamp('timestamp1').notNull().defaultNow(),
timestamp2: timestamp('timestamp2', { mode: 'string' })
.notNull()
.default(sql`now()`),
timestamp3: timestamp('timestamp3', { fsp: 3 }) // fractional seconds part
.notNull()
.default(sql`now(3)`),
});
```
```sql
CREATE TABLE `users` (
`id` serial AUTO_INCREMENT NOT NULL,
`timestamp1` timestamp NOT NULL DEFAULT now(),
`timestamp2` timestamp NOT NULL DEFAULT now(),
`timestamp3` timestamp(3) NOT NULL DEFAULT now(3),
CONSTRAINT `users_id` PRIMARY KEY(`id`)
);
```
`fsp` option defines the number of fractional seconds to include in the timestamp. The default value is `0`.
The `mode` option defines how values are handled in the application. Values with `string` mode are treated as `string` in the application, but stored as timestamps in the database.
```plaintext
// Data stored in the database
+----+---------------------+---------------------+-------------------------+
| id | timestamp1 | timestamp2 | timestamp3 |
+----+---------------------+---------------------+-------------------------+
| 1 | 2024-04-11 15:24:53 | 2024-04-11 15:24:53 | 2024-04-11 15:24:53.236 |
+----+---------------------+---------------------+-------------------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp1: 2024-04-11T15:24:53.000Z, // Date object
timestamp2: '2024-04-11 15:24:53', // string
timestamp3: 2024-04-11T15:24:53.236Z // Date object
}
]
```
To set unix timestamp as a default value in MySQL, you can use the `sql` operator and `unix_timestamp()` function which returns the number of seconds since `1970-01-01 00:00:00 UTC`:
```ts copy {8}
import { sql } from 'drizzle-orm';
import { mysqlTable, serial, int } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: serial('id').primaryKey(),
timestamp: int('timestamp')
.notNull()
.default(sql`(unix_timestamp())`),
});
```
```sql
CREATE TABLE `users` (
`id` serial AUTO_INCREMENT NOT NULL,
`timestamp` int NOT NULL DEFAULT (unix_timestamp()),
CONSTRAINT `users_id` PRIMARY KEY(`id`)
);
```
```plaintext
// Data stored in the database
+----+------------+
| id | timestamp |
+----+------------+
| 1 | 1712847986 |
+----+------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp: 1712847986 // number
}
]
```
### SQLite
To set current timestamp as a default value in SQLite, you can use `sql` operator with `current_timestamp` constant which returns text representation of the current UTC date and time `(YYYY-MM-DD HH:MM:SS)`:
```ts copy {8}
import { sql } from 'drizzle-orm';
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
timestamp: text('timestamp')
.notNull()
.default(sql`(current_timestamp)`),
});
```
```sql
CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`timestamp` text DEFAULT (current_timestamp) NOT NULL
);
```
```plaintext
// Data stored in the database
+----+---------------------+
| id | timestamp |
+----+---------------------+
| 1 | 2024-04-11 15:40:43 |
+----+---------------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp: '2024-04-11 15:40:43' // string
}
]
```
To set unix timestamp as a default value in SQLite, you can use the `sql` operator and `unixepoch()` function which returns the number of seconds since `1970-01-01 00:00:00 UTC`:
```ts copy {8,11,14}
import { sql } from 'drizzle-orm';
import { integer, sqliteTable } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
timestamp1: integer('timestamp1', { mode: 'timestamp' })
.notNull()
.default(sql`(unixepoch())`),
timestamp2: integer('timestamp2', { mode: 'timestamp_ms' })
.notNull()
.default(sql`(unixepoch() * 1000)`),
timestamp3: integer('timestamp3', { mode: 'number' })
.notNull()
.default(sql`(unixepoch())`),
});
```
```sql
CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`timestamp1` integer DEFAULT (unixepoch()) NOT NULL,
`timestamp2` integer DEFAULT (unixepoch() * 1000) NOT NULL,
`timestamp3` integer DEFAULT (unixepoch()) NOT NULL
);
```
The `mode` option defines how values are handled in the application. In the application, values with `timestamp` and `timestamp_ms` modes are treated as `Date` objects, but stored as integers in the database.
The difference is that `timestamp` handles seconds, while `timestamp_ms` handles milliseconds.
```plaintext
// Data stored in the database
+------------+------------+---------------+------------+
| id | timestamp1 | timestamp2 | timestamp3 |
+------------+------------+---------------+------------+
| 1 | 1712835640 | 1712835640000 | 1712835640 |
+------------+------------+---------------+------------+
```
```ts
// Data returned by the application
[
{
id: 1,
timestamp1: 2024-04-11T11:40:40.000Z, // Date object
timestamp2: 2024-04-11T11:40:40.000Z, // Date object
timestamp3: 1712835640 // number
}
]
```
Source: https://orm.drizzle.team/docs/guides/toggling-a-boolean-field
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Update statement](/docs/update)
- [Filters](/docs/operators) and [not operator](/docs/operators#not)
- Boolean data type in [MySQL](/docs/mysql/column-types#boolean) and [SQLite](/docs/sqlite/column-types#boolean)
To toggle a column value you can use `update().set()` method like below:
```tsx copy {8}
import { eq, not } from 'drizzle-orm';
const db = drizzle(...);
await db
.update(table)
.set({
isActive: not(table.isActive),
})
.where(eq(table.id, 1));
```
```sql
update "table" set "is_active" = not "is_active" where "id" = 1;
```
Please note that there is no boolean type in MySQL and SQLite.
MySQL uses tinyint(1).
SQLite uses integers 0 (false) and 1 (true).
Source: https://orm.drizzle.team/docs/guides/unique-case-insensitive-email
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql) and [SQLite](/docs/sqlite/get-started-sqlite)
- [Indexes](/docs/indexes-constraints#indexes)
- [Insert statement](/docs/insert) and [Select method](/docs/select)
- [sql operator](/docs/sql)
- You should have `drizzle-orm@0.31.0` and `drizzle-kit@0.22.0` or higher.
### PostgreSQL
To implement a unique and case-insensitive `email` handling in PostgreSQL with Drizzle, you can create a unique index on the lowercased `email` column. This way, you can ensure that the `email` is unique regardless of the case.
Drizzle has simple and flexible API, which lets you easily create such an index using SQL-like syntax:
```ts copy {12,13}
import { SQL, sql } from 'drizzle-orm';
import { AnyPgColumn, pgTable, serial, text, uniqueIndex } from 'drizzle-orm/pg-core';
export const users = pgTable(
'users',
{
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
uniqueIndex('emailUniqueIndex').on(lower(table.email)),
],
);
// custom lower function
export function lower(email: AnyPgColumn): SQL {
return sql`lower(${email})`;
}
```
```sql
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"email" text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "emailUniqueIndex" ON "users" USING btree (lower("email"));
```
This is how you can select user by `email` with `lower` function:
```ts copy {10}
import { eq } from 'drizzle-orm';
import { lower, users } from './schema';
const db = drizzle(...);
const findUserByEmail = async (email: string) => {
return await db
.select()
.from(users)
.where(eq(lower(users.email), email.toLowerCase()));
};
```
```sql
select * from "users" where lower(email) = 'john@email.com';
```
### MySQL
In MySQL, the default collation setting for string comparison is case-insensitive, which means that when performing operations like searching or comparing strings in SQL queries, the case of the characters does not affect the results. However, because collation settings can vary and may be configured to be case-sensitive, we will explicitly ensure that the `email` is unique regardless of case by creating a unique index on the lowercased `email` column.
Drizzle has simple and flexible API, which lets you easily create such an index using SQL-like syntax:
```ts copy {12,13}
import { SQL, sql } from 'drizzle-orm';
import { AnyMySqlColumn, mysqlTable, serial, uniqueIndex, varchar } from 'drizzle-orm/mysql-core';
export const users = mysqlTable(
'users',
{
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }).notNull(),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`(lower(${table.email}))`),
uniqueIndex('emailUniqueIndex').on(lower(table.email)),
]
);
// custom lower function
export function lower(email: AnyMySqlColumn): SQL {
return sql`(lower(${email}))`;
}
```
```sql
CREATE TABLE `users` (
`id` serial AUTO_INCREMENT NOT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
CONSTRAINT `users_id` PRIMARY KEY(`id`),
CONSTRAINT `emailUniqueIndex` UNIQUE((lower(`email`)))
);
```
Functional indexes are supported in MySQL starting from version `8.0.13`. For the correct syntax, the expression should be enclosed in parentheses, for example, `(lower(column))`.
This is how you can select user by `email` with `lower` function:
```ts copy {10}
import { eq } from 'drizzle-orm';
import { lower, users } from './schema';
const db = drizzle(...);
const findUserByEmail = async (email: string) => {
return await db
.select()
.from(users)
.where(eq(lower(users.email), email.toLowerCase()));
};
```
```sql
select * from `users` where lower(email) = 'john@email.com';
```
### SQLite
To implement a unique and case-insensitive `email` handling in SQLite with Drizzle, you can create a unique index on the lowercased `email` column. This way, you can ensure that the `email` is unique regardless of the case.
Drizzle has simple and flexible API, which lets you easily create such an index using SQL-like syntax:
```ts copy {12,13}
import { SQL, sql } from 'drizzle-orm';
import { AnySQLiteColumn, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable(
'users',
{
id: integer('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
uniqueIndex('emailUniqueIndex').on(lower(table.email)),
]
);
// custom lower function
export function lower(email: AnySQLiteColumn): SQL {
return sql`lower(${email})`;
}
```
```sql
CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`email` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `emailUniqueIndex` ON `users` (lower(`email`));
```
This is how you can select user by `email` with `lower` function:
```ts copy {10}
import { eq } from 'drizzle-orm';
import { lower, users } from './schema';
const db = drizzle(...);
const findUserByEmail = async (email: string) => {
return await db
.select()
.from(users)
.where(eq(lower(users.email), email.toLowerCase()));
};
```
```sql
select * from "users" where lower(email) = 'john@email.com';
```
Source: https://orm.drizzle.team/docs/guides/update-many-with-different-value
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Update statement](/docs/update)
- [Filters](/docs/operators) and [sql operator](/docs/sql)
To implement update many with different values for each row within 1 request you can use `sql` operator with `case` statement and `.update().set()` methods like this:
```ts {26, 29, 32, 36, 38, 40}
import { SQL, inArray, sql } from 'drizzle-orm';
import { users } from './schema';
const db = drizzle(...);
const inputs = [
{
id: 1,
city: 'New York',
},
{
id: 2,
city: 'Los Angeles',
},
{
id: 3,
city: 'Chicago',
},
];
// You have to be sure that inputs array is not empty
if (inputs.length === 0) {
return;
}
const sqlChunks: SQL[] = [];
const ids: number[] = [];
sqlChunks.push(sql`(case`);
for (const input of inputs) {
sqlChunks.push(sql`when ${users.id} = ${input.id} then ${input.city}`);
ids.push(input.id);
}
sqlChunks.push(sql`end)`);
const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
await db.update(users).set({ city: finalSql }).where(inArray(users.id, ids));
```
```sql
update users set "city" =
(case when id = 1 then 'New York' when id = 2 then 'Los Angeles' when id = 3 then 'Chicago' end)
where id in (1, 2, 3)
```
Source: https://orm.drizzle.team/docs/guides/upsert
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from "@mdx/Callout.astro";
import CodeTab from '@mdx/CodeTab.astro';
- Get started with [PostgreSQL](/docs/get-started-postgresql), [MySQL](/docs/mysql/get-started-mysql), [SQLite](/docs/sqlite/get-started-sqlite), [MSSQL](/docs/mssql/get-started-mssql) and [Cockroach](/docs/cockroach/get-started-cockroach)
- [Insert statement](/docs/insert), [onConflictDoUpdate method](/docs/insert#on-conflict-do-update) and [onDuplicateKeyUpdate method](/docs/mysql/insert#on-duplicate-key-update)
- [Composite primary key](/docs/indexes-constraints#composite-primary-key)
- [sql operator](/docs/sql)
**SQL Server (MSSQL)** does not support the ***ON CONFLICT DO UPDATE*** syntax
### PostgreSQL, Cockroach and SQLite
To implement an upsert query in PostgreSQL, Cockroach and SQLite (skip to [MySQL](/docs/guides/upsert#mysql)) with Drizzle you can use `.onConflictDoUpdate()` method:
```ts copy {8,9,10,11}
import { users } from './schema';
const db = drizzle(...);
await db
.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoUpdate({
target: users.id,
set: { name: 'Super John' },
});
```
```sql
insert into users ("id", "name") values (1, 'John')
on conflict ("id") do update set name = 'Super John';
```
To upsert multiple rows in one query in PostgreSQL, Cockroach and SQLite you can use `sql operator` and `excluded` keyword. `excluded` is a special reference that refer to the row that was proposed for insertion, but wasn't inserted because of the conflict.
This is how you can do it:
```ts copy {21,24}
import { sql } from 'drizzle-orm';
import { users } from './schema';
const values = [
{
id: 1,
lastLogin: new Date(),
},
{
id: 2,
lastLogin: new Date(Date.now() + 1000 * 60 * 60),
},
{
id: 3,
lastLogin: new Date(Date.now() + 1000 * 60 * 120),
},
];
await db
.insert(users)
.values(values)
.onConflictDoUpdate({
target: users.id,
set: { lastLogin: sql.raw(`excluded.${users.lastLogin.name}`) },
});
```
```sql
insert into users ("id", "last_login")
values
(1, '2024-03-15T22:29:06.679Z'),
(2, '2024-03-15T23:29:06.679Z'),
(3, '2024-03-16T00:29:06.679Z')
on conflict ("id") do update set last_login = excluded.last_login;
```
```ts copy
import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
lastLogin: timestamp('last_login', { mode: 'date' }).notNull(),
});
```
Drizzle has simple and flexible API, which lets you easily create custom solutions. This is how you do custom function for updating specific columns in multiple rows due to the conflict in PostgreSQL, Cockroach and SQLite:
```ts copy {43,46}
import { SQL, getColumns, sql } from 'drizzle-orm';
import { PgTable } from 'drizzle-orm/pg-core';
import { SQLiteTable } from 'drizzle-orm/sqlite-core';
import { CockroachTable } from "drizzle-orm/cockroach-core";
import { users } from './schema';
const buildConflictUpdateColumns = <
T extends PgTable | CockroachTable | SQLiteTable,
Q extends keyof T['_']['columns']
>(
table: T,
columns: Q[],
) => {
const cls = getColumns(table);
return columns.reduce((acc, column) => {
const colName = cls[column].name;
acc[column] = sql.raw(`excluded.${colName}`);
return acc;
}, {} as Record);
};
const values = [
{
id: 1,
lastLogin: new Date(),
active: true,
},
{
id: 2,
lastLogin: new Date(Date.now() + 1000 * 60 * 60),
active: true,
},
{
id: 3,
lastLogin: new Date(Date.now() + 1000 * 60 * 120),
active: true,
},
];
await db
.insert(users)
.values(values)
.onConflictDoUpdate({
target: users.id,
set: buildConflictUpdateColumns(users, ['lastLogin', 'active']),
});
```
```sql
insert into users ("id", "last_login", "active")
values
(1, '2024-03-16T15:44:41.141Z', true),
(2, '2024-03-16T16:44:41.141Z', true),
(3, '2024-03-16T17:44:41.141Z', true)
on conflict ("id") do update set last_login = excluded.last_login, active = excluded.active;
```
```ts copy
import { boolean, pgTable, serial, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
lastLogin: timestamp('last_login', { mode: 'date' }).notNull(),
active: boolean('active').notNull().default(false),
});
```
This is how you can implement an upsert query with multiple targets in PostgreSQL, Cockroach and SQLite:
```ts copy {8}
import { sql } from 'drizzle-orm';
import { inventory } from './schema';
await db
.insert(inventory)
.values({ warehouseId: 1, productId: 1, quantity: 100 })
.onConflictDoUpdate({
target: [inventory.warehouseId, inventory.productId], // composite primary key
set: { quantity: sql`${inventory.quantity} + 100` }, // add 100 to the existing quantity
});
```
```sql
insert into inventory ("warehouse_id", "product_id", "quantity") values (1, 1, 100)
on conflict ("warehouse_id","product_id") do update set quantity = quantity + 100;
```
If you want to implement upsert query with `where` clause for `update` statement, you can use `setWhere` property in `onConflictDoUpdate` method:
```ts copy {25,26,27,28}
import { or, sql } from 'drizzle-orm';
import { products } from './schema';
const data = {
id: 1,
title: 'Phone',
price: '999.99',
stock: 10,
lastUpdated: new Date(),
};
const excludedPrice = sql.raw(`excluded.${products.price.name}`);
const excludedStock = sql.raw(`excluded.${products.stock.name}`);
await db
.insert(products)
.values(data)
.onConflictDoUpdate({
target: products.id,
set: {
price: excludedPrice,
stock: excludedStock,
lastUpdated: sql.raw(`excluded.${products.lastUpdated.name}`)
},
setWhere: or(
sql`${products.stock} != ${excludedStock}`,
sql`${products.price} != ${excludedPrice}`
),
});
```
```sql
insert into products ("id", "title", "stock", "price", "last_updated")
values (1, 'Phone', 10, '999.99', '2024-04-29T21:56:55.563Z')
on conflict ("id") do update
set stock = excluded.stock, price = excluded.price, last_updated = excluded.last_updated
where (stock != excluded.stock or price != excluded.price);
```
```ts copy
import { integer, numeric, pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const products = pgTable('products', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
stock: integer('stock').notNull(),
price: numeric('price', { precision: 10, scale: 2 }).notNull(),
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
});
```
If you want to update all columns except of specific one, you can leave the previous value like this:
```ts copy {16}
import { sql } from 'drizzle-orm';
import { users } from './schema';
const data = {
id: 1,
name: 'John',
email: 'john@email.com',
age: 29,
};
await db
.insert(users)
.values(data)
.onConflictDoUpdate({
target: users.id,
set: { ...data, email: sql`${users.email}` }, // leave email as it was
});
```
```sql
insert into users ("id", "name", "email", "age") values (1, 'John', 'john@email.com', 29)
on conflict ("id") do update set id = 1, name = 'John', email = email, age = 29;
```
### MySQL
To implement an upsert query in MySQL with Drizzle you can use `.onDuplicateKeyUpdate()` method. MySQL will automatically determine the conflict target based on the primary key and unique indexes, and will update the row if any unique index conflicts.
This is how you can do it:
```ts copy {4}
await db
.insert(users)
.values({ id: 1, name: 'John' })
.onDuplicateKeyUpdate({ set: { name: 'Super John' } });
```
```sql
insert into users (`id`, `first_name`) values (1, 'John')
on duplicate key update first_name = 'Super John';
```
To upsert multiple rows in one query in MySQL you can use `sql operator` and `values()` function. `values()` function refers to the value of column that would be inserted if duplicate-key conflict hadn't occurred.
```ts copy {21,24}
import { sql } from 'drizzle-orm';
import { users } from './schema';
const values = [
{
id: 1,
lastLogin: new Date(),
},
{
id: 2,
lastLogin: new Date(Date.now() + 1000 * 60 * 60),
},
{
id: 3,
lastLogin: new Date(Date.now() + 1000 * 60 * 120),
},
];
await db
.insert(users)
.values(values)
.onDuplicateKeyUpdate({
set: {
lastLogin: sql`values(${users.lastLogin})`,
},
});
```
```sql
insert into users (`id`, `last_login`)
values
(1, '2024-03-15 23:08:27.025'),
(2, '2024-03-15 00:08:27.025'),
(3, '2024-03-15 01:08:27.025')
on duplicate key update last_login = values(last_login);
```
```ts copy
import { mysqlTable, serial, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: serial('id').primaryKey(),
lastLogin: timestamp('last_login', { mode: 'date' }).notNull(),
});
```
Drizzle has simple and flexible API, which lets you easily create custom solutions. This is how you do custom function for updating specific columns in multiple rows due to the conflict in MySQL:
```ts copy {36,38}
import { SQL, getColumns, sql } from 'drizzle-orm';
import { MySqlTable } from 'drizzle-orm/mysql-core';
import { users } from './schema';
const buildConflictUpdateColumns = (
table: T,
columns: Q[],
) => {
const cls = getColumns(table);
return columns.reduce((acc, column) => {
acc[column] = sql`values(${cls[column]})`;
return acc;
}, {} as Record);
};
const values = [
{
id: 1,
lastLogin: new Date(),
active: true,
},
{
id: 2,
lastLogin: new Date(Date.now() + 1000 * 60 * 60),
active: true,
},
{
id: 3,
lastLogin: new Date(Date.now() + 1000 * 60 * 120),
active: true,
},
];
await db
.insert(users)
.values(values)
.onDuplicateKeyUpdate({
set: buildConflictUpdateColumns(users, ['lastLogin', 'active']),
});
```
```sql
insert into users (`id`, `last_login`, `active`)
values
(1, '2024-03-16 15:23:28.013', true),
(2, '2024-03-16 16:23:28.013', true),
(3, '2024-03-16 17:23:28.013', true)
on duplicate key update last_login = values(last_login), active = values(active);
```
```ts copy
import { boolean, mysqlTable, serial, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: serial('id').primaryKey(),
lastLogin: timestamp('last_login', { mode: 'date' }).notNull(),
active: boolean('active').notNull().default(false),
});
```
If you want to update all columns except of specific one, you can leave the previous value like this:
```ts copy {15}
import { sql } from 'drizzle-orm';
import { users } from './schema';
const data = {
id: 1,
name: 'John',
email: 'john@email.com',
age: 29,
};
await db
.insert(users)
.values(data)
.onDuplicateKeyUpdate({
set: { ...data, email: sql`${users.email}` }, // leave email as it was
});
```
```sql
insert into users (`id`, `name`, `email`, `age`) values (1, 'John', 'john@email.com', 29)
on duplicate key update id = 1, name = 'John', email = email, age = 29;
```
Source: https://orm.drizzle.team/docs/guides/vector-similarity-search
import Section from "@mdx/Section.astro";
import IsSupportedChipGroup from "@mdx/IsSupportedChipGroup.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Npm from "@mdx/Npm.astro";
- Get started with [PostgreSQL](/docs/get-started-postgresql)
- [Select statement](/docs/select)
- [Indexes](/docs/indexes-constraints#indexes)
- [sql operator](/docs/sql)
- [pgvector extension](/docs/extensions#pg_vector)
- [Drizzle kit](/docs/kit-overview)
- You should have installed the `openai` [package](https://www.npmjs.com/package/openai) for generating embeddings.
openai
- You should have `drizzle-orm@0.31.0` and `drizzle-kit@0.22.0` or higher.
To implement vector similarity search in PostgreSQL with Drizzle ORM, you can use the `pgvector` extension. This extension provides a set of functions to work with vectors and perform similarity search.
As for now, Drizzle doesn't create extension automatically, so you need to create it manually. Create an empty migration file and add SQL query:
```bash
npx drizzle-kit generate --custom
```
```sql
CREATE EXTENSION vector;
```
To perform similarity search, you need to create a table with a vector column and an `HNSW` or `IVFFlat` index on this column for better performance:
```ts copy {10, 13}
import { index, pgTable, serial, text, vector } from 'drizzle-orm/pg-core';
export const guides = pgTable(
'guides',
{
id: serial('id').primaryKey(),
title: text('title').notNull(),
description: text('description').notNull(),
url: text('url').notNull(),
embedding: vector('embedding', { dimensions: 1536 }),
},
(table) => [
index('embeddingIndex').using('hnsw', table.embedding.op('vector_cosine_ops')),
]
);
```
```sql
CREATE TABLE IF NOT EXISTS "guides" (
"id" serial PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"description" text NOT NULL,
"url" text NOT NULL,
"embedding" vector(1536)
);
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "embeddingIndex" ON "guides" USING hnsw (embedding vector_cosine_ops);
```
The `embedding` column is used to store vector embeddings of the guide descriptions. Vector embedding is just a representation of some data. It converts different types of data into a common format (vectors) that language models can process. This allows us to perform mathematical operations, such as measuring the distance between two vectors, to determine how similar or different two data items are.
In this example we will use `OpenAI` model to generate [embeddings](https://platform.openai.com/docs/guides/embeddings) for the description:
```ts copy
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env['OPENAI_API_KEY'],
});
export const generateEmbedding = async (value: string): Promise => {
const input = value.replaceAll('\n', ' ');
const { data } = await openai.embeddings.create({
model: 'text-embedding-ada-002',
input,
});
return data[0].embedding;
};
```
To search for similar guides by embedding, you can use `gt` and `sql` operators with `cosineDistance` function to calculate the similarity between the `embedding` column and the generated embedding:
```ts copy {10,15,16}
import { cosineDistance, desc, gt, sql } from 'drizzle-orm';
import { generateEmbedding } from './embedding';
import { guides } from './schema';
const db = drizzle(...);
const findSimilarGuides = async (description: string) => {
const embedding = await generateEmbedding(description);
const similarity = sql`1 - (${cosineDistance(guides.embedding, embedding)})`;
const similarGuides = await db
.select({ name: guides.title, url: guides.url, similarity })
.from(guides)
.where(gt(similarity, 0.5))
.orderBy((t) => desc(t.similarity))
.limit(4);
return similarGuides;
};
```
```ts
const description = 'Guides on using Drizzle ORM with different platforms';
const similarGuides = await findSimilarGuides(description);
```
```json
[
{
name: 'Drizzle with Turso',
url: '/docs/tutorials/drizzle-with-turso',
similarity: 0.8642314333984994
},
{
name: 'Drizzle with Supabase Database',
url: '/docs/tutorials/drizzle-with-supabase',
similarity: 0.8593631126014918
},
{
name: 'Drizzle with Neon Postgres',
url: '/docs/tutorials/drizzle-with-neon',
similarity: 0.8541051184461372
},
{
name: 'Drizzle with Vercel Edge Functions',
url: '/docs/tutorials/drizzle-with-vercel-edge-functions',
similarity: 0.8481551084241092
}
]
```
Source: https://orm.drizzle.team/docs/jit-mappers
# JIT Mappers
JIT mappers are **disabled by default**. Enable them with the `jit` option:
```ts
import { drizzle } from 'drizzle-orm/...';
const db = drizzle({ client, jit: true });
```
On initialization, Drizzle runs a compatibility check that tests whether the `Function` constructor works in the current runtime. If it doesn't (e.g. some edge runtimes or CSP-restricted environments), it falls back to regular mappers with a console warning.
The `jit` option is available in every driver's config
## What are JIT mappers?
JIT (Just-In-Time) mappers are dynamically generated JavaScript functions that transform database rows into JS objects. Instead of interpreting column metadata at runtime for every row, JIT mappers **compile a specialized function once** and reuse it for all rows in the result set.
## Why are they needed?
The regular Drizzle ORM mapper loops through column metadata for every single row:
```
For each row:
For each column:
â look up decoder
â check if codec exists
â check if value is null
â apply codec if needed
â apply decoder if needed
â navigate nested path
â assign to result object
```
With thousands of rows, this interpretive overhead adds up. JIT mappers eliminate it by generating a function where all the metadata lookups, null checks, and nested object construction are hardcoded into the function body at generation time
Source: https://orm.drizzle.team/docs/kit-migrations-for-teams
# Drizzle migrations for teams
This section will be updated right after our release of the next version of migrations folder structure.
You can read an extended [github discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2832) and subscribe to the updates!
Source: https://orm.drizzle.team/docs/kit-seed-data
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Drizzle Kit data seeding
This section will be updated right after our release of `drizzle-seed` package.
Source: https://orm.drizzle.team/docs/kit-web-mobile
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Drizzle migrations in web and mobile environments
This section will be updated in the next release.
For **Expo SQLite**, **OP SQLite** and **React Native** migrations - please refer to our [Get Started](/docs/get-started/expo-new) guide.
Source: https://orm.drizzle.team/docs/mssql/aliases
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, mssqlTable, int, text } from "drizzle-orm/mssql-core";
const employees = mssqlTable("employees", {
id: int().identity().primaryKey(),
name: text(),
managerId: int("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] [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);
```
```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));
```
```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);
```
```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);
```
```sql
with [sq] as (select upper([name]) as [name] from [users])
select [name] from [sq];
```
Source: https://orm.drizzle.team/docs/mssql/arktype
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# arktype
### Install the dependencies
drizzle-orm@rc arktype
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import type { ArkErrors } from 'arktype';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).top(1).from(users);
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().top(1).from(users);
const parsed: ArkErrors | { id: number; name: string; age: number } = userSelectSchema(rows[0]); // Will parse successfully
```
Views and enums are also supported.
```ts copy
import { pgEnum, pgView } from 'drizzle-orm/mssql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from "arktype";
const usersView = mssqlView('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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createInsertSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from 'arktype';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { parse } from 'arktype';
import { eq } from 'drizzle-orm';
import { ArkErrors } from 'arktype';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors, type } from 'arktype';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
bio: text(),
preferences: text()
});
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
MSSQL data type mappings follow the MSSQL column builders. See the [MSSQL data types](/docs/mssql/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/mssql/cache
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/node-mssql";
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/node-mssql";
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/node-mssql";
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: "cacheman@upstash.com" });
```
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/node-mssql";
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() });
```
```ts
import Keyv from "keyv";
export class TestGlobalCache extends Cache {
private globalTtl: number = 1000;
// This object will be used to store which query keys were used
// for a specific table, so we can later use it for invalidation.
private usedTablesPerKey: Record = {};
constructor(private kv: Keyv = new Keyv()) {
super();
}
// For the strategy, we have two options:
// - 'explicit': The cache is used only when .$withCache() is added to a query.
// - 'all': All queries are cached globally.
// The default behavior is 'explicit'.
override strategy(): "explicit" | "all" {
return "all";
}
// This function accepts query and parameters that cached into key param,
// allowing you to retrieve response values for this query from the cache.
override async get(key: string): Promise {
const res = (await this.kv.get(key)) ?? undefined;
return res;
}
// This function accepts several options to define how cached data will be stored:
// - 'key': A hashed query and parameters.
// - 'response': An array of values returned by Drizzle from the database.
// - 'tables': An array of tables involved in the select queries. This information is needed for cache invalidation.
// - 'isTag': A boolean flag indicating whether this cache entry represents a tag-based query.
// When true, the entry is associated with a specific tag rather than table-level invalidation,
// allowing you to invalidate cached queries by tag name. See the UpstashCache:
// https://github.com/drizzle-team/drizzle-orm/blob/beta/drizzle-orm/src/cache/upstash/cache.ts for an example
//
// For example, if a query uses the "users" and "posts" tables, you can store this information. Later, when the app executes
// any mutation statements on these tables, you can remove the corresponding key from the cache.
// If you're okay with eventual consistency for your queries, you can skip this option.
override async put(
key: string,
response: any,
tables: string[],
isTag: boolean = false,
config?: CacheConfig,
): Promise {
const ttl = config?.px ?? (config?.ex ? config.ex * 1000 : this.globalTtl);
await this.kv.set(key, response, ttl);
for (const table of tables) {
const keys = this.usedTablesPerKey[table];
if (keys === undefined) {
this.usedTablesPerKey[table] = [key];
} else {
keys.push(key);
}
}
}
// This function is called when insert, update, or delete statements are executed.
// You can either skip this step or invalidate queries that used the affected tables.
//
// The function receives an object with two keys:
// - 'tags': Used for queries labeled with a specific tag, allowing you to invalidate by that tag.
// - 'tables': The actual tables affected by the insert, update, or delete statements,
// helping you track which tables have changed since the last cache update.
override async onMutate(params: {
tags: string | string[];
tables: string | string[] | Table | Table[];
}): Promise {
const tagsArray = params.tags
? Array.isArray(params.tags)
? params.tags
: [params.tags]
: [];
const tablesArray = params.tables
? Array.isArray(params.tables)
? params.tables
: [params.tables]
: [];
const keysToDelete = new Set();
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
const keys = this.usedTablesPerKey[tableName] ?? [];
for (const key of keys) keysToDelete.add(key);
}
if (keysToDelete.size > 0 || tagsArray.length > 0) {
for (const tag of tagsArray) {
await this.kv.delete(tag);
}
for (const key of keysToDelete) {
await this.kv.delete(key);
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
this.usedTablesPerKey[tableName] = [];
}
}
}
}
}
```
## Limitations
#### Queries that won't be handled by the `cache` extension:
- Using cache with raw queries, such as:
```ts
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
```ts
await db.query.users.findMany();
```
- Using cache with views
Source: https://orm.drizzle.team/docs/mssql/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](/docs/mssql/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 API` to define a mapping strategy for Drizzle.
You can read more about it [here](/docs/mssql/sql-schema-declaration#shape-your-data-schema)
### int
Signed 4-byte int
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int: int()
});
```
```sql
CREATE TABLE [table] (
[int] int
);
```
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int1: int().default(10),
});
```
```sql
CREATE TABLE [table] (
[int1] int CONSTRAINT [table_int1_default] DEFAULT ((10))
);
```
### smallint
`smallint`
Small-range signed 2-byte int
```typescript
import { smallint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
smallint: smallint()
});
```
```sql
CREATE TABLE [table] (
[smallint] smallint
);
```
```typescript
import { smallint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
smallint1: smallint().default(10),
});
```
```sql
CREATE TABLE [table] (
[smallint1] smallint CONSTRAINT [table_smallint1_default] DEFAULT ((10))
);
```
### tinyint
`tinyint`
Small-range signed 1-byte int
```typescript
import { tinyint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
tinyint: tinyint()
});
```
```sql
CREATE TABLE [table] (
[tinyint] tinyint
);
```
```typescript
import { tinyint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
tinyint1: tinyint().default(10),
});
```
```sql
CREATE TABLE [table] (
[tinyint1] tinyint CONSTRAINT [table_tinyint1_default] DEFAULT ((10))
);
```
### bigint
`bigint`
Signed 8-byte int
If you're expecting values above 2^31 but below 2^53, you can utilise `mode: 'number'` and deal with javascript number as opposed to bigint.
```typescript
import { bigint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bigint: bigint({ mode: 'number' })
});
// will be inferred as `number`
bigint: bigint({ mode: 'number' })
// will be inferred as `bigint`
bigint: bigint({ mode: 'bigint' })
// will be inferred as `string`
bigint: bigint({ mode: 'string' })
```
```sql
CREATE TABLE [table] (
[bigint] bigint
);
```
```typescript
import { bigint, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bigint1: bigint({ mode: 'number' }).default(10)
});
```
```sql
CREATE TABLE [table] (
[bigint1] bigint CONSTRAINT [table_bigint1_default] DEFAULT ((10))
);
```
## ---
### bit
An int data type that can take a value of `1`, `0`, or `NULL`
Drizzle will accept `true` or `false` as values instead of `1` and `0`
```typescript
import { bit, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
bit: bit()
});
```
```sql
CREATE TABLE [table] (
[bit] bit
);
```
## ---
### text
`text`
Variable-length non-Unicode data in the code page of the server and with a maximum string length
of 2^31 - 1 (2,147,483,647)
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/ntext-text-and-image-transact-sql?view=sql-server-ver17#text)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { text, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
text: text()
});
// will be inferred as text: "value1" | "value2" | null
text: text({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE [table] (
[text] text
);
```
### ntext
`ntext`
Variable-length Unicode data with a maximum string length of 2^30 - 1 (1,073,741,823).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/ntext-text-and-image-transact-sql?view=sql-server-ver17#ntext)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { ntext, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
ntext: ntext()
});
// will be inferred as text: "value1" | "value2" | null
ntext: ntext({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE [table] (
[ntext] ntext
);
```
### varchar
`varchar(n|max)`
Variable-size string data. Use n to define the string size in bytes and can be a value from 1 through 8,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver17#varchar---n--max--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { varchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
varchar1: varchar(),
varchar2: varchar({ length: 256 }),
varchar3: varchar({ length: 'max' })
});
// will be inferred as text: "value1" | "value2" | null
varchar: varchar({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE [table] (
[varchar1] varchar,
[varchar2] varchar(256),
[varchar3] varchar(max)
);
```
### nvarchar
`nvarchar(n|max)`
Variable-size string data. The value of n defines the string size in byte-pairs
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver17#nvarchar---n--max--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { nvarchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
nvarchar1: nvarchar(),
nvarchar2: nvarchar({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
nvarchar: nvarchar({ enum: ["value1", "value2"] });
// will be inferred as `json`
nvarchar: nvarchar({ mode: 'json' })
```
```sql
CREATE TABLE [table] (
[nvarchar1] nvarchar,
[nvarchar2] nvarchar(256)
);
```
### char
`char(n)`
Fixed-size string data. n defines the string size in bytes and must be a value from 1 through 8,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver17#char---n--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { char, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('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(1),
[char2] char(256)
);
```
### nchar
`nchar(n)`
Fixed-size string data. n defines the string size in byte-pairs, and must be a value from 1 through 4,000
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver17#nchar---n--)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to MSSQL docs.
```typescript
import { nchar, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
nchar1: nchar(),
nchar2: nchar({ length: 256 }),
});
// will be inferred as text: "value1" | "value2" | null
nchar: nchar({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE [table] (
[nchar1] nchar(1),
[nchar2] nchar(256)
);
```
## ---
### binary
Fixed-length binary data with a length of n bytes, where n is a value from 1 through 8,000. The storage size is n bytes.
```typescript
import { binary, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
binary: binary(),
binary1: binary({ length: 256 })
});
```
```sql
CREATE TABLE [table] (
[binary] binary,
[binary1] binary(256)
);
```
### varbinary
Variable-length binary data. n can be a value from 1 through 8,000. max indicates that the maximum storage size is 2^31-1 bytes
```typescript
import { varbinary, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
varbinary: varbinary(),
varbinary1: varbinary({ length: 256 }),
varbinary2: varbinary({ length: 'max' })
});
```
```sql
CREATE TABLE [table] (
[varbinary] varbinary,
[varbinary1] varbinary(256),
[varbinary2] varbinary(max)
);
```
## ---
### numeric
`numeric`
Fixed precision and scale numbers. When maximum precision is used, valid values are from -10^38 + 1 through 10^38 - 1
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver17#decimal---p---s----and-numeric---p---s---)**
```typescript
import { numeric, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
numeric1: numeric(),
numeric2: numeric({ precision: 100 }),
numeric3: numeric({ precision: 100, scale: 20 })
// numericNum: numeric({ mode: 'number' }),
// numericBig: numeric({ mode: 'bigint' }),
});
```
```sql
CREATE TABLE [table] (
[numeric1] numeric,
[numeric2] numeric(100),
[numeric3] numeric(100, 20)
);
```
### decimal
An alias of **[numeric.](#numeric)**
### real
The ISO synonym for real is float(24).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-ver17)**
```typescript
import { real, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
real1: real(),
real2: real().default(10.10)
});
```
```sql
CREATE TABLE [table] (
[real1] real,
[real2] real CONSTRAINT [table_real2_default] DEFAULT ((10.1))
);
```
### float
float [ (n) ] Where n is the number of bits that are used to store the mantissa of the float number in scientific notation and, therefore, dictates the precision and storage size. If n is specified, it must be a value between 1 and 53. The default value of n is 53.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql?view=sql-server-ver17#syntax)**
```typescript
import { float, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
float1: float(),
float2: float({ precision: 16 })
});
```
```sql
CREATE TABLE [table] (
[float1] float,
[float2] float(16)
);
```
## ---
### time
`time`
Defines a time of a day. The time is without time zone awareness and is based on a 24-hour clock.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql?view=sql-server-ver17#time-description)**
```typescript
import { time, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
time1: time(),
time2: time({ mode: 'string' }),
time3: time({ precision: 6 }),
time4: time({ precision: 6, mode: 'date' })
});
```
```sql
CREATE TABLE [table] (
[time1] time,
[time2] time,
[time3] time(6),
[time4] time(6)
);
```
### date
`date`
Calendar date (year, month, day)
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/date-transact-sql?view=sql-server-ver17#date-description)**
```typescript
import { date, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('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" }),
```
### datetime
`datetime`
Defines a date that is combined with a time of day with fractional seconds that is based on a 24-hour clock.
Avoid using datetime for new work. Instead, use the time, date, datetime2, and datetimeoffset data types. These types align with the SQL Standard, and are more portable. time, datetime2 and datetimeoffset provide more seconds precision. datetimeoffset provides time zone support for globally deployed applications.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetime-transact-sql?view=sql-server-ver17#description)**
```typescript
import { datetime, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
datetime: datetime(),
});
```
```sql
CREATE TABLE [table] (
[datetime] datetime
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetime: datetime({ mode: "date" }),
// will infer as string
datetime: datetime({ mode: "string" }),
```
### datetime2
`datetime2`
Defines a date that is combined with a time of day that is based on 24-hour clock. datetime2 can be considered as an extension of the existing datetime type that has a larger date range, a larger default fractional precision, and optional user-specified precision.
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql?view=sql-server-ver17#datetime2-description)**
```typescript
import { datetime2, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
datetime2: datetime2(),
});
```
```sql
CREATE TABLE [table] (
[datetime2] datetime2
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetime2: datetime2({ mode: "date" }),
// will infer as string
datetime2: datetime2({ mode: "string" }),
```
### datetimeoffset
`datetimeoffset`
Defines a date that is combined with a time of a day based on a 24-hour clock like datetime2, and adds time zone awareness based on Coordinated Universal Time (UTC).
For more info please refer to the official MSSQL **[docs.](https://learn.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql?view=sql-server-ver17#datetimeoffset-description)**
```typescript
import { datetimeoffset, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
datetimeoffset: datetimeoffset(),
});
```
```sql
CREATE TABLE [table] (
[datetimeoffset] datetimeoffset
);
```
You can specify either `date` or `string` infer modes:
```typescript
// will infer as date
datetimeoffset: datetimeoffset({ mode: "date" }),
// will infer as string
datetimeoffset: datetimeoffset({ mode: "string" }),
```
## ---
### 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' };
export const users = mssqlTable('users', {
id: int().$type().primaryKey(),
});
```
### 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`
```typescript
import { int, mssqlTable, text } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int: int().default(42),
text: text().default('text'),
});
```
```sql
CREATE TABLE [table] (
[int] int CONSTRAINT [table_int_default] DEFAULT ((42)),
[text] text CONSTRAINT [table_text_default] DEFAULT ('text')
);
```
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.
Note: This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`
```ts
import { text, mssqlTable } from "drizzle-orm/mssql-core";
import { createId } from '@paralleldrive/cuid2';
export const table = mssqlTable('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 { int, datetime2, text, mssqlTable } from "drizzle-orm/mssql-core";
import { SQL, sql } from 'drizzle-orm';
export const table = mssqlTable('table', {
updateCounter: int().default(sql`1`).$onUpdateFn((): SQL => sql`${table.updateCounter} + 1`),
updatedAt: datetime2({ mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text().$type().$onUpdate(() => null),
});
```
### Not null
`NOT NULL` constraint dictates that the associated column may not contain a `NULL` value.
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int: int().notNull(),
});
```
```sql
CREATE TABLE [table] (
[int] int 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 { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
id: int().primaryKey(),
});
```
```sql
CREATE TABLE [table] (
[id] int,
CONSTRAINT [table_pkey] PRIMARY KEY([id])
);
```
Source: https://orm.drizzle.team/docs/mssql/connect-overview
# Database connection with Drizzle
Use these MSSQL drivers and providers with Drizzle ORM.
## MSSQL connection docs
- [MSSQL](/docs/mssql/get-started-mssql)
Source: https://orm.drizzle.team/docs/mssql/custom-types
# Custom types
Custom types let you define your own column types with custom SQL data types and JS/DB value transforms
MSSQL exposes `customType` from `drizzle-orm/mysql-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.
#### Int
```ts
import { customType, mssqlTable } from "drizzle-orm/mssql-core";
const customInt = customType<{ data: number }>({
dataType() {
return "int";
},
});
export const users = mssqlTable("users", {
id: customInt(),
});
```
#### Text
```ts
import { customType, mssqlTable } from 'drizzle-orm/mssql-core';
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});
export const users = mssqlTable("users", {
name: customText(),
});
```
#### Datetime2
```ts
import { customType, mssqlTable } from "drizzle-orm/mssql-core";
const customTimestamp = customType<{
data: Date;
driverData: string;
config: { precision?: number };
configRequired: true;
}>({
dataType(config) {
const precision = typeof config.precision !== "undefined" ? ` (${config.precision})` : "";
return `datetime2${precision}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
});
export const users = mssqlTable("users", {
createdAt: customTimestamp("created_at", { precision: 4 }),
});
```
Usage for all types will be same as defined functions in Drizzle ORM. For example:
```ts
const usersTable = mssqlTable("users", {
id: customInt().primaryKey(),
name: customText().notNull(),
createdAt: customTimestamp("created_at", { precision: 4 })
.notNull()
});
```
### TS-doc for type definitions
You can check `ts-doc` for types, param definition and examples
```ts
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 returning for specific database data type
*
* Needed only in case driver's output and input for type differ
*
* Defaults to {@link driverData}
*/
driverOutput?: unknown;
/**
* Type helper, that represents what type database driver is accepting for specific database data type
*/
driverData?: unknown;
/**
* Type helper, that represents what type field returns after being aggregated to JSON
*/
jsonData?: unknown;
/**
* What config type should be used for {@link CustomTypeParams} `dataType` generation
*/
config?: Record;
/**
* 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 {
/**
* 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 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-07 03:25:16.635";
* }
* ```
* to:
* ```
* {
* customField: new Date("2025-04-07 03:25:16.635");
* }
* ```
*/
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: `binary`, `varbinary`, `time`, `datetime`, `decimal`, `float`, `bigint`
* @example
* For example, when using bigint we need to cast field to text to preserve data integrity
* ```
* forJsonSelect(identifier: SQL, sql: SQLGenerator): SQL {
* return sql`cast(${identifier} as char)`
* },
* ```
*
* This will change query from:
* ```
* SELECT
* json_build_object('bigint', `t`.`bigint`)
* FROM
* (
* SELECT
* `table`.`custom_bigint` AS `bigint`
* FROM
* `table`
* ) AS `t`
* ```
* to:
* ```
* SELECT
* json_build_object('bigint', `t`.`bigint`)
* FROM
* (
* SELECT
* cast(`table`.`custom_bigint` as char) 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) => SQL;
}
```
Source: https://orm.drizzle.team/docs/mssql/data-querying
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/mssql/sql-schema-declaration)
- How to connect to the database - [Connection Fundamentals](/docs/mssql/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.
```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
```ts
await db.insert(users).values({ email: 'user@gmail.com' })
```
```sql
INSERT INTO [users] ([email]) VALUES ('user@gmail.com')
```
```ts
await db.update(users)
.set({ email: 'user@gmail.com' })
.where(eq(users.id, 1))
```
```sql
UPDATE [users]
SET [email] = 'user@gmail.com'
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(like(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?
Source: https://orm.drizzle.team/docs/mssql/delete
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'));
```
### Output
You can delete a row and get it back in PostgreSQL:
```typescript copy
const deletedUser = await db.delete(users)
.where(eq(users.name, 'Dan'))
.output();
// partial return
const deletedUserId = await db.delete(users)
.where(eq(users.name, "Dan"))
.output({ deletedId: users.id });
// deletedUserId: { deletedId: number }[]
```
```sql
delete from [users] output DELETED.[id], DELETED.[name], DELETED.[age] where [users].[name] = 'Dan'
delete from [users] output DELETED.[id] where [users].[name] = 'Dan'
```
Source: https://orm.drizzle.team/docs/mssql/drizzle-config-file
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](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-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: "mssql",
schema: "./src/schema.ts",
out: "./drizzle",
});
```
```js
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
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: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:3306/dbname",
},
schemaFilter: "dbo",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "drizzle",
},
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: "mssql",
schema: "./src/schema/*",
out: "./drizzle",
});
```
## ---
### `dialect`
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export` |
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
});
```
### `schema`
[`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 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",
});
```
## ---
### `dbCredentials`
Database connection credentials in a form of `url` or
`user:password@host:port/db` params.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "mssql",
dbCredentials: {
url: "mssql://user:password@host:3306/db",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'mssql',
schema: './schema.ts',
breakpoints: false,
dbCredentials: {
database: '',
password: '',
port: 3306,
server: '',
user: '',
options: {
encrypt: true,
trustServerCertificate: true,
},
},
});
```
### `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: "mssql",
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` |
```ts
import * as p from "drizzle-orm/mssql-core"
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
firstName: p.varchar("first-name", { length: 255 }),
lastName: p.varchar("LastName", { length: 255 }),
email: p.varchar("email", { length: 255 }),
phoneNumber: p.varchar("phone_number", { length: 255 }),
});
```
```sql
SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users';
```
```
COLUMN_NAME | DATA_TYPE
---------------+--------------
id | int
first-name | varchar
LastName | varchar
email | varchar
phone_number | varchar
```
```ts
import * as p from "drizzle-orm/mssql-core"
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
"first-name": p.varchar("first-name", { length: 255 }),
LastName: p.varchar("LastName", { length: 255 }),
email: p.varchar("email", { length: 255 }),
phone_number: p.varchar("phone_number", { length: 255 }),
});
```
```sql
SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users';
```
```
COLUMN_NAME | DATA_TYPE
---------------+--------------
id | int
first-name | varchar
LastName | varchar
email | varchar
phone_number | varchar
```
## ---
### `tablesFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/mssql/goodies#multi-project-schema).
`drizzle-kit push` and `drizzle-kit pull` will manage all tables by default.
You can configure list of tables and schemas via `tablesFilters` and `schemaFilter` options.
`tablesFilter` 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 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: "mssql",
tablesFilter: ["users", "posts", "project1_*"],
});
```
### `schemaFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/mssql/goodies#multi-project-schema).
`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. `["dbo", "auth"]` or `"tenant_*"`
| | |
| :------------ | :----------------- |
| type | `string[]` |
| commands | `push` `pull` |
```ts
export default defineConfig({
dialect: "mssql",
schemaFilter: ["dbo", "schema1", "schema2"],
});
```
## ---
### `verbose`
Print all SQL statements during `drizzle-kit push` command.
| | |
| :------------ | :----------------- |
| type | `boolean` |
| default | `false` |
| commands | `pull` |
```ts
export default defineConfig({
dialect: "mssql",
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.
`breakpoints` option flag lets you switch it on and off
| | |
| :------------ | :----------------- |
| type | `boolean` |
| default | `true` |
| commands | `generate` `pull` |
```ts
export default defineConfig({
dialect: "mssql",
breakpoints: false,
});
```
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-check
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit check`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mssql/drizzle-kit-generate)
`drizzle-kit check` command lets you check consistency of your generated SQL migrations history.
That's extremely useful when you have multiple developers working on the project and
altering database schema on different branches - read more about [migrations for teams](/docs/mssql/kit-migrations-for-teams).
`drizzle-kit check` command requires you to specify `dialect`,
you can provide it either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
});
```
```shell
npx drizzle-kit check
```
```shell
npx drizzle-kit check --dialect=mssql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit check --config=drizzle-dev.config.ts
drizzle-kit check --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Ignore conflicts
In case you need `check` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit check --ignore-conflicts
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit check --dialect=mssql
drizzle-kit check --dialect=mssql --out=./migrations-folder
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-export
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit export`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
`drizzle-kit export` lets you export SQL representation of Drizzle schema and print in console SQL DDL representation on it.
Drizzle Kit `export` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Based on the current json snapshot it will generate SQL DDL statements
3. Output SQL DDL statements to console
It's designed to cover [codebase first](/docs/mssql/migrations) approach of managing Drizzle migrations.
You can export the SQL representation of the Drizzle schema, allowing external tools like Atlas to handle all the migrations for you
`drizzle-kit export` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit export
```
```shell
npx drizzle-kit export --dialect=mssql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 export --config=drizzle-dev.config.ts
drizzle-kit export --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of available configurations
`drizzle-kit export` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `--sql` | generating SQL representation of Drizzle Schema |
By default, Drizzle Kit outputs SQL files, but in the future, we want to support different formats
drizzle-kit export --sql=true
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
### Example
Example of how to export drizzle schema to console with Drizzle schema located in `./src/schema.ts`
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ configs
â â đ drizzle.config.ts
â đ src
â â đ schema.ts
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
});
```
```ts filename='schema.ts'
import { mssqlTable, int, text } from 'drizzle-orm/mssql-core'
export const users = mssqlTable('users', {
id: int().primaryKey().identity(),
email: text().notNull(),
name: text()
});
```
Now let's run
```shell
npx drizzle-kit export --config=./configs/drizzle.config.ts
```
And it will successfully output SQL representation of drizzle schema
```bash
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[email] text NOT NULL,
[name] text,
CONSTRAINT [users_pkey] PRIMARY KEY([id])
);
```
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-generate
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit generate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
`drizzle-kit generate` lets you generate SQL migrations based on your Drizzle schema upon declaration or on subsequent schema changes.
Drizzle Kit `generate` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. It will read through your previous migrations folders and compare current json snapshot to the most recent one
3. Based on json differences it will generate SQL migrations
4. Save `migration.sql` and `snapshot.json` in migration folder under current timestamp
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.nvarchar({ length: 255 }),
email: p.nvarchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ migration.sql
â â đ snapshot.json
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] nvarchar(255),
[email] nvarchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [users_email_key] UNIQUE([email])
);
```
It's designed to cover [code first](/docs/mssql/migrations) approach of managing Drizzle migrations.
You can apply generated migrations using [`drizzle-kit migrate`](/docs/mssql/drizzle-kit-migrate), using drizzle-orm's `migrate()`,
using external migration tools like [bytebase](https://www.bytebase.com/) or running migrations yourself directly on the database.
`drizzle-kit generate` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit generate
```
```shell
npx drizzle-kit generate --dialect=mssql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Custom migration file name
You can set custom migration file names by providing `--name` CLI option
```shell
npx drizzle-kit generate --name=init
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ snapshot.json
â â đ migration.sql
â đ src
â âĻ
```
### Multiple configuration files in one project
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
```
### Custom migrations
You can generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding. Extended docs on custom migrations - [see here](/docs/mssql/kit-custom-migrations)
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {5}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed/migration.sql
INSERT INTO [users] ([name]) VALUES('Dan');
INSERT INTO [users] ([name]) VALUES('Andrew');
INSERT INTO [users] ([name]) VALUES('Dandrew');
```
### Ignore conflicts
In case you need `generate` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit generate --ignore-conflicts
```
### Extended list of available configurations
`drizzle-kit generate` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `custom` | generate empty SQL for custom migration |
| `name` | generate migration with custom name |
| `ignore-conflicts` | skip commutativity conflict checks, default is `false` |
drizzle-kit generate --name=init
drizzle-kit generate --name=seed_users --custom
drizzle-kit generate --ignore-conflicts
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `out` | | Migrations output folder, default is `./drizzle` |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `breakpoints` | | SQL statements breakpoints, default is `true` |
### Extended example
Example of how to create a custom MSSQL migration file named `0001_seed-users.sql`
with Drizzle schema located in `./src/schema.ts` and migrations folder named `./migrations` instead of default `./drizzle`.
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ migrations
â đ configs
â â đ drizzle.config.ts
â đ src
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
out: "./migrations",
});
```
Now let's run
```shell
npx drizzle-kit generate --config=./configs/drizzle.config.ts --name=seed-users --custom
```
And it will successfully generate
```plaintext {6}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed-users/migration.sql
INSERT INTO [users] ([name]) VALUES('Dan');
INSERT INTO [users] ([name]) VALUES('Andrew');
INSERT INTO [users] ([name]) VALUES('Dandrew');
```
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-migrate
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit migrate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mssql/drizzle-kit-generate)
`drizzle-kit migrate` lets you apply SQL migrations generated by [`drizzle-kit generate`](/docs/mssql/drizzle-kit-generate).
It's designed to cover [code first(option 3)](/docs/mssql/migrations) approach of managing Drizzle migrations.
Drizzle Kit `migrate` command triggers a sequence of events:
1. Reads through migration folder and read all `.sql` migration files
2. Connects to the database and fetches entries from drizzle migrations log table
3. Based on previously applied migrations it will decide which new migrations to run
4. Runs SQL migrations and logs applied migrations to drizzle migrations table
```plaintext
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ 20242409135510_delicate_professor_xavie
â âĻ
```
```plaintext
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. reads migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
`drizzle-kit migrate` command requires you to specify both `dialect` and database connection credentials,
you can provide them either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit migrate
```
```shell
npx drizzle-kit migrate --dialect=mssql --url=mssql://user:password@host:port/dbname
```
### Applied migrations log in the database
Upon running migrations Drizzle Kit will persist records about successfully applied migrations in your database.
It will store them in migrations log table named `__drizzle_migrations` in `drizzle` schema.
You can customise both **table** and **schema** of that table via drizzle config file:
```ts filename="drizzle.config.ts" {8-9}
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname"
},
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
schema: 'drizzle', // `drizzle` by default
},
});
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended example
Let's generate SQL migration and apply it to our database using `drizzle-kit generate` and `drizzle-kit migrate` commands
```plaintext
đĻ
â đ drizzle
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname"
},
migrations: {
table: 'journal',
schema: 'drizzle',
},
});
```
```ts
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.nvarchar({ length: 255 }),
})
```
Now let's run
```shell
npx drizzle-kit generate --name=init
```
it will generate
```plaintext {5}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â âĻ
```
```sql
-- ./drizzle/0000_init.sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] nvarchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id])
);
```
Now let's run
```shell
npx drizzle-kit migrate
```
and our SQL migration is now successfully applied to the database â
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-pull
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 Prerequisites from "@mdx/Prerequisites.astro";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import Npm from "@mdx/Npm.astro"
import Npx from "@mdx/Npx.astro"
# `drizzle-kit pull`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file) docs
`drizzle-kit pull` lets you literally pull(introspect) your existing database schema and generate `schema.ts` drizzle schema file,
it is designed to cover [database first](/docs/mssql/migrations) approach of Drizzle migrations.
When you run Drizzle Kit `pull` command it will:
1. Pull database schema(DDL) from your existing database
2. Generate `schema.ts` drizzle schema file and save it to `out` folder
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââââââââ
â â <--- CREATE TABLE [users] (
ââââââââââââââââââââââââââââ â â [id] int identity primary key,
â ~ drizzle-kit pull â â â [name] varchar(255),
âââŦâââââââââââââââââââââââââ â DATABASE â [email] varchar(255) unique
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().identity({ seed: 1 ,increment: 1 }),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }),
}, (table) => [
p.primaryKey({ columns: [table.id], name: "PK__users__3213E83F9C94E8E5"}),
p.unique("UQ__users__AB6E6164167EE806").on(table.email)
]);
```
It is a great approach if you need to manage database schema outside of your TypeScript project or
you're using database, which is managed by somebody else.
`drizzle-kit pull` requires you to specify `dialect` and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
dbCredentials: {
url: "mssql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit pull
```
```shell
npx drizzle-kit pull --dialect=mssql --url=mssql://user:password@host:port/dbname
```
### Multiple configuration files in one project
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 pull --config=drizzle-dev.config.ts
drizzle-kit pull --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Initial pull
You can use the `--init` flag to mark the pulled schema as an applied migration in your database,
so that all subsequent migrations are diffed against the initial one
```shell
npx drizzle-kit pull --init
```
### Including tables and schemas
`drizzle-kit pull` will by default manage all tables in all schemas.
You can configure list of tables and schemas via `tablesFilter` and `schemaFilter` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | `glob` based schema names filter, e.g. `["dbo", "drizzle"]` or `"drizzle*"`. Default is `"*"` |
Let's configure drizzle-kit to only operate with **all tables** in **dbo** schema.
```ts filename="drizzle.config.ts" {9-10}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname",
},
schemaFilter: ["dbo"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit pull
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `out` | | Migrations output folder path, default is `./drizzle` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `introspect-casing` | | Strategy for JS keys creation in columns, tables, etc. `preserve` `camel` |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
drizzle-kit pull --dialect=mssql --url=mssql://user:password@host:port/dbname
drizzle-kit pull --dialect=mssql --tablesFilter='user*' url=mssql://user:password@host:port/dbname
{/* TODO update gif */}

Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-push
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import DriversExamples from "@mdx/DriversExamples.mdx"
# `drizzle-kit push`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file) docs
`drizzle-kit push` lets you literally push your schema and subsequent schema changes directly to the
database while omitting SQL files generation, it's designed to cover [code first](/docs/mssql/migrations)
approach of Drizzle migrations.
When you run Drizzle Kit `push` command it will:
1. Read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Pull(introspect) database schema
3. Based on differences between those two it will generate SQL migrations
4. Apply SQL migrations to the database
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.nvarchar({ length: 255 }),
});
```
```
âââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´ââââââ
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] nvarchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id])
);
```
It's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
It pairs exceptionally well with blue/green deployment strategy, managed database deployments, and CI/CD workflows.
`drizzle-kit push` requires you to specify `dialect`, path to the `schema` file(s) and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit push
```
```shell
npx drizzle-kit push --dialect=mssql --schema=./src/schema.ts --url=mssql://user:password@host:port/dbname
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 push --config=drizzle-dev.config.ts
drizzle-kit push --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Including tables and schemas
`drizzle-kit push` will manage all tables and schemas by default.
You can configure list of tables, schemas via `tablesFilter`, `schemaFilter` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | Schema names filter, e.g. `["dbo", "drizzle"]` |
Let's configure drizzle-kit to only operate with **all tables** in **dbo** schema.
```ts filename="drizzle.config.ts" {9-10}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname",
},
schemaFilter: ["dbo"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit push
```
### Extended list of configurations
`drizzle-kit push` has a list of cli-only options
| | |
| :-------- | :---------------------------------------------------------------|
| `verbose` | print all SQL statements prior to execution |
| `explain` | print the planned SQL changes, without applying them (dry run) |
| `force` | auto-accept all data-loss statements |
drizzle-kit push --explain --verbose --force
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit push dialect=mssql schema=src/schema.ts url=mssql://user:password@host:port/dbname
drizzle-kit push dialect=mssql schema=src/schema.ts --tablesFilter='user*' url=mssql://user:password@host:port/dbname
### Extended example
Let's declare drizzle schema in the project and push it to the database via `drizzle-kit push` command
```plaintext
đĻ
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mssql://user:password@host:port/dbname"
},
});
```
```ts
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.nvarchar({ length: 255 }),
})
```
Now let's run
```shell
npx drizzle-kit push
```
it will pull existing(empty) schema from the database and generate SQL migration and apply it under the hood
```sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] nvarchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id])
);
```
DONE â
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-studio
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit studio`
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
- Drizzle Studio, our database browser - [read here](/drizzle-studio/overview)
`drizzle-kit studio` command spins up a server for [Drizzle Studio](/drizzle-studio/overview) hosted on [local.drizzle.studio](https://local.drizzle.studio).
It requires you to specify database connection credentials via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file.
By default it will start a Drizzle Studio server on `127.0.0.1:4983`
```ts {6}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
dbCredentials: {
url: "mssql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit studio
```
### Configuring `host` and `port`
By default Drizzle Studio server starts on `127.0.0.1:4983`,
you can config `host` and `port` via CLI options
drizzle-kit studio --port=3000
drizzle-kit studio --host=0.0.0.0
drizzle-kit studio --host=0.0.0.0 --port=3000
### Logging
You can enable logging of every SQL statement by providing `verbose` flag
drizzle-kit studio --verbose
### Safari and Brave support
Safari and Brave block access to localhost by default.
You need to install [mkcert](https://github.com/FiloSottile/mkcert) and generate self-signed certificate:
1. Follow the mkcert [installation steps](https://github.com/FiloSottile/mkcert#installation)
2. Run `mkcert -install`
3. Restart your `drizzle-kit studio`
### Embeddable version of Drizzle Studio
While hosted version of Drizzle Studio for local development is free forever and meant to just enrich Drizzle ecosystem,
we have a B2B offering of an embeddable version of Drizzle Studio for businesses.
**Drizzle Studio component** - is a pre-bundled framework agnostic web component of Drizzle Studio
which you can embed into your UI `React` `Vue` `Svelte` `VanillaJS` etc.
That is an extremely powerful UI element that can elevate your offering
if you provide Database as a SaaS or a data centric SaaS solutions based
on SQL or for private non-customer facing in-house usage.
Database platforms using Drizzle Studio:
- [Turso](https://turso.tech/), our first customers since Oct 2023!
- [Neon](https://neon.tech/), [launch post](https://neon.tech/docs/changelog/2024-05-24)
- [Hydra](https://www.hydra.so/)
Data centric platforms using Drizzle Studio:
- [Nuxt Hub](https://hub.nuxt.com/), SÊbastien Chopin's [launch post](https://x.com/Atinux/status/1768663789832929520)
- [Deco.cx](https://deco.cx/)
You can read a detailed overview [here](https://www.npmjs.com/package/@drizzle-team/studio) and
if you're interested - hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Drizzle Studio chrome extension
Drizzle Studio [chrome extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd)
lets you browse your [PlanetScale](https://planetscale.com),
[Cloudflare](https://developers.cloudflare.com/d1/) and [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres)
serverless databases directly in their vendor admin panels!
### Limitations
Our hosted version Drizzle Studio is meant to be used for local development and not meant to be used on remote (VPS, etc).
If you want to deploy Drizzle Studio to your VPS - we have an alpha version of Drizzle Studio Gateway,
hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Is it open source?
No. Drizzle ORM and Drizzle Kit are fully open sourced, while Studio is not.
Drizzle Studio for local development is free to use forever to enrich Drizzle ecosystem,
open sourcing one would've break our ability to provide B2B offerings and monetise it, unfortunately.
Source: https://orm.drizzle.team/docs/mssql/drizzle-kit-up
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit up`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mssql/drizzle-kit-generate)
`drizzle-kit up` command lets you upgrade drizzle schema snapshots to a newer version.
It's required whenever we introduce breaking changes to the json snapshots of the schema and upgrade the internal version.
`drizzle-kit up` command requires you to specify `dialect` param,
you can provide it either via [drizzle.config.ts](/docs/mssql/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
});
```
```shell
npx drizzle-kit up
```
```shell
npx drizzle-kit up --dialect=mssql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mssql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit up --dialect=mssql
drizzle-kit up --dialect=mssql --out=./migrations-folder
{/* TODO: update up gif */}

Source: https://orm.drizzle.team/docs/mssql/dynamic-query-building
import Callout from '@mdx/Callout.astro';
# Dynamic query building
By default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once.
For example, in a `SELECT` statement there might only be one `WHERE` clause, so you can only invoke `.where()` once:
```ts
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // â Type error - where() can only be invoked once
```
In the previous ORM versions, when such restrictions weren't implemented, this example in particular was a source of confusion for many users, as they expected the query builder to "merge" multiple `.where()` calls into a single condition.
This behavior is useful for conventional query building, i.e. when you create the whole query at once.
However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it.
To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once.
To enable it, you need to call `.$dynamic()` on a query builder.
Let's see how it works by implementing a simple `withPagination` function that adds `OFFSET` and `FETCH` clauses to a query based on the provided page number and an optional page size:
```ts
function withPagination(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.offset((page - 1) * pageSize).fetch(pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1)).orderBy(users.id);
withPagination(query, 1); // â Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // â
OK
```
Note that the `withPagination` function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:
```ts
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
This is possible, because `MsSqlSelect` and other similar types are specifically designed to be used in dynamic query building. They can only be used in dynamic mode.
Here is the list of all types that can be used as generic parameters in dynamic query building:
| Query | Type |
|:--------|:-----------------------------------|
| Select | `MsSqlSelect` / `MsSqlSelectQueryBuilder` |
| Insert | `MsSqlInsert` |
| Update | `MsSqlUpdate` |
| Delete | `MsSqlDelete` |
The `...QueryBuilder` types are for usage with [standalone query builder
instances](/docs/mssql/goodies#standalone-query-builder). DB query builders are
subclasses of them, so you can use them as well.
```ts
import { QueryBuilder } from 'drizzle-orm/mssql-core';
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
Source: https://orm.drizzle.team/docs/mssql/effect-schema
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# effect-schema
Allows you to generate [effect](https://effect.website/) schemas from Drizzle ORM schemas.
**Features**
- Create a select schema for tables and views.
- Create insert and update schemas for tables.
- Supported dialect: MSSQL.
#### Usage
```ts
import { int, mssqlTable, text, datetime2 } from 'drizzle-orm/mssql-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/effect-schema';
import { Effect, Schema } from "effect";
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
email: text().notNull(),
role: text({ enum: ['admin', 'user'] }).notNull(),
createdAt: datetime2('created_at').notNull(),
});
// Schema for inserting a user - can be used to validate API requests
const UserInsert = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const UserUpdate = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const UserSelect = createSelectSchema(users);
// Overriding the fields
const UserInsert = createInsertSchema(users, {
role: Schema.String,
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const UserInsert = createInsertSchema(users, {
id: (schema) => schema.check(Schema.isGreaterThanOrEqualTo(0)),
role: Schema.String,
});
// Usage
const program = Effect.gen(function*() {
const parsedUser = yield* Schema.decodeUnknownEffect(UserInsert)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
});
```
Source: https://orm.drizzle.team/docs/mssql/extensions
import Callout from '@mdx/Callout.astro';
Currently, there are no MSSQL extensions natively supported by Drizzle. Once those are added, we will have them here!
Source: https://orm.drizzle.team/docs/mssql/generated-columns
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Generated Columns
MSSQL supports computed columns. A computed column is calculated from an expression that can reference other columns in the same table.
1. Virtual computed columns are calculated when queried and do not occupy storage space.
2. Persisted computed columns store the computed value on disk and can be indexed when the expression is deterministic.
For more info, see the Microsoft docs for [computed columns](https://learn.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table).
#### Drizzle Side
In Drizzle you can specify `.generatedAlwaysAs()` on a column and pass a SQL expression.
#### Virtual Computed Column
```ts
import { SQL, sql } from "drizzle-orm";
import { mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable("users", {
firstName: varchar("first_name", { length: 256 }),
lastName: varchar("last_name", { length: 256 }),
fullName: varchar("full_name", { length: 512 }).generatedAlwaysAs(
(): SQL => sql`concat(${users.firstName}, ' ', ${users.lastName})`,
),
});
```
```sql
CREATE TABLE [users] (
[first_name] varchar(256),
[last_name] varchar(256),
[full_name] AS (concat([users].[first_name], ' ', [users].[last_name]))
);
```
#### Persisted Computed Column
Use `{ mode: "persisted" }` when you want SQL Server to persist the computed value.
```typescript copy
import { SQL, sql } from "drizzle-orm";
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const orders = mssqlTable("orders", {
quantity: int("quantity"),
unitPrice: int("unit_price"),
total: int("total").generatedAlwaysAs(
(): SQL => sql`${orders.quantity} * ${orders.unitPrice}`,
{ mode: "persisted" },
),
});
```
```sql
CREATE TABLE [orders] (
[quantity] int,
[unit_price] int,
[total] AS ([orders].[quantity] * [orders].[unit_price]) PERSISTED
);
```
#### Limitations
Drizzle Kit will also have limitations for `push` command:
1. You can't change the generated constraint expression using `push`. Drizzle-kit will ignore this change. To make it work, you would need to `drop the column`, `push`, and then `add a column with a new expression`. This was done due to the complex mapping from the database side, where the schema expression will be modified on the database side and, on introspection, we will get a different string. We can't be sure if you changed this expression or if it was changed and formatted by the database. As long as these are generated columns and `push` is mostly used for prototyping on a local database, it should be fast to `drop` and `create` generated columns. Since these columns are `generated`, all the data will be restored
2. `generate` should have no limitations
Source: https://orm.drizzle.team/docs/mssql/get-started-mssql
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextMSSQL from "@mdx/WhatsNextMSSQL.astro";
# Drizzle \<\> MSSQL
- Database [connection basics](/docs/mssql/connect-overview) with Drizzle
- node-mssql [basics](https://github.com/tediousjs/node-mssql)
Drizzle has native support for MSSQL connections with the `mssql` driver.
#### Step 1 - Install packages
drizzle-orm@rc mssql
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
// Make sure to install the 'mssql' package
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript copy
// Make sure to install the 'mssql' package
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({
connection: process.env.DATABASE_URL,
});
const result = await db.execute('select 1');
```
As long as the `node-mssql` driver requires `await` on `Pool` initialization, we need to `await` it before each request - unless you are providing your own Pool instance to Drizzle. In that case, when you want to access `db.$client`, you first need to `await` it, and then use it
```ts
const awaitedClient = await db.$client.$instance();
const response = awaitedClient.query...;
```
If you need to provide your existing driver:
```typescript copy
// Make sure to install the 'mssql' package
import { drizzle } from "drizzle-orm/node-mssql";
import { connect } from 'mssql';
const pool = await connect(process.env.DATABASE_URL!);
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/mssql/goodies
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
## Type API
Use Drizzle's type helpers to infer `select` and `insert` models from a MSSQL table schema.
```ts
import { int, text, mssqlTable } from 'drizzle-orm/mssql-core';
import { type InferSelectModel, type InferInsertModel } from 'drizzle-orm';
const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: text().notNull(),
});
type SelectUser = typeof users.$inferSelect;
type InsertUser = typeof users.$inferInsert;
// or
type SelectUser = InferSelectModel;
type InsertUser = InferInsertModel;
```
## Logging
Enable default query logging by passing `{ logger: true }` to `drizzle`.
```ts
import { drizzle } from "drizzle-orm/node-mssql";
const db = drizzle(process.env.DB_URL, { logger: true });
```
You can change the logs destination by creating a `DefaultLogger` instance and providing a custom `writer` to it:
```typescript copy
import { DefaultLogger, type LogWriter } from "drizzle-orm/logger";
import { drizzle } from "drizzle-orm/node-mssql";
class MyLogWriter implements LogWriter {
write(message: string) {
// Write to file, stdout, etc.
}
}
const logger = new DefaultLogger({ writer: new MyLogWriter() });
const db = drizzle(process.env.DB_URL, { logger });
```
You can also provide a custom logger.
```ts
import type { Logger } from 'drizzle-orm/logger';
import { drizzle } from "drizzle-orm/node-mssql";
class MyLogger implements Logger {
logQuery(query: string, params: unknown[]): void {
console.log({ query, params });
}
}
const db = drizzle(process.env.DB_URL, { logger: new MyLogger() });
```
## Multi-project schema
Use `mssqlTableCreator` to customize table names when several projects share one database.
```ts
import { int, text, mssqlTableCreator } from 'drizzle-orm/mssql-core';
const mssqlTable = mssqlTableCreator((name) => `project1_${name}`);
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: text().notNull(),
});
```
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema/*',
out: './drizzle',
dialect: 'mssql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
tablesFilter: ['project1_*'],
});
```
```sql
CREATE TABLE [project1_users] (
[id] int IDENTITY(1, 1),
[name] text NOT NULL,
CONSTRAINT [project1_users_pkey] PRIMARY KEY([id])
);
```
## Printing SQL Query
Print generated SQL from a query with `.toSQL()`.
```ts
const query = db
.select({ id: users.id, name: users.name })
.from(users)
.groupBy(users.id)
.toSQL();
```
## Raw SQL Queries
Use `db.execute` for raw parametrized SQL.
```ts
import { sql } from 'drizzle-orm';
const statement = sql`select * from ${users} where ${users.id} = ${userId}`;
const result = await db.execute(statement);
```
## Standalone Query Builder
Use the MSSQL query builder without creating a database instance.
```ts
import { QueryBuilder } from 'drizzle-orm/mssql-core';
const qb = new QueryBuilder();
const query = qb.select().from(users).where(eq(users.name, 'Dan'));
const { sql, params } = query.toSQL();
```
## Get Typed Columns
Use `getColumns` to get a typed column map, which is useful when excluding fields from a selection.
```ts
import { getColumns } from 'drizzle-orm';
import { users } from './schema';
const { password, role, ...rest } = getColumns(users);
await db.select({ ...rest }).from(users);
```
```ts
import { int, text, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
});
```
## Get Table Information
Use `getTableConfig` to inspect PostgreSQL table metadata.
```ts
import { getTableConfig, mssqlTable } from 'drizzle-orm/mssql-core';
export const table = mssqlTable(...);
const { columns, indexes, foreignKeys, checks, primaryKeys, name, schema } =
getTableConfig(table);
```
## Compare Object Types
Use `is()` instead of `instanceof` to check Drizzle object types.
```ts
import { Column, is } from 'drizzle-orm';
if (is(value, Column)) {
// value is narrowed to Column
}
```
## Mock Driver
Use `drizzle.mock()` when you need a typed database object without a real PostgreSQL connection.
```ts
import { drizzle } from 'drizzle-orm/node-mssql';
import * as schema from './schema';
const db = drizzle.mock({ schema });
```
Source: https://orm.drizzle.team/docs/mssql/indexes-constraints
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Indexes & Constraints
## Constraints
SQL constraints are the rules enforced on table columns. They are used to prevent invalid data from being entered into the database.
This ensures the accuracy and reliability of your data in the database.
### Default
The `DEFAULT` clause specifies a default value to use for the column if no value 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 { int, time, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable("table", {
int: int().default(42),
time: time().default(sql`cast('14:06:10' AS TIME)`),
});
```
```sql
CREATE TABLE [table] (
[int] int CONSTRAINT [table_int_default] DEFAULT ((42)),
[time] time CONSTRAINT [table_time_default] DEFAULT (cast('14:06:10' AS TIME))
);
```
### Not null
By default, a column can hold **NULL** values. The `NOT NULL` constraint enforces a column to **NOT** accept **NULL** values.
This enforces a field to always contain a value, which means that you cannot insert a new record,
or update a record without adding a value to this field.
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const table = mssqlTable('table', {
int: int().notNull(),
});
```
```sql
CREATE TABLE [table] (
[int] int NOT NULL
);
```
### Unique
The `UNIQUE` constraint ensures that all values in a column are different.
Both the `UNIQUE` and `PRIMARY KEY` constraints provide a guarantee for uniqueness for a column or set of columns.
A `PRIMARY KEY` constraint automatically has a `UNIQUE` constraint.
You can have many `UNIQUE` constraints per table, but only one `PRIMARY KEY` constraint per table.
```typescript
import { int, varchar, unique, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable('user', {
id: int().unique(),
});
export const table = mssqlTable('table', {
id: int().unique('custom_name_1'),
});
export const composite = mssqlTable('composite_example', {
id: int(),
name: varchar({ length: 256 }),
}, (t) => [
unique().on(t.id, t.name),
unique('custom_name_2').on(t.id, t.name)
]);
```
```sql
CREATE TABLE [user] (
[id] int,
CONSTRAINT [user_id_key] UNIQUE([id])
);
CREATE TABLE [table] (
[id] int,
CONSTRAINT [custom_name_1] UNIQUE([id])
);
CREATE TABLE [composite_example] (
[id] int,
[name] varchar(256),
CONSTRAINT [composite_example_id_name_key] UNIQUE([id],[name]),
CONSTRAINT [custom_name_2] UNIQUE([id],[name])
);
```
### Check
The `CHECK` constraint is used to limit the value range that can be placed in a column.
If you define a `CHECK` constraint on a column it will allow only certain values for this column.
If you define a `CHECK` constraint on a table it can limit the values in certain columns based on values in other columns in the row.
```typescript copy
import { sql } from "drizzle-orm";
import { check, int, mssqlTable, text } from "drizzle-orm/mssql-core";
export const users = mssqlTable(
"users",
{
id: int().primaryKey(),
username: text().notNull(),
age: int(),
},
(table) => [
check("age_check1", sql`${table.age} > 21`)
]
);
```
```sql
CREATE TABLE [users] (
[id] int,
[username] text NOT NULL,
[age] int,
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [age_check1] CHECK ([users].[age] > 21)
);
```
### Primary Key
The `PRIMARY KEY` constraint uniquely identifies each record in a table.
Primary keys must contain `UNIQUE` values, and cannot contain `NULL` values.
A table can have only **ONE** primary key; and in the table, this primary key can consist of single or multiple columns (fields).
```typescript
import { int, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
})
export const table = mssqlTable("table", {
id: int().primaryKey(),
})
```
```sql
CREATE TABLE [user] (
[id] int IDENTITY(1, 1),
CONSTRAINT [user_pkey] PRIMARY KEY([id])
);
CREATE TABLE [table] (
[id] int,
CONSTRAINT [table_pkey] PRIMARY KEY([id])
);
```
### Composite Primary Key
Just like `PRIMARY KEY`, composite primary key uniquely identifies each record in a table using multiple fields.
Drizzle ORM provides a standalone `primaryKey` operator for that:
```typescript {18, 19}
import { int, text, primaryKey, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
name: text(),
});
export const book = mssqlTable("book", {
id: int().identity().primaryKey(),
name: text(),
});
export const booksToAuthors = mssqlTable("books_to_authors", {
authorId: int("author_id"),
bookId: int("book_id"),
}, (table) => [
primaryKey({ columns: [table.bookId, table.authorId] }),
// Or PK with custom name
primaryKey({ name: 'custom_name', columns: [table.bookId, table.authorId] })
]);
```
```sql {6}
...
CREATE TABLE [books_to_authors] (
[author_id] int,
[book_id] int,
CONSTRAINT [books_to_authors_pkey] PRIMARY KEY([book_id],[author_id])
);
```
### Foreign key
The `FOREIGN KEY` constraint is used to prevent actions that would destroy links between tables.
A `FOREIGN KEY` is a field (or collection of fields) in one table, that refers to the `PRIMARY KEY` in another table.
The table with the foreign key is called the child table, and the table with the primary key is called the referenced or parent table.
Drizzle ORM provides several ways to declare foreign keys.
You can declare them in a column declaration statement:
```typescript {11}
import { int, text, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().primaryKey().identity(),
name: text(),
});
export const book = mssqlTable("book", {
id: int().primaryKey().identity(),
name: text(),
authorId: int("author_id").references(() => user.id)
});
```
If you want to do a self reference, due to a TypeScript limitations you will have to either explicitly
set return type for reference callback or use a standalone `foreignKey` operator.
```typescript {6,16-19}
import { int, text, foreignKey, type AnyMsSqlColumn, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().primaryKey().identity(),
name: text(),
parentId: int("parent_id").references((): AnyMsSqlColumn => user.id),
});
// or
export const user = mssqlTable("user", {
id: int().primaryKey().identity(),
name: text(),
parentId: int("parent_id")
}, (table) => [
foreignKey({
columns: [table.parentId],
foreignColumns: [table.id],
name: "custom_fk"
})
]);
```
To declare multi-column foreign keys you can use a dedicated `foreignKey` operator:
```typescript copy {4-5,14-15,18-21}
import { int, varchar, primaryKey, foreignKey, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
firstName: varchar('firstName', { length: 100 }),
lastName: varchar('lastName', { length: 100 }),
}, (table) => [
primaryKey({ columns: [table.firstName, table.lastName]})
]);
export const profile = mssqlTable("profile", {
id: int("id").identity().primaryKey(),
userFirstName: varchar("user_first_name", { length: 100 }),
userLastName: varchar("user_last_name", { length: 100 }),
}, (table) => [
foreignKey({
columns: [table.userFirstName, table.userLastName],
foreignColumns: [user.firstName, user.lastName],
name: "custom_name"
})
]);
```
## Indexes
Drizzle ORM provides API for both `index` and `unique index` declaration:
```typescript copy {9-10}
import { int, varchar, index, uniqueIndex, mssqlTable } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().primaryKey().identity(),
name: varchar({ length: 256 }),
email: varchar({ length: 256 }),
}, (table) => [
index("name_idx").on(table.name),
uniqueIndex("email_idx").on(table.email),
]);
```
```sql {5-6}
CREATE TABLE `user` (
...
);
CREATE INDEX [name_idx] ON [user] ([name]);
CREATE UNIQUE INDEX [email_idx] ON [user] ([email]);
```
Drizzle ORM provides set of all params for index creation:
```typescript
// Index declaration reference
index('name_idx')
.on(table.name)
.where(sql``)
```
Source: https://orm.drizzle.team/docs/mssql/insert
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
# SQL Insert
All the values provided to `.values()`are parameterized automatically.
For example, this query:
```ts
await db.insert(users).values({ name: 'Andrew' });
```
will be translated to:
```sql
insert into [users] ([name]) values (@par0); -- params: ['Andrew']
```
Drizzle ORM provides you the most SQL-like way to insert rows into the database tables.
Inserting data with Drizzle is extremely straightforward and sql-like. See for yourself:
```typescript copy
await db.insert(users).values({ name: 'Andrew' });
```
```sql
insert into [users] ([name], [age]) values ('Andrew', default)
```
If you need insert type for a particular table you can use `typeof usersTable.$inferInsert` syntax.
```typescript copy
type NewUser = typeof users.$inferInsert;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);
```
## Insert with output
MSSQL supports the `OUTPUT` clause for returning inserted rows. Drizzle exposes it through the `.output()` method.
```typescript copy
const result = await db
.insert(usersTable)
.output({ id: usersTable.id })
.values([{ name: 'John' }, { name: 'John1' }]);
// ^? { id: number }[]
```
You can also return all inserted columns by calling `.output()` without arguments.
```ts
const result = await db.insert(usersTable).output().values({ name: 'John' });
// ^? typeof usersTable.$inferSelect[]
```
## Insert multiple rows
```typescript copy
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);
```
## Upserts and conflicts
MSSQL does not support PostgreSQL-style `ON CONFLICT` or MySQL-style `ON DUPLICATE KEY UPDATE` clauses. For upsert workflows, use a transaction with explicit `select`/`update`/`insert` logic or execute a dialect-specific `MERGE` statement with the `sql` operator.
## Insert into ... select
Currently not supported
Source: https://orm.drizzle.team/docs/mssql/joins
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Joins [SQL]
Join clause in SQL is used to combine 2 or more tables, based on related columns between them.
Drizzle ORM joins syntax is a balance between the SQL-likeness and type safety.
## Join types
Drizzle ORM has APIs for `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, and `FULL JOIN`.
Lets have a quick look at examples based on below table schemas:
```typescript copy
export const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
});
export const pets = mssqlTable('pets', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
ownerId: int('owner_id').notNull().references(() => users.id),
})
```
### Left Join
```typescript copy
const result = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from [users] left join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
};
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Right Join
```typescript copy
const result = await db.select().from(users).rightJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from [users] right join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join
```typescript copy
const result = await db.select().from(users).innerJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from [users] inner join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Full Join
```typescript copy
const res = await db.select().from(users).fullJoin(pets, eq(users.id, pets.ownerId));
```
```sql
select ... from [users] full join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
## Partial select
If you need to select a particular subset of fields or to have a flat response type, Drizzle ORM
supports joins with partial select and will automatically infer return type based on `.select({ ... })` structure.
```typescript copy
await db.select({
userId: users.id,
petId: pets.id,
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select [users].[id], [pets].[id] from [users] left join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
}[];
```
You might've noticed that `petId` can be null now, it's because we're left joining and there can be users without a pet.
It's very important to keep in mind when using `sql` operator for partial selection fields and aggregations when needed,
you should to use `sql` for proper result type inference, that one is on you!
```typescript copy
const result = await db.select({
userId: users.id,
petId: pets.id,
petName1: sql`upper(${pets.name})`,
petName2: sql`upper(${pets.name})`,
//Ëwe should explicitly tell 'string | null' in type, since we're left joining that field
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select [users].[id], [pets].[id], upper([pets].[name])... from [users] left join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
petName1: unknown;
petName2: string | null;
}[];
```
To avoid plethora of nullable fields when joining tables with lots of columns we can utilise our **nested select object syntax**,
our smart type inference will make whole object nullable instead of making all table fields nullable!
```typescript copy
await db.select({
userId: users.id,
userName: users.name,
pet: {
id: pets.id,
name: pets.name,
upperName: sql`upper(${pets.name})`
}
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from [users] left join [pets] on [users].[id] = [pets].[owner_id]
```
```typescript
// result type
const result: {
userId: number;
userName: string;
pet: {
id: number;
name: string;
upperName: string;
} | null;
}[];
```
## Aliases & Selfjoins
Drizzle ORM supports table aliases which comes really handy when you need to do selfjoins.
Lets say you need to fetch users with their parents:
```typescript copy
import { user } from "./schema";
const parent = alias(user, "parent");
const result = await db
.select()
.from(user)
.leftJoin(parent, eq(parent.id, user.parentId));
```
```sql
select ... from [user] left join [user] [parent] on [parent].[id] = [user].[parent_id]
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
parentId: number;
};
parent: {
id: number;
name: string;
parentId: number;
} | null;
}[];
```
```typescript
export const user = mssqlTable("user", {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
parentId: int("parent_id").notNull().references((): MsSqlColumn => user.id)
});
```
## Aggregating results
Drizzle ORM delivers name-mapped results from the driver without changing the structure.
You're free to operate with results the way you want, here's an example of mapping many-one relational data:
```typescript
type User = typeof users.$inferSelect;
type Pet = typeof pets.$inferSelect;
const rows = await db.select({
user: users,
pet: pets,
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId));
const result = rows.reduce>(
(acc, row) => {
const user = row.user;
const pet = row.pet;
if (!acc[user.id]) {
acc[user.id] = { user, pets: [] };
}
if (pet) {
acc[user.id]!.pets.push(pet);
}
return acc;
},
{}
);
// result type
const result: Record;
```
## Many-to-one example
```typescript
import { mssqlTable, varchar, int } from 'drizzle-orm/mssql-core';
import { drizzle } from 'drizzle-orm/node-mssql';
const cities = mssqlTable('cities', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }),
});
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }),
cityId: int('city_id').references(() => cities.id)
});
const db = drizzle();
const result = await db.select().from(cities).leftJoin(users, eq(cities.id, users.cityId));
```
## Many-to-many example
```typescript
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }),
});
const chatGroups = mssqlTable('chat_groups', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }),
});
const usersToChatGroups = mssqlTable('usersToChatGroups', {
userId: int('user_id').notNull().references(() => users.id),
groupId: int('group_id').notNull().references(() => chatGroups.id),
});
// querying user group with id 1 and all the participants(users)
await db.select()
.from(usersToChatGroups)
.leftJoin(users, eq(usersToChatGroups.userId, users.id))
.leftJoin(chatGroups, eq(usersToChatGroups.groupId, chatGroups.id))
.where(eq(chatGroups.id, 1))
```
Source: https://orm.drizzle.team/docs/mssql/kit-custom-migrations
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
- Drizzle Kit [overview](/docs/mssql/kit-overview) and [config file](/docs/mssql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mssql/drizzle-kit-generate)
- `drizzle-kit migrate` command - [read here](/docs/mssql/drizzle-kit-migrate)
Drizzle lets you generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding, which you can then run with [`drizzle-kit migrate`](/docs/mssql/drizzle-kit-migrate) command.
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init_sql
â â đ 20242409135510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409135510_seed-users.sql
INSERT INTO [users] ([name]) VALUES('Dan');
INSERT INTO [users] ([name]) VALUES('Andrew');
INSERT INTO [users] ([name]) VALUES('Dandrew');
```
### Running JavaScript and TypeScript migrations
We will add ability to run custom JavaScript and TypeScript migration/seeding scripts in the upcoming release, you can follow [github discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2832).
Source: https://orm.drizzle.team/docs/mssql/kit-overview
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mssql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mssql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mssql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mssql/migrations)
**Drizzle Kit** is a CLI tool for managing SQL database migrations with Drizzle.
-D drizzle-kit
Make sure to first go through Drizzle [get started](/docs/mssql/get-started) and [migration fundamentals](/docs/mssql/migrations) and pick SQL migration flow that suits your business needs best.
Based on your schema, Drizzle Kit let's you generate and run SQL migration files,
push schema directly to the database, pull schema from database, spin up drizzle studio and has a couple of utility commands.
drizzle-kit generate
drizzle-kit migrate
drizzle-kit push
drizzle-kit pull
drizzle-kit check
drizzle-kit up
drizzle-kit studio
drizzle-kit export
| | |
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`drizzle-kit generate`](/docs/mssql/drizzle-kit-generate) | lets you generate SQL migration files based on your Drizzle schema either upon declaration or on subsequent changes, [see here](/docs/mssql/drizzle-kit-generate). |
| [`drizzle-kit migrate`](/docs/mssql/drizzle-kit-migrate) | lets you apply generated SQL migration files to your database, [see here](/docs/mssql/drizzle-kit-migrate). |
| [`drizzle-kit pull`](/docs/mssql/drizzle-kit-pull) | lets you pull(introspect) database schema, convert it to Drizzle schema and save it to your codebase, [see here](/docs/mssql/drizzle-kit-pull) |
| [`drizzle-kit push`](/docs/mssql/drizzle-kit-push) | lets you push your Drizzle schema to database either upon declaration or on subsequent schema changes, [see here](/docs/mssql/drizzle-kit-push) |
| [`drizzle-kit studio`](/docs/mssql/drizzle-kit-studio) | will connect to your database and spin up proxy server for Drizzle Studio which you can use for convenient database browsing, [see here](/docs/mssql/drizzle-kit-studio) |
| [`drizzle-kit check`](/docs/mssql/drizzle-kit-check) | will walk through all generate migrations and check for any race conditions(collisions) of generated migrations, [see here](/docs/mssql/drizzle-kit-check) |
| [`drizzle-kit up`](/docs/mssql/drizzle-kit-up) | used to upgrade snapshots of previously generated migrations, [see here](/docs/mssql/drizzle-kit-up) |
| [`drizzle-kit export`](/docs/mssql/drizzle-kit-export) | used to convert a TypeScript schema into raw SQL DDL and print it out [see here](/docs/mssql/drizzle-kit-export) |
Drizzle Kit is configured through [drizzle.config.ts](/docs/mssql/drizzle-config-file) configuration file or via CLI params.
It's required to at least provide SQL `dialect` and `schema` path for Drizzle Kit to know how to generate migrations.
```
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle.config.ts <--- Drizzle config file
â đ package.json
â đ tsconfig.json
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mssql",
schema: "./src/schema.ts",
});
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
dialect: "mssql",
schema: "./src/schema.ts",
dbCredentials: {
url: "./database/",
},
schemaFilter: "dbo",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "drizzle",
},
breakpoints: true,
verbose: true,
});
```
You can provide Drizzle Kit config path via CLI param, it's very useful when you have multiple database stages or multiple databases or different databases on the same project:
drizzle-kit push --config=drizzle-dev.drizzle.config
drizzle-kit push --config=drizzle-prod.drizzle.config
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
Source: https://orm.drizzle.team/docs/mssql/migrations
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
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 Npm from '@mdx/Npm.astro';
import Tag from '@mdx/Tag.astro'
# Drizzle migrations fundamentals
SQL databases require you to specify a **strict schema** of entities you're going to store upfront
and if (when) you need to change the shape of those entities - you will need to do it via **schema migrations**.
There're multiple production grade ways of managing database migrations.
Drizzle is designed to perfectly suits all of them, regardless of you going **database first** or **codebase first**.
**Database first** is when your database schema is a source of truth. You manage your database schema either directly on the database or
via database migration tools and then you pull your database schema to your codebase application level entities.
**Codebase first** is when database schema in your codebase is a source of truth and is under version control. You declare and manage your database schema in JavaScript/TypeScript
and then you apply that schema to the database itself either with Drizzle, directly or via external migration tools.
#### How can Drizzle help?
We've built [**drizzle-kit**](/docs/mssql/kit-overview) - CLI app for managing migrations with Drizzle.
```shell
drizzle-kit migrate
drizzle-kit generate
drizzle-kit push
drizzle-kit pull
drizzle-kit export
```
It is designed to let you choose how to approach migrations based on your current business demands.
It fits in both database and codebase first approaches, it lets you **push your schema** or **generate SQL migration** files or **pull the schema** from database.
It is perfect wether you work alone or in a team.
**Now let's pick the best option for your project:**
**Option 1**
> I manage database schema myself using external migration tools or by running SQL migrations directly on my database.
> From Drizzle I just need to get current state of the schema from my database and save it as TypeScript schema file.
That's a **database first** approach. You have your database schema as a **source of truth** and
Drizzle lets you pull database schema to TypeScript using [`drizzle-kit pull`](/docs/mssql/drizzle-kit-pull) command.
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââââââââ
â â <--- CREATE TABLE [users] (
ââââââââââââââââââââââââââââ â â [id] int identity primary key,
â ~ drizzle-kit pull â â â [name] varchar(255),
âââŦâââââââââââââââââââââââââ â DATABASE â [email] varchar(255) unique
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().identity({ seed: 1 ,increment: 1 }),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }),
}, (table) => [
p.primaryKey({ columns: [table.id], name: "PK__users__3213E83FFAA14405"}),
p.unique("UQ__users__AB6E6164E0557A2A").on(table.email)
]);
```
**Option 2**
> I want to have database schema in my TypeScript codebase,
> I don't wanna deal with SQL migration files.
> I want Drizzle to "push" my schema directly to the database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a **source of truth** and
Drizzle lets you push schema changes to the database using [`drizzle-kit push`](/docs/mssql/drizzle-kit-push) command.
That's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
```typescript {6} filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(), // <--- added column
});
```
```
Add column to `users` table
ââââââââââââââââââââââââââââââââââââââââââââââ
â + email: varchar({ length: 255 }).unique() â
âââŦâââââââââââââââââââââââââââââââââââââââââââ
â
v
ââââââââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦâââââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´ââââââââââââââââââââââââââââââââ
ALTER TABLE [users] ADD [email] varchar(255);
ALTER TABLE [users] ADD CONSTRAINT [users_email_key] UNIQUE([email]);
```
**Option 3**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and apply them to my database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mssql/drizzle-kit-generate)
and then apply them to the database with [`drizzle-kit migrate`](/docs/mssql/drizzle-kit-migrate) commands.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] varchar(255),
[email] varchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [users_email_key] UNIQUE([email])
);
```
```
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. read migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 4**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and I want Drizzle to apply them during runtime
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mssql/drizzle-kit-generate) and then
you can apply them to the database during runtime of your application.
This approach is widely used for **monolithic** applications when you apply database migrations
during zero downtime deployment and rollback DDL changes if something fails.
This is also used in **serverless** deployments with migrations running in **custom resource** once during deployment process.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] varchar(255),
[email] varchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [users_email_key] UNIQUE([email])
);
```
```ts
// index.ts
import { drizzle } from "drizzle-orm/node-mssql"
import { migrate } from 'drizzle-orm/node-mssql/migrator';
const db = drizzle(process.env.DATABASE_URL);
await migrate(db);
```
```
âââââââââââââââââââââââââ
â npx tsx src/index.ts â
âââŦââââââââââââââââââââââ
â
â 1. init database connection ââââââââââââââââââââââââââââ
â 2. read migration.sql files in migrations folder â â
3. fetch migration history from database -------------> â â
â 4. pick previously unapplied migrations <-------------- â DATABASE â
â 5. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 5**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me,
> but I will apply them to my database myself or via external migration tools
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mssql/drizzle-kit-generate) and then
you can apply them to the database either directly or via external migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous scheama
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] varchar(255),
[email] varchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [users_email_key] UNIQUE([email])
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
directly to the database
â ââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââŦâââ>â â
â â â Database â
or via external tools â â â
â â ââââââââââââââââââââââ
â ââââââââââââââââââââââ â
ââââ Bytebase ââââââââââââââ
ââââââââââââââââââââââ¤
â Liquibase â
ââââââââââââââââââââââ¤
â Atlas â
ââââââââââââââââââââââ¤
â etcâĻ â
ââââââââââââââââââââââ
[â] done!
```
**Option 6**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to output the SQL representation of my Drizzle schema to the console,
> and I will apply them to my database via [Atlas](https://atlasgo.io/guides/orms/drizzle)
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you export SQL statements based on your schema changes with [`drizzle-kit export`](/docs/mssql/drizzle-kit-generate) and then
you can apply them to the database via [Atlas](https://atlasgo.io/guides/orms/drizzle) or other external SQL migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mssql-core";
export const users = p.mssqlTable("users", {
id: p.int().primaryKey().identity(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit export â
âââŦâââââââââââââââââââââââ
â
â 1. read your drizzle schema
2. generated SQL representation of your schema
â 3. outputs to console
â
â
v
```
```sql
CREATE TABLE [users] (
[id] int IDENTITY(1, 1),
[name] varchar(255),
[email] varchar(255),
CONSTRAINT [users_pkey] PRIMARY KEY([id]),
CONSTRAINT [users_email_key] UNIQUE([email])
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
via Atlas
â ââââââââââââââââ
â ââââââââââââââââââââââ â â
ââââ Atlas ââââââââââââ>â Database â
ââââââââââââââââââââââ â â
ââââââââââââââââ
[â] done!
```
Source: https://orm.drizzle.team/docs/mssql/operators
import Section from '@mdx/Section.astro';
import Callout from "@mdx/Callout.astro";
# Filter and conditional operators
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select [id], [name], [age] from [users] where [users].[id] = @par0; -- params: [42]
```
We natively support all dialect specific filter and conditional operators.
You can import all filter & conditional from `drizzle-orm`:
```typescript copy
import { eq, ne, gt, gte, ... } from "drizzle-orm";
```
### eq
Value equal to `n`
```typescript copy
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column, 5));
```
```sql copy
SELECT * FROM [table] WHERE [table].[column] = 5
```
```typescript
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] = [table].[column2]
```
### ne
Value is not equal to `n`
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column, 5));
```
```sql
SELECT * FROM [table] WHERE [table].[column] <> 5
```
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] <> [table].[column2]
```
## ---
### gt
Value is greater than `n`
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column, 5));
```
```sql
SELECT * FROM [table] WHERE [table].[column] > 5
```
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] > [table].[column2]
```
### gte
Value is greater than or equal to `n`
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column, 5));
```
```sql
SELECT * FROM [table] WHERE [table].[column] >= 5
```
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] >= [table].[column2]
```
### lt
Value is less than `n`
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column, 5));
```
```sql
SELECT * FROM [table] WHERE [table].[column] < 5
```
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] < [table].[column2]
```
### lte
Value is less than or equal to `n`.
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column, 5));
```
```sql
SELECT * FROM [table] WHERE [table].[column] <= 5
```
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column1, table.column2));
```
```sql
SELECT * FROM [table] WHERE [table].[column1] <= [table].[column2]
```
## ---
### exists
Value exists
```typescript
import { exists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(exists(query));
```
```sql
SELECT * FROM [table] WHERE EXISTS (SELECT * FROM [table2])
```
### notExists
```typescript
import { notExists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(notExists(query));
```
```sql
SELECT * FROM [table] WHERE NOT EXISTS (SELECT * FROM [table2])
```
### isNull
Value is `null`
```typescript
import { isNull } from "drizzle-orm";
db.select().from(table).where(isNull(table.column));
```
```sql
SELECT * FROM [table] WHERE ([table].[column] IS NULL)
```
### isNotNull
Value is not `null`
```typescript
import { isNotNull } from "drizzle-orm";
db.select().from(table).where(isNotNull(table.column));
```
```sql
SELECT * FROM [table] WHERE ([table].[column] IS NOT NULL)
```
## ---
### inArray
Value is in array of values
```typescript
import { inArray } from "drizzle-orm";
db.select().from(table).where(inArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM [table] WHERE [table].[column] IN (1, 2, 3, 4)
```
```typescript
import { inArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(inArray(table.column, query));
```
```sql
SELECT * FROM [table] WHERE [table].[column] IN (SELECT [table2].[column] FROM [table2])
```
### notInArray
Value is not in array of values
```typescript
import { notInArray } from "drizzle-orm";
db.select().from(table).where(notInArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM [table] WHERE [table].[column] NOT IN (1, 2, 3, 4)
```
```typescript
import { notInArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(notInArray(table.column, query));
```
```sql
SELECT * FROM [table] WHERE [table].[column] NOT IN (SELECT [table2].[column] FROM [table2])
```
## ---
### between
Value is between two values
```typescript
import { between } from "drizzle-orm";
db.select().from(table).where(between(table.column, 2, 7));
```
```sql
SELECT * FROM [table] WHERE [table].[column] BETWEEN 2 AND 7
```
### notBetween
Value is not between two value
```typescript
import { notBetween } from "drizzle-orm";
db.select().from(table).where(notBetween(table.column, 2, 7));
```
```sql
SELECT * FROM [table] WHERE [table].[column] NOT BETWEEN 2 AND 7
```
## ---
### like
Value is like other value, case sensitive
```typescript
import { like } from "drizzle-orm";
db.select().from(table).where(like(table.column, "%llo wor%"));
```
```sql
SELECT * FROM [table] WHERE [table].[column] LIKE '%llo wor%'
```
### notLike
Value is not like other value, case sensitive
```typescript
import { notLike } from "drizzle-orm";
db.select().from(table).where(notLike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM [table] WHERE [table].[column] NOT LIKE '%llo wor%'
```
## ---
### not
All conditions must return `false`.
```typescript
import { eq, not } from "drizzle-orm";
db.select().from(table).where(not(eq(table.column, 5)));
```
```sql
SELECT * FROM [table] WHERE NOT ([table].[column] = 5)
```
### and
All conditions must return `true`.
```typescript
import { gt, lt, and } from "drizzle-orm";
db.select().from(table).where(and(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM [table] WHERE ([table].[column] > 5 AND [table].[column] < 7)
```
### or
One or more conditions must return `true`.
```typescript
import { gt, lt, or } from "drizzle-orm";
db.select().from(table).where(or(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM [table] WHERE ([table].[column] > 5 OR [table].[column] < 7)
```
Source: https://orm.drizzle.team/docs/mssql/overview
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro'
# Drizzle ORM
Drizzle ORM is a headless TypeScript ORM with a head. đ˛
> Drizzle is a good friend who's there for you when necessary and doesn't bother when you need some space.
It looks and feels simple, performs on day _1000_ of your project,\
lets you do things your way, and is there when you need it.
**It's the only ORM with both [relational](/docs/mssql/rqb) and [SQL-like](/docs/mssql/select) query APIs**,
providing you the best of both worlds when it comes to accessing your relational data.
Drizzle is lightweight, performant, typesafe, non-lactose, gluten-free, sober, flexible and **serverless-ready by design**.
Drizzle is not just a library, it's an experience. đ¤Š
[](https://bestofjs.org/projects/drizzle-orm)
## Headless ORM?
First and foremost, Drizzle is a library and a collection of complementary opt-in tools.
**ORM** stands for _object relational mapping_, and developers tend to call Django-like or Spring-like tools an ORM.
We truly believe it's a misconception based on legacy nomenclature, and we call them **data frameworks**.
With data frameworks you have to build projects **around them** and not **with them**.
**Drizzle** lets you build your project the way you want, without interfering with your project or structure.
Using Drizzle you can define and manage database schemas in TypeScript, access your data in a SQL-like
or relational way, and take advantage of opt-in tools
to push your developer experience _through the roof_. đ¤¯
## Why SQL-like?
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate/abstract you away from SQL, which
leads to a double learning curve: needing to know 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 can have zero to no
learning curve and access to the full power of SQL.
We bring all the familiar **[SQL schema](/docs/mssql/sql-schema-declaration)**, **[queries](/docs/mssql/select)**,
**[automatic migrations](/docs/mssql/migrations)** and **[one more thing](/docs/mssql/rqb)**. â¨
```typescript copy
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
```
```typescript copy
// manage your schema
export const countries = mssqlTable('countries', {
id: int().primaryKey().identity(),
name: varchar({ length: 256 }),
});
export const cities = mssqlTable('cities', {
id: int().primaryKey().identity(),
name: varchar({ length: 256 }),
countryId: int('country_id').references(() => countries.id),
});
```
```sql
CREATE TABLE [countries] (
[id] int IDENTITY(1, 1),
[name] varchar(256),
CONSTRAINT [countries_pkey] PRIMARY KEY([id])
);
CREATE TABLE [cities] (
[id] int IDENTITY(1, 1),
[name] varchar(256),
[country_id] int,
CONSTRAINT [cities_pkey] PRIMARY KEY([id])
);
ALTER TABLE [cities] ADD CONSTRAINT [cities_country_id_countries_id_fk] FOREIGN KEY ([country_id]) REFERENCES [countries]([id]);
```
## Why not SQL-like?
We're always striving for a perfectly balanced solution, and while SQL-like does cover 100% of the needs,
there are certain common scenarios where you can query data in a better way.
We've built the **[Queries API](/docs/mssql/rqb)** for you, so you can fetch relational nested data from the database
in the most convenient and performant way, and never think about joins and data mapping.
**Drizzle always outputs exactly 1 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
},
});
```
## Serverless?
The best part is no part. **Drizzle has exactly 0 dependencies!**

Drizzle ORM is dialect-specific, slim, performant and serverless-ready **by design**.
We've spent a lot of time to make sure you have best-in-class SQL dialect support, including Postgres, MySQL, and others.
Drizzle operates natively through industry-standard database drivers. We support all major **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[SingleStore](/docs/singlestore/get-started-singlestore)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** drivers out there, and we're adding new ones **[really fast](https://twitter.com/DrizzleORM/status/1653082492742647811?s=20)**.
## Welcome on board!
More and more companies are adopting Drizzle in production, experiencing immense benefits in both DX and performance.
**We're always there to help, so don't hesitate to reach out. We'll gladly assist you in your Drizzle journey!**
We have an outstanding **[Discord community](https://driz.link/discord)** and welcome all builders to our **[Twitter](https://twitter.com/drizzleorm)**.
Now go build something awesome with Drizzle and your **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** database. đ
### Video Showcase
{/* tRPC + NextJS App Router = Simple Typesafe APIs
Jack Herrington 19:17
https://www.youtube.com/watch?v=qCLV0Iaq9zU */}
{/* https://www.youtube.com/watch?v=qDunJ0wVIec */}
{/* https://www.youtube.com/watch?v=NZpPMlSAez0 */}
{/* https://www.youtube.com/watch?v=-A0kMiJqQRY */}
Source: https://orm.drizzle.team/docs/mssql/perf-queries
# Query performance
When it comes to **Drizzle** â we're a thin TypeScript layer on top of SQL with
almost 0 overhead and to make it actual 0, you can utilise our prepared statements API.
**When you run a query on the database, there are several things that happen:**
- all the configurations of the query builder got concatenated to the SQL string
- that string and params are sent to the database driver
- driver compiles SQL query to the binary SQL executable format and sends it to the database
With prepared statements you do SQL concatenation once on the Drizzle ORM side and then database
driver is able to reuse precompiled binary SQL instead of parsing query all the time.
It has extreme performance benefits on large SQL queries.
Different database drivers support prepared statements in different ways and sometimes
Drizzle ORM you can go [**faster than better-sqlite3 driver.**](https://twitter.com/_alexblokh/status/1593593415907909634)
## Prepared statement
```typescript copy {3}
const db = drizzle(...);
const prepared = db.select().from(customers).prepare();
const res1 = await prepared.execute();
const res2 = await prepared.execute();
const res3 = await prepared.execute();
```
## Placeholder
Whenever you need to embed a dynamic runtime value - you can use the `sql.placeholder(...)` api
```ts copy {6,9-10,15,18}
import { sql } from "drizzle-orm";
const p1 = db
.select()
.from(customers)
.where(eq(customers.id, sql.placeholder('id')))
.prepare()
await p1.execute({ id: 10 }) // SELECT * FROM customers WHERE id = 10
await p1.execute({ id: 12 }) // SELECT * FROM customers WHERE id = 12
const p2 = db
.select()
.from(customers)
.where(sql`lower(${customers.name}) like ${sql.placeholder('name')}`)
.prepare();
await p2.execute({ name: '%an%' }) // SELECT * FROM customers WHERE lower(name) like '%an%'
```
Source: https://orm.drizzle.team/docs/mssql/query-utils
import $count from '@mdx/$count.mdx';
# Drizzle query utils
### $count
Currently not supported
Source: https://orm.drizzle.team/docs/mssql/read-replicas
# Read Replicas
When your project involves a set of read replica instances, and you require a convenient method for managing
SELECT queries from read replicas, as well as performing create, delete, and update operations on the primary
instance, you can leverage the `withReplicas()` function within Drizzle
```ts copy
import { drizzle } from "drizzle-orm/node-mssql";
import { ConnectionPool } from "mssql";
import { bit, int, mssqlTable, text, withReplicas } from 'drizzle-orm/mssql-core';
const usersTable = mssqlTable('users', {
id: int('id').primaryKey().identity(),
name: text('name').notNull(),
verified: bit('verified').notNull().default(false),
});
const createClient = async (database: string) => {
const pool = new ConnectionPool({
server: "host",
user: "user",
password: "password",
database,
options: { encrypt: true },
});
await pool.connect();
return pool;
};
const primaryDb = drizzle({ client: await createClient("primary_db") });
const read1 = drizzle({ client: await createClient("read_1") });
const read2 = drizzle({ client: await createClient("read_2") });
const db = withReplicas(primaryDb, [read1, read2]);
```
You can now use the `db` instance the same way you did before. Drizzle will
handle the choice between read replica and the primary instance automatically
```ts
// Read from either the read1 connection or the read2 connection
await db.select().from(usersTable)
// Use the primary database for the delete operation
await db.delete(usersTable).where(eq(usersTable.id, 1))
```
You can use the `$primary` key to force using primary instances even for read operations
```ts
// read from primary
await db.$primary.select().from(usersTable);
```
With Drizzle, you can also specify custom logic for choosing read replicas.
You can make a weighted decision or any other custom selection method for random read replica choice.
Here is an implementation example of custom logic for selecting read replicas,
where the first replica has a 70% chance of being chosen, and the second replica has a 30%
chance of being selected.
Keep in mind that you can implement any type of random selection method for read replicas
```ts
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!
});
await db.select().from(usersTable)
```
Source: https://orm.drizzle.team/docs/mssql/relations-schema-declaration
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle Relations Fundamentals
In the world of databases, especially relational databases, the concept of relations is absolutely fundamental.
Think of "relations" as the connections and links between different pieces of data. Just like in real life,
where people have relationships with each other, or objects are related to categories, databases use relations to model how different
types of information are connected and work together.
### Normalization
Normalization is the process of organizing data in your database to reduce redundancy (duplication) and improve data integrity
(accuracy and consistency). Think of it like tidying up a messy filing cabinet. Instead of having all sorts of papers
crammed into one folder, you organize them into logical folders and categories to make everything easier to find and manage.
- **Reduces Data Redundancy**: Imagine storing a customer's address every time they place an order. If the address changes, you'd have to update it in multiple places! Normalization helps you store information in one place and refer to it from other places, minimizing repetition.
- **Improves Data Integrity**: Less redundancy means less chance of inconsistencies. If you update an address in one place, it's updated everywhere it's needed.
- **Prevents Anomalies**: Normalization helps prevent issues like:
1. **Insertion Anomalies**: Difficulty adding new data because you're missing related information.
2. **Update Anomalies**: Having to update the same information in multiple rows.
3. **Deletion Anomalies**: Accidentally losing valuable information when you delete something seemingly unrelated.
- **Easier to Understand and Maintain**: A normalized database is generally more logically structured and easier to understand, query, and modify.
Normalization is often described in terms of "normal forms" (1NF, 2NF, 3NF, and beyond). While the details can get quite technical, the core ideas are straightforward:
#### 1NF (First Normal Form): `Atomic Values`
**Goal**: Each column should hold a single, indivisible value. No repeating groups of data within a single cell
**Example**: Instead of having a single `address` column that stores `123 Main St, City, USA`, you'd
break it down into separate columns: `street_address`, `city`, `state`, `zip_code`.
```sql
-- Unnormalized (violates 1NF)
CREATE TABLE [Customers_Unnormalized] (
[customer_id] INT PRIMARY KEY,
[name] VARCHAR(255),
[address] VARCHAR(255) -- Problem: Multiple pieces of info in one column
);
-- Normalized to 1NF
CREATE TABLE [Customers_1NF] (
[customer_id] INT PRIMARY KEY,
[name] VARCHAR(255),
[street_address] VARCHAR(255),
[city] VARCHAR(255),
[state] VARCHAR(255),
[zip_code] VARCHAR(10)
);
```
#### 2NF (Second Normal Form): `Eliminate Redundant Data Dependent on Part of the Key`
**Goal**: Applies when you have a table with a composite primary key (a primary key made up of two or more columns).
2NF ensures that all non-key attributes are fully dependent on the entire composite primary key, not just part of it.
Imagine we have a table called `order_items`. This table tracks items within orders, and we use a composite primary key (`order_id`, `product_id`)
because a single order can have multiple of the same product (though in this simplified example, let's assume each product appears only
once per order for clarity, but the composite key logic still applies).
```sql
CREATE TABLE [OrderItems_Unnormalized] (
[order_id] INT,
[product_id] VARCHAR(10),
[product_name] VARCHAR(100),
[product_price] DECIMAL(10, 2),
[quantity] INT,
[order_date] DATE,
PRIMARY KEY ([order_id], [product_id]) -- Composite Primary Key
);
INSERT INTO [OrderItems_Unnormalized] ([order_id], [product_id], [product_name], [product_price], [quantity], [order_date]) VALUES
(101, 'A123', 'Laptop', 1200.00, 1, '2023-10-27'),
(101, 'B456', 'Mouse', 25.00, 2, '2023-10-27'),
(102, 'A123', 'Laptop', 1200.00, 1, '2023-10-28'),
(103, 'C789', 'Keyboard', 75.00, 1, '2023-10-29');
```
```
+------------------------------------------------------------------------------------+
| OrderItems_Unnormalized |
+------------------------------------------------------------------------------------+
| PK (order_id, product_id) | product_name | product_price | quantity | order_date |
+------------------------------------------------------------------------------------+
| 101, A123 | Laptop | 1200.00 | 1 | 2023-10-27 |
| 101, B456 | Mouse | 25.00 | 2 | 2023-10-27 |
| 102, A123 | Laptop | 1200.00 | 1 | 2023-10-28 |
| 103, C789 | Keyboard | 75.00 | 1 | 2023-10-29 |
+------------------------------------------------------------------------------------+
```
**Problem**: Notice that `product_name` and `product_price` are repeated whenever the same `product_id` appears in different orders.
These attributes are only dependent on `product_id`, which is part of the composite primary key (`order_id`, `product_id`), but not the entire key.
This is a partial dependency.
To achieve 2NF, we need to remove the partially dependent attributes (`product_name`, `product_price`) and place them in a separate table where they are
fully dependent on the primary key of that new table.
```
+-------------------+ 1:M +---------------------------+
| Products | <---------- | OrderItems_2NF |
+-------------------+ +---------------------------+
| PK product_id | | PK (order_id, product_id) |
| product_name | | quantity |
| product_price | | order_date |
+-------------------+ | FK product_id |
+---------------------------+
```
```sql
CREATE TABLE [Products] (
[product_id] VARCHAR(10) PRIMARY KEY,
[product_name] VARCHAR(100),
[product_price] DECIMAL(10, 2)
);
CREATE TABLE [OrderItems_2NF] (
[order_id] INT,
[product_id] VARCHAR(10),
[quantity] INT,
[order_date] DATE,
PRIMARY KEY ([order_id], [product_id]), -- Composite Primary Key remains
FOREIGN KEY ([product_id]) REFERENCES [Products]([product_id]) -- Foreign Key to Products
);
-- Insert data into Products
INSERT INTO [Products] ([product_id], [product_name], [product_price]) VALUES
('A123', 'Laptop', 1200.00),
('B456', 'Mouse', 25.00),
('C789', 'Keyboard', 75.00);
-- Insert data into OrderItems_2NF (referencing Products)
INSERT INTO [OrderItems_2NF] ([order_id], [product_id], [quantity], [order_date]) VALUES
(101, 'A123', 1, '2023-10-27'),
(101, 'B456', 2, '2023-10-27'),
(102, 'A123', 1, '2023-10-28'),
(103, 'C789', 1, '2023-10-29');
```
#### 3NF (Third Normal Form): `Eliminate Redundant Data Dependent on Non-Key Attributes`
**Goal**: Remove data that is dependent on other non-key attributes. This is about eliminating transitive dependencies.
**Problem**: Let's say we have a `suppliers` table. We store supplier information, including their `zip_code`, `city`, and `state`. `supplier_id` is the primary key.
```sql
CREATE TABLE [suppliers] (
[supplier_id] VARCHAR(10) PRIMARY KEY,
[supplier_name] VARCHAR(255),
[zip_code] VARCHAR(10),
[city] VARCHAR(100),
[state] VARCHAR(50)
);
INSERT INTO [suppliers] ([supplier_id], [supplier_name], [zip_code], [city], [state]) VALUES
('S1', 'Acme Corp', '12345', 'Anytown', 'NY'),
('S2', 'Beta Inc', '67890', 'Otherville', 'CA'),
('S3', 'Gamma Ltd', '12345', 'Anytown', 'NY');
```
```
+---------------------------------------------------------------+
| suppliers |
+---------------------------------------------------------------+
| PK supplier_id | supplier_name | zip_code | city | state |
+---------------------------------------------------------------+
| S1 | Acme Corp | 12345 | Anytown | NY |
| S2 | Beta Inc | 67890 | Otherville | CA |
| S3 | Gamma Ltd | 12345 | Anytown | NY |
+---------------------------------------------------------------+
```
**Solution**: To achieve 3NF, we remove the attributes dependent on the non-key attribute (`city`, `state` dependent on `zip_code`) and put
them into a separate table keyed by the non-key attribute itself (`zip_code`).
```
+-------------------+ 1:M +--------------------+
| zip_codes | <---------- | suppliers |
+-------------------+ +--------------------+
| PK zip_code | | PK supplier_id |
| city | | supplier_name |
| state | | FK zip_code |
+-------------------+ +--------------------+
```
```sql
CREATE TABLE [zip_codes] (
[zip_code] VARCHAR(10) PRIMARY KEY,
[city] VARCHAR(100),
[state] VARCHAR(50)
);
CREATE TABLE [suppliers] (
[supplier_id] VARCHAR(10) PRIMARY KEY,
[supplier_name] VARCHAR(255),
[zip_code] VARCHAR(10), -- Foreign Key to zip_codes
FOREIGN KEY ([zip_code]) REFERENCES [zip_codes]([zip_code])
);
-- Insert data into zip_codes
INSERT INTO [zip_codes] ([zip_code], [city], [state]) VALUES
('12345', 'Anytown', 'NY'),
('67890', 'Otherville', 'CA');
-- Insert data into suppliers (referencing zip_codes)
INSERT INTO [suppliers] ([supplier_id], [supplier_name], [zip_code]) VALUES
('S1', 'Acme Corp', '12345'),
('S2', 'Beta Inc', '67890'),
('S3', 'Gamma Ltd', '12345');
```
There are additional normal forms, such as `4NF`, `5NF`, `6NF`, `EKNF`, `ETNF`, and `DKNF`. We won't cover these here, but we will create a
dedicated set of tutorials for them in our guides and tutorials section.
### Database Relationships
#### One-to-One
In a one-to-one relationship, each record in `table A` is related to at most one record in `table B`, and each record in `table B` is
related to at most one record in `table A`. It's a very direct, exclusive pairing.
1. **User Profiles and User Account Details**: Think of a website. Each user account (in a Users table) might have exactly one user profile (in a UserProfiles table) containing more detailed information.
2. **Employees and Parking Spaces**: An Employees table and a ParkingSpaces table. Each employee might be assigned at most one parking space, and each parking space is assigned to at most one employee.
3. **Splitting Tables for Organization**: Sometimes, you might split a very wide table into two for better organization or security reasons, maintaining a 1-1 relationship between them.
```
Table A (One Side) Table B (One Side)
+---------+ +---------+
| PK (A) | <---------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
```
#### One-to-Many
In a one-to-many relationship, one record in `table A` can be related to many records in `table B`, but each
record in `table B` is related to at most one record in `table A`. Think of it as a "parent-child" relationship.
1. **Customers and Orders**: One customer can place many orders, but each order belongs to only one customer.
2. **Authors and Books**: One author can write many books, but (let's simplify for now and say) each book is written by one primary author.
3. **Departments and Employees**: One department can have many employees, but each employee belongs to only one department.
```
Table A (One Side) Table B (Many Side)
+---------+ +---------+
| PK (A) | ----------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
(One) (Many)
```
#### Many-to-Many
In a many-to-many relationship, one record in `table A` can be related to many records in `table B`, and one
record in `table B` can be related to many records in `table A`. It's a more complex, bidirectional relationship.
1. **Students and Courses**: One student can enroll in many courses, and one course can have many students enrolled.
2. **Products and Categories**: One product can belong to multiple categories (e.g., a "T-shirt" can be
in "Clothing" and "Summer Wear" categories), and one category can contain many products.
3. **Authors and Books**: A book can be written by multiple authors, and an author can write multiple books.
```
Table A (Many Side) Junction Table Table B (Many Side)
+---------+ +-------------+ +---------+
| PK (A) | -------->| FK (A) | <----| FK (B) |
| ... | | FK (B) | | ... |
+---------+ +-------------+ +---------+
(Many) (Junction) (Many)
```
Many-to-many relationships are not directly implemented with foreign keys between the two main tables.
Instead, you need a `junction` table (also called an associative table or bridging table).
This table acts as an intermediary to link records from both tables.
```sql
-- Table for Students (Many side)
CREATE TABLE [students] (
[id] INT PRIMARY KEY,
[name] VARCHAR(255)
);
-- Table for Courses (Many side)
CREATE TABLE [courses] (
[id] INT PRIMARY KEY,
[name] VARCHAR(255),
[credits] INT
);
-- Junction Table: Enrollments (Connects Students and Courses - M-M relationship)
CREATE TABLE [enrollments] (
[id] INT IDENTITY(1,1) PRIMARY KEY, -- Optional, but good practice for junction tables
[student_id] INT,
[course_id] INT,
[enrollment_date] DATE,
-- Composite Foreign Keys (often part of a composite primary key or unique constraint)
FOREIGN KEY ([student_id]) REFERENCES [students]([id]),
FOREIGN KEY ([course_id]) REFERENCES [courses]([id]),
UNIQUE ([student_id], [course_id]) -- Prevent duplicate enrollments for the same student and course
);
```
### Why Foreign Keys?
You might think of foreign key constraints as simply a way to validate data - ensuring
that when you enter a value in a foreign key column, that value actually exists in the primary key
column of another table. And you'd be partially right! This value checking is the mechanism foreign keys use.
But it's crucial to understand that this validation is not the end goal, it's the means
to a much larger purpose. Foreign key constraints are fundamentally about:
We've discussed relationships like `One-to-Many` between Customers and Orders.
A foreign key is the SQL language's way of telling the database:
> Hey database, I want to enforce a 1-M relationship here. Every value in the customer_id column of the
Orders table must correspond to a valid customer_id in the Customers table.
It's not just a suggestion; it's a constraint the database actively enforces.
The database becomes relationship-aware because of the foreign key.
- This is the core of "data integrity" in the context of relationships. Referential integrity means
that relationships between tables remain consistent and valid over time.
- Foreign keys prevent orphaned records. What's an orphaned record? In our Customer-Order example,
an order that exists in the Orders table but doesn't have a corresponding customer in the Customers
table would be an orphan. Foreign keys prevent this from happening (or control what happens
if you try to delete a customer with orders - via CASCADE, SET NULL, etc.).
- Why is preventing orphans important? Orphaned records break the logical structure of your data.
If you have an order without a customer, you lose crucial context. Queries become unreliable, reports
become inaccurate, and your application's logic can break down.
**Example**:
```
Without a foreign key, you could accidentally delete a customer from the Customers
table while their orders still exist in the Orders table. Suddenly, you have orders that point to
a customer that no longer exists! A foreign key constraint prevents this data inconsistency.
```
- Foreign keys are not just about technical enforcement; they are also a crucial part of database design documentation.
- When you see a foreign key in a database schema, it immediately tells you:
`Table 'X' is related to Table 'Y' in this way.` It's a clear visual and structural indicator of relationships.
- This makes databases easier to understand, maintain, and evolve over time. New developers can quickly
grasp how different parts of the database are connected.
In essence, foreign key constraints are not just about checking values; they are about:
1. Defining the rules of your data relationships
2. Actively enforcing those rules at the database level
3. Guaranteeing data integrity and consistency within those relationships
4. Making your database more robust, reliable, and understandable
### Why NOT Foreign Keys?
While highly beneficial, there are some scenarios where you might reconsider or use Foreign Keys with caution.
These are typically edge cases and often involve trade-offs.
- **Scenario**: Extremely high-volume transactional systems (e.g., real-time logging, very high-frequency trading
platforms, massive IoT data ingestion).
- **Explanation**: Every time you insert or update data in a table with a foreign key, the database system
needs to perform checks to ensure referential integrity. In extremely high-write scenarios, these
checks can introduce a small but potentially noticeable performance overhead.
- **Scenario**: Systems where data is distributed across multiple database nodes or clusters (common in sharded databases, cloud environments, and microservices).
- **Explanation**: Cross-node foreign keys can introduce significant complexity and performance overhead. Validating referential integrity requires communication between nodes, leading to increased latency. Distributed transactions needed to maintain consistency are also more complex and can be less performant than local transactions. In such architectures, application-level data integrity checks or eventual consistency models might be considered alternatives.
- **Scenario**: Integrating a relational database with older legacy systems or non-relational data stores (e.g., NoSQL, flat files, external APIs).
- **Explanation**: Legacy systems or non-relational data might not consistently adhere to the referential integrity rules enforced by foreign keys. Imposing foreign keys in such scenarios can lead to data import issues, data inconsistencies, and might necessitate complex data transformation or application-level integrity management instead. You might need to carefully evaluate the data quality and consistency of the external sources and potentially rely on application logic or ETL processes to ensure data integrity instead of strictly enforcing foreign keys at the database level.
You can also check out some great explanations from the PlanetScale team in their [article](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints#why-does-planetscale-not-recommend-constraints)
### Polymorphic Relations
Polymorphic relationships are a more advanced concept that allows a single relationship to point to
different types of entities or tables. It's about creating more flexible and adaptable relationships when
you have different kinds of data that share some commonality.
Imagine you have an `activities` log. An activity could be a `comment` a `like` or a `share`.
Each of these `activity` types has different details. Instead of creating separate tables and
relationships for each activity type and the things they relate to, you might use a polymorphic approach.
- **Comments/Reviews**: A "Comment" might be related to different types of content: articles, products, videos, etc.
Instead of having separate article_id, product_id, video_id columns in a Comments table, you can use a
polymorphic relationship.
```
+---------------------+
| **Comments** |
+---------------------+
| PK comment_id |
| commentable_type | ------> [Polymorphic Relationship]
| commentable_id | -------->
| user_id |
| comment_text |
| ... |
+---------------------+
^
|
+---------------------+ +---------------------+ +---------------------+
| **Articles** | | **Products** | | **Videos** |
+---------------------+ +---------------------+ +---------------------+
| PK article_id | | PK product_id | | PK video_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +---------------------+
```
- **Notifications:** A notification could be related to a user, an order, a system event, etc.
```
+----------------------+
| **Notifications** |
+----------------------+
| PK notification_id |
| notifiable_type | ------> [Polymorphic Relationship]
| notifiable_id | -------->
| user_id |
| message |
| ... |
+----------------------+
^
|
+---------------------+ +---------------------+ +-----------------------+
| **Users** | | **Orders** | | **System Events** |
+---------------------+ +---------------------+ +-----------------------+
| PK user_id | | PK order_id | | PK event_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +-----------------------+
```
Polymorphic relationships are more complex and are often handled at the application level or
using more advanced database features (depending on the specific database system). Standard SQL doesn't
have direct, built-in support for enforcing polymorphic foreign key constraints in the same way as regular foreign keys.
Source: https://orm.drizzle.team/docs/mssql/relations
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import IsSupportedChipGroup from '@mdx/IsSupportedChipGroup.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import CodeTab from '@mdx/CodeTab.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
# Drizzle soft relations
The sole purpose of Drizzle relations is to let you query your relational data in the most simple and consise way:
```ts
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({ client, schema });
const result = db._query.users.findMany({
with: {
posts: true,
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```ts
import { drizzle } from 'drizzle-orm/node-mssql';
import { eq } from 'drizzle-orm';
import { posts, users } from './schema';
const db = drizzle(client);
const res = await db.select()
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.orderBy(users.id)
const mappedResult =
```
### One-to-one
Drizzle ORM provides you an API to define `one-to-one` relations between tables with the `relations` operator.
An example of a `one-to-one` relation between users and users, where a user can invite another (this example uses a self reference):
```typescript copy {10-15}
import { mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
invitedBy: int('invited_by'),
});
export const usersRelations = relations(users, ({ one }) => ({
invitee: one(users, {
fields: [users.invitedBy],
references: [users.id],
}),
}));
```
Another example would be a user having a profile information stored in separate table. In this case, because the foreign key is stored in the "profile_info" table, the user relation have neither fields or references. This tells Typescript that `user.profileInfo` is nullable:
```typescript copy {9-17}
import { mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const usersRelations = relations(users, ({ one }) => ({
profileInfo: one(profileInfo),
}));
export const profileInfo = mssqlTable('profile_info', {
id: int().identity().primaryKey(),
userId: int('user_id').references(() => users.id),
metadata: ntext(),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, { fields: [profileInfo.userId], references: [users.id] }),
}));
const user = await queryUserWithProfileInfo();
//____^? type { id: number, profileInfo: string | null }
```
### One-to-many
Drizzle ORM provides you an API to define `one-to-many` relations between tables with `relations` operator.
Example of `one-to-many` relation between users and posts they've written:
```typescript copy {9-11, 19-24}
import { mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = mssqlTable('posts', {
id: int().identity().primaryKey(),
content: ntext(),
authorId: int('author_id'),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
Now lets add comments to the posts:
```typescript copy {14,17-22,24-29}
...
export const posts = mssqlTable('posts', {
id: int().identity().primaryKey(),
content: ntext(),
authorId: int('author_id'),
});
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
comments: many(comments)
}));
export const comments = mssqlTable('comments', {
id: int().identity().primaryKey(),
text: ntext(),
authorId: int('author_id'),
postId: int('post_id'),
});
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, {
fields: [comments.postId],
references: [posts.id],
}),
}));
```
### Many-to-many
Drizzle ORM provides you an API to define `many-to-many` relations between tables through so called `junction` or `join` tables,
they have to be explicitly defined and store associations between related tables.
Example of `many-to-many` relation between users and groups:
```typescript copy {9-11, 18-20, 37-46}
import { relations } from 'drizzle-orm/_relations';
import { int, mssqlTable, primaryKey, ntext } from 'drizzle-orm/mssql-core';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const usersRelations = relations(users, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const groups = mssqlTable('groups', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroups = mssqlTable(
'users_to_groups',
{
userId: int('user_id')
.notNull()
.references(() => users.id),
groupId: int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [
primaryKey({ columns: [t.userId, t.groupId] })
],
);
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, {
fields: [usersToGroups.groupId],
references: [groups.id],
}),
user: one(users, {
fields: [usersToGroups.userId],
references: [users.id],
}),
}));
```
### Foreign keys
You might've noticed that `relations` look similar to foreign keys â they even have a `references` property. So what's the difference?
While foreign keys serve a similar purpose, defining relations between tables, they work on a different level compared to `relations`.
Foreign keys are a database level constraint, they are checked on every `insert`/`update`/`delete` operation and throw an error if a constraint is violated.
On the other hand, `relations` are a higher level abstraction, they are used to define relations between tables on the application level only.
They do not affect the database schema in any way and do not create foreign keys implicitly.
What this means is `relations` and foreign keys can be used together, but they are not dependent on each other.
You can define `relations` without using foreign keys (and vice versa), which allows them to be used with databases that do not support foreign keys.
The following two examples will work exactly the same in terms of querying the data using Drizzle relational queries.
```ts {15}
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const profileInfo = mssqlTable('profile_info', {
id: int().identity().primaryKey(),
userId: int('user_id'),
metadata: nvarchar({ mode: 'json' }),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, {
fields: [profileInfo.userId],
references: [users.id],
}),
}));
```
```ts {15}
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const profileInfoRelations = relations(profileInfo, ({ one }) => ({
user: one(users, {
fields: [profileInfo.userId],
references: [users.id],
}),
}));
export const profileInfo = mssqlTable('profile_info', {
id: int().identity().primaryKey(),
userId: int('user_id').references(() => users.id),
metadata: nvarchar({ mode: 'json' }),
});
```
### Foreign key actions
For more information check the [MSSQL foreign keys docs](https://learn.microsoft.com/en-us/sql/relational-databases/tables/create-foreign-key-relationships?view=sql-server-ver17)
You can specify actions that should occur when the referenced data in the parent table is modified. These actions are known as "foreign key actions." MSSQL provides several options for these actions.
On Delete/ Update Actions
- `CASCADE`: When a row in the parent table is deleted, all corresponding rows in the child table will also be deleted. This ensures that no orphaned rows exist in the child table.
- `NO ACTION`: This is the default action. It prevents the deletion of a row in the parent table if there are related rows in the child table. The DELETE operation in the parent table will fail.
- `SET DEFAULT`: If a row in the parent table is deleted, the foreign key column in the child table will be set to its default value if it has one. If it doesn't have a default value, the DELETE operation will fail.
- `SET NULL`: When a row in the parent table is deleted, the foreign key column in the child table will be set to NULL. This action assumes that the foreign key column in the child table allows NULL values.
> Analogous to ON DELETE there is also ON UPDATE which is invoked when a referenced column is changed (updated). The possible actions are the same, except that column lists cannot be specified for SET NULL and SET DEFAULT. In this case, CASCADE means that the updated values of the referenced column(s) should be copied into the referencing row(s).
in drizzle you can add foreign key action using `references()` second argument.
type of the actions
```typescript
export type UpdateDeleteAction = 'cascade' | 'no action' | 'set null' | 'set default';
// second argument of references interface
actions?: {
onUpdate?: UpdateDeleteAction;
onDelete?: UpdateDeleteAction;
} | undefined
```
In the following example, adding `onDelete: 'cascade'` to the author field on the `posts` schema means that deleting the `user` will also delete all related Post records.
```typescript {11}
import { mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const posts = mssqlTable('posts', {
id: int().identity().primaryKey(),
name: ntext(),
author: int().references(() => users.id, {onDelete: 'cascade'}).notNull(),
});
```
For constraints specified with the `foreignKey` operator, foreign key actions are defined with the syntax:
```typescript {18-19}
import { foreignKey, mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const posts = mssqlTable('posts', {
id: int().identity().primaryKey(),
name: ntext(),
author: int().notNull(),
}, (table) => [
foreignKey({
name: "author_fk",
columns: [table.author],
foreignColumns: [users.id],
})
.onDelete('cascade')
.onUpdate('cascade')
]);
```
### Disambiguating relations
Drizzle also provides the `relationName` option as a way to disambiguate
relations when you define multiple of them between the same two tables. For
example, if you define a `posts` table that has the `author` and `reviewer`
relations.
```ts {9-12, 21-32}
import { mssqlTable, ntext, int } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int().identity().primaryKey(),
name: ntext(),
});
export const usersRelations = relations(users, ({ many }) => ({
author: many(posts, { relationName: 'author' }),
reviewer: many(posts, { relationName: 'reviewer' }),
}));
export const posts = mssqlTable('posts', {
id: int().identity().primaryKey(),
content: ntext(),
authorId: int('author_id'),
reviewerId: int('reviewer_id'),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
relationName: 'author',
}),
reviewer: one(users, {
fields: [posts.reviewerId],
references: [users.id],
relationName: 'reviewer',
}),
}));
```
Source: https://orm.drizzle.team/docs/mssql/rqb
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Drizzle Queries
Drizzle ORM is designed to be a thin typed layer on top of SQL.
We truly believe we've designed the best way to operate an SQL database from TypeScript and it's time to make it better.
Relational queries are meant to provide you with a great developer experience for querying
nested relational data from an SQL database, avoiding multiple joins and complex data mappings.
It is an extension to the existing schema definition and query builder.
You can opt-in to use it based on your needs.
We've made sure you have both the best-in-class developer experience and performance.
```typescript copy /schema/3
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({ schema });
const result = await db._query.users.findMany({
with: {
posts: true
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```typescript copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const posts = mssqlTable('posts', {
id: int().primaryKey().identity(),
content: text().notNull(),
authorId: int('author_id').notNull(),
});
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
```
â ī¸ If you have SQL schema declared in multiple files you can do it like that
```typescript copy /schema/3
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
const result = await db._query.users.findMany({
with: {
posts: true
},
});
```
```ts
// schema declaration in the first file
```
```ts
// schema declaration in the second file
```
## Querying
Relational queries are an extension to Drizzle's original **[query builder](/docs/mssql/select)**.
You need to provide all `tables` and `relations` from your schema file/files upon `drizzle()`
initialization and then just use the `db._query` API.
`drizzle` import path depends on the **[database driver](/docs/mssql/connect-overview)** you're using.
```ts
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({ schema });
await db._query.users.findMany(...);
```
```ts
// if you have schema in multiple files
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/node-mssql';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
await db._query.users.findMany(...);
```
```typescript copy
import { type AnyMsSqlColumn, bit, int, mssqlTable, primaryKey, text, datetime2 } from 'drizzle-orm/mssql-core';
import { relations } from 'drizzle-orm/_relations';
export const users = mssqlTable('users', {
id: int('id').primaryKey().identity(),
name: text('name').notNull(),
verified: bit('verified').notNull(),
invitedBy: int('invited_by').references((): AnyMsSqlColumn => users.id),
});
export const usersRelations = relations(users, ({ one, many }) => ({
invitee: one(users, { fields: [users.invitedBy], references: [users.id] }),
usersToGroups: many(usersToGroups),
posts: many(posts),
}));
export const groups = mssqlTable('groups', {
id: int('id').primaryKey().identity(),
name: text('name').notNull(),
description: text('description'),
});
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroups = mssqlTable('users_to_groups', {
id: int('id').primaryKey().identity(),
userId: int('user_id').notNull().references(() => users.id),
groupId: int('group_id').notNull().references(() => groups.id),
}, (t) => [
primaryKey({ columns: [t.userId, t.groupId] })
]);
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, { fields: [usersToGroups.groupId], references: [groups.id] }),
user: one(users, { fields: [usersToGroups.userId], references: [users.id] }),
}));
export const posts = mssqlTable('posts', {
id: int('id').primaryKey().identity(),
content: text('content').notNull(),
authorId: int('author_id').references(() => users.id),
createdAt: datetime2('created_at').notNull().defaultGetDate(),
});
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const comments = mssqlTable('comments', {
id: int('id').primaryKey().identity(),
content: text('content').notNull(),
creator: int('creator').references(() => users.id),
postId: int('post_id').references(() => posts.id),
createdAt: datetime2('created_at').notNull().defaultGetDate(),
});
export const commentsRelations = relations(comments, ({ one, many }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.creator], references: [users.id] }),
likes: many(commentLikes),
}));
export const commentLikes = mssqlTable('comment_likes', {
id: int('id').primaryKey().identity(),
creator: int('creator').references(() => users.id),
commentId: int('comment_id').references(() => comments.id),
createdAt: datetime2('created_at').notNull().defaultGetDate(),
});
export const commentLikesRelations = relations(commentLikes, ({ one }) => ({
comment: one(comments, { fields: [commentLikes.commentId], references: [comments.id] }),
author: one(users, { fields: [commentLikes.creator], references: [users.id] }),
}));
```
Drizzle provides `.findMany()` and `.findFirst()` APIs.
### Find many
```typescript copy
const users = await db._query.users.findMany();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: bit;
invitedBy: number | null;
}[];
```
### Find first
`.findFirst()` will add `limit 1` to the query.
```typescript copy
const user = await db._query.users.findFirst();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: bit;
invitedBy: number | null;
};
```
### Include relations
`With` operator lets you combine data from multiple related tables and properly aggregate results.
**Getting all posts with comments:**
```typescript copy
const posts = await db._query.posts.findMany({
with: {
comments: true,
},
});
```
**Getting first post with comments:**
```typescript copy
const post = await db._query.posts.findFirst({
with: {
comments: true,
},
});
```
You can chain nested with statements as much as necessary.
For any nested `with` queries Drizzle will infer types using [Core Type API](/docs/mssql/goodies#type-api).
**Get all users with posts. Each post should contain a list of comments:**
```typescript copy
const users = await db._query.users.findMany({
with: {
posts: {
with: {
comments: true,
},
},
},
});
```
### Partial fields select
`columns` parameter lets you include or omit columns you want to get from the database.
Drizzle performs partial selects on the query level, no additional data is transferred from the database.
Keep in mind that **a single SQL statement is outputted by Drizzle.**
**Get all posts with just `id`, `content` and include `comments`:**
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});
```
**Get all posts without `content`:**
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
content: false,
},
});
```
When both `true` and `false` select options are present, all `false` options are ignored.
If you include the `name` field and exclude the `id` field, `id` exclusion will be redundant,
all fields apart from `name` would be excluded anyways.
**Exclude and Include fields in the same query:**
```typescript copy
const users = await db._query.users.findMany({
columns: {
name: true,
id: false //ignored
},
});
```
```ts
// result type
const users: {
name: string;
};
```
**Only include columns from nested relations:**
```typescript copy
const res = await db._query.users.findMany({
columns: {},
with: {
posts: true
}
});
```
```ts
// result type
const res: {
posts: {
id: number,
text: string
}
}[];
```
### Nested partial fields select
Just like with **[`partial select`](#partial-select)**, you can include or exclude columns of nested relations:
```typescript copy
const posts = await db._query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});
```
### Select filters
Just like in our SQL-like query builder,
relational queries API lets you define filters and conditions with the list of our **[`operators`](/docs/mssql/operators)**.
You can either import them from `drizzle-orm` or use from the callback syntax:
```typescript copy
import { eq } from 'drizzle-orm';
const users = await db._query.users.findMany({
where: eq(users.id, 1)
})
```
```ts copy
const users = await db._query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
})
```
Find post with `id=1` and comments that were created before particular date:
```typescript copy
await db._query.posts.findMany({
where: (posts, { eq }) => (eq(posts.id, 1)),
with: {
comments: {
where: (comments, { lt }) => lt(comments.createdAt, new Date()),
},
},
});
```
### Limit & Offset
Drizzle ORM provides `limit` & `offset` API for queries and for the nested entities.
**Find 5 posts:**
```typescript copy
await db._query.posts.findMany({
limit: 5,
});
```
**Find posts and get 3 comments at most:**
```typescript copy
await db._query.posts.findMany({
with: {
comments: {
limit: 3,
},
},
});
```
`offset` is only available for top level query.
```typescript
await db._query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // incorrect â
limit: 3,
},
},
});
```
Find posts with comments from the 6th to the 10th post:
```typescript copy
await db._query.posts.findMany({
limit: 5,
offset: 5,
with: {
comments: true,
},
});
```
### Order By
Drizzle provides API for ordering in the relational query builder.
You can use same ordering **[core API](/docs/mssql/select#order-by)** or use
`order by` operator from the callback with no imports.
```typescript copy
import { desc, asc } from 'drizzle-orm';
await db._query.posts.findMany({
orderBy: [asc(posts.id)],
});
```
```typescript copy
await db._query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
});
```
**Order by `asc` + `desc`:**
```typescript copy
await db._query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
with: {
comments: {
orderBy: (comments, { desc }) => [desc(comments.id)],
},
},
});
```
### Include custom fields
Relational query API lets you add custom additional fields.
It's useful when you need to retrieve data and apply additional functions to it.
As of now aggregations are not supported in `extras`, please use **[`core queries`](/docs/mssql/select)** for that.
```typescript copy {5}
import { sql } from 'drizzle-orm';
await db._query.users.findMany({
extras: {
loweredName: sql`lower(${users.name})`.as('lowered_name'),
},
})
```
```typescript copy {3}
await db._query.users.findMany({
extras: (table, { sql }) => ({
loweredName: sql`lower(${table.name})`.as('lowered_name'),
}),
})
```
`lowerName` as a key will be included to all fields in returned object.
You have to explicitly specify `.as("")`
To retrieve all users with groups, but with the fullName field included (which is a concatenation of firstName and lastName),
you can use the following query with the Drizzle relational query builder.
```typescript copy
const res = await db._query.users.findMany({
extras: {
fullName: sql`concat(${users.name}, " ", ${users.name})`.as('full_name'),
},
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});
```
```ts
// result type
const res: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
fullName: string;
usersToGroups: {
group: {
id: number;
name: string;
description: string | null;
};
}[];
}[];
```
To retrieve all posts with comments and add an additional field to calculate the size of the post content and the size of each comment content:
```typescript copy
const res = await db._query.posts.findMany({
extras: (table, { sql }) => ({
contentLength: (sql`length(${table.content})`).as('content_length'),
}),
with: {
comments: {
extras: {
commentSize: sql`length(${comments.content})`.as('comment_size'),
},
},
},
});
```
```ts
// result type
const res: {
id: number;
createdAt: Date;
content: string;
authorId: number | null;
contentLength: number;
comments: {
id: number;
createdAt: Date;
content: string;
creator: number | null;
postId: number | null;
commentSize: number;
}[];
}[];
```
### Prepared statements
Prepared statements are designed to massively improve query performance â [see here.](/docs/mssql/perf-queries)
In this section, you can learn how to define placeholders and execute prepared statements
using the Drizzle relational query builder.
##### **Placeholder in `where`**
```ts copy
const prepared = db._query.users.findMany({
where: ((users, { eq }) => eq(users.id, sql.placeholder('id'))),
with: {
posts: {
where: ((posts, { eq }) => eq(posts.id, sql.placeholder('pid'))),
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ id: 1, pid: 1 });
```
##### **Placeholder in `limit`**
```ts copy
const prepared = db._query.users.findMany({
with: {
posts: {
limit: placeholder('limit'),
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ limit: 1 });
```
##### **Placeholder in `offset`**
```ts copy
const prepared = db._query.users.findMany({
offset: placeholder('offset'),
with: {
posts: true,
},
}).prepare();
const usersWithPosts = await prepared.execute({ offset: 1 });
```
##### **Multiple placeholders**
```ts copy
const prepared = db._query.users.findMany({
limit: placeholder('uLimit'),
offset: placeholder('uOffset'),
where: ((users, { eq, or }) => or(eq(users.id, placeholder('id')), eq(users.id, 3))),
with: {
posts: {
where: ((users, { eq }) => eq(users.id, placeholder('pid'))),
limit: placeholder('pLimit'),
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 });
```
Source: https://orm.drizzle.team/docs/mssql/schemas
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# Table schemas
If you declare an entity within a schema, query builder will prepend schema names in queries:
`select * from [schema].[users]`
```ts copy {3,5}
import { int, text, mssqlSchema, nvarchar } from 'drizzle-orm/mssql-core';
export const mySchema = mssqlSchema('my_schema');
export const mySchemaUsers = mySchema.table('users', {
id: int().identity().primaryKey(),
name: text(),
color: nvarchar({ length: 100 }).default('red'),
});
```
```sql
CREATE SCHEMA [my_schema];
CREATE TABLE [my_schema].[users] (
[id] int IDENTITY(1, 1),
[name] text,
[color] nvarchar(100) CONSTRAINT [users_color_default] DEFAULT ('red'),
CONSTRAINT [users_pkey] PRIMARY KEY([id])
);
```
Source: https://orm.drizzle.team/docs/mssql/seed-functions
import Callout from '@mdx/Callout.astro';
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
# Generators
For now, specifying `arraySize` along with `isUnique` in generators that support it will result in unique values being generated (not unique arrays), which will then be packed into arrays.
## ---
### `default`
Generates the same given value each time the generator is called.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`defaultValue` |-- |`any`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.default({
// value you want to generate
defaultValue: "post content",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `valuesFromArray`
Generates values from given array
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`values` |-- |`any[]` \| `{ weight: number; values: any[] }[]`
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
title: funcs.valuesFromArray({
// Array of values you want to generate (can be an array of weighted values)
values: ["Title1", "Title2", "Title3", "Title4", "Title5"],
// Property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `intPrimaryKey`
Generates sequential integers starting from 1.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |-- |-- |--
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
id: funcs.intPrimaryKey(),
},
},
}));
```
### `number`
Generates numbers with a floating point within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`precision` |`100` |`number`
| |`maxValue` |``` `precision * 1000` if isUnique equals false``` ``` `precision * count` if isUnique equals true``` |`number`
| |`minValue` |`-maxValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitPrice: funcs.number({
// lower border of range.
minValue: 10,
// upper border of range.
maxValue: 120,
// precision of generated number:
// precision equals 10 means that values will be accurate to one tenth (1.2, 34.6);
// precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67).
precision: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `int`
Generates integers within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`maxValue` |``` `1000` if isUnique equals false``` ``` `count * 10` if isUnique equals true``` |`number \| bigint`
| |`minValue` |`-maxValue` |`number \| bigint`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitsInStock: funcs.int({
// lower border of range.
minValue: 0,
// lower border of range.
maxValue: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `bit`
Generates bit values (true or false)
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
isAvailable: funcs.bit({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `date`
Generates a date within the given range
| | param | default | type
|:-| :-------- | :-------------- | :--------
| |`minDate` |``` `'2024-05-08' - 4 years` - if maxDate is not set``` ``` `maxDate - 8 years` - if maxDate is set ``` |`string \| Date`
| |`maxDate` |``` `'2024-05-08' + 4 years` - if minDate is not set``` ``` `minDate + 8 years` - if minDate is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
If only one of the parameters (`minDate` or `maxDate`) is provided, the unspecified parameter will be calculated by adding or subtracting 8 years to/from the specified one
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthDate: funcs.date({
// lower border of range.
minDate: "1990-01-01",
// upper border of range.
maxDate: "2010-12-31",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `time`
Generates time in 24-hour format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |`'00:00:00.000Z'` |`string \| Date`
| |`max` |`'23:59:59.999Z'` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthTime: funcs.time({
// lower border of range.
min: "11:12:13.141",
// upper border of range.
max: "15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `timestamp`
Generates timestamps
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.timestamp({
// lower border of range.
min: "2025-03-07T11:12:13.141",
// upper border of range.
max: "2025-03-08T15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `datetime`
Generates datetime objects
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.datetime({
// lower border of range.
min: "2025-03-07 13:12:13Z",
// upper border of range.
max: "2025-03-09 15:12:13Z",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `year`
Generates years in `YYYY` format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthYear: funcs.year({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `text`
Generates JSON objects with a fixed structure
```ts
{ email, name, isGraduated, hasJob, salary, startedWorking, visitedCountries}
// or
{ email, name, isGraduated, hasJob, visitedCountries }
```
> The JSON structure will be picked randomly
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
metadata: funcs.text({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `interval`
Generates time intervals.
Example of a generated value: `1 year 12 days 5 minutes`
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
timeSpentOnWebsite: funcs.interval({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `string`
Generates random strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
hashedPassword: funcs.string({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `uuid`
Generates v4 UUID strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
id: funcs.uuid({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `firstName`
Generates a person's first name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
firstName: funcs.firstName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `lastName`
Generates a person's last name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
lastName: funcs.lastName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `fullName`
Generates a person's full name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
fullName: funcs.fullName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `email`
Generates unique email addresses
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
email: funcs.email({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `phoneNumber`
Generates unique phone numbers
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`template` |-- |`string`
| |`prefixes` |[Used dataset for prefixes](https://github.com/OleksiiKH0240/drizzle-orm/blob/main/drizzle-seed/src/datasets/phonesInfo.ts) |`string[]`
| |`generatedDigitsNumbers` | `7` - `if prefixes was defined` |`number \| number[]`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
//generate phone number using template property
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `template` - phone number template, where all '#' symbols will be substituted with generated digits.
template: "+(380) ###-####",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
//generate phone number using prefixes and generatedDigitsNumbers properties
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: 7,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
// generate phone number using prefixes and generatedDigitsNumbers properties but with different generatedDigitsNumbers for prefixes
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67", "+1"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: [7, 7, 10],
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `country`
Generates country's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
country: funcs.country({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `city`
Generates city's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
city: funcs.city({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `streetAddress`
Generates street address
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
streetAddress: funcs.streetAddress({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `jobTitle`
Generates job titles
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
jobTitle: funcs.jobTitle({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `postcode`
Generates postal codes
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
postcode: funcs.postcode({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `state`
Generates US states
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
state: funcs.state({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `companyName`
Generates random company's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
company: funcs.companyName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `loremIpsum`
Generates `lorem ipsum` text sentences.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`sentencesCount` | 1 |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.loremIpsum({
// `sentencesCount` - number of sentences you want to generate as one generated value(string).
sentencesCount: 2,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `point`
Generates 2D points within specified ranges for x and y coordinates.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`maxXValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minXValue` |`-maxXValue` |`number`
| |`maxYValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minYValue` |`-maxYValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
triangles: {
columns: {
pointCoords: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minXValue` - lower bound of range for x coordinate.
minXValue: -5,
// `maxXValue` - upper bound of range for x coordinate.
maxXValue: 20,
// `minYValue` - lower bound of range for y coordinate.
minYValue: 0,
// `maxYValue` - upper bound of range for y coordinate.
maxYValue: 30,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `line`
Generates 2D lines within specified ranges for a, b and c parameters of line.
```
line equation: a*x + b*y + c = 0
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`maxAValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minAValue` |`-maxAValue` |`number`
| |`maxBValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minBValue` |`-maxBValue` |`number`
| |`maxCValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minCValue` |`-maxCValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
lines: {
columns: {
lineParams: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minAValue` - lower bound of range for a parameter.
minAValue: -5,
// `maxAValue` - upper bound of range for x parameter.
maxAValue: 20,
// `minBValue` - lower bound of range for y parameter.
minBValue: 0,
// `maxBValue` - upper bound of range for y parameter.
maxBValue: 30,
// `minCValue` - lower bound of range for y parameter.
minCValue: 0,
// `maxCValue` - upper bound of range for y parameter.
maxCValue: 10,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `bitString`
Generates bit strings based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`dimensions` |`database column bit-length` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
bitStringTable: {
columns: {
bit: funcs.bitString({
// desired length of each bit string (e.g., `dimensions = 3` produces values like `'010'`).
dimensions: 12,
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
}),
},
},
}));
```
### `inet`
Generates ip addresses based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
| |`ipAddress` |`'ipv4'` |`'ipv4' \| 'ipv6'`
| |`includeCidr`|`true` |`bit`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
inetTable: {
columns: {
inet: funcs.inet({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// type of IP address to generate â either "ipv4" or "ipv6";
ipAddress: "ipv4",
// determines whether generated IPs include a CIDR suffix.
includeCidr: true,
}),
},
},
}));
```
### `geometry`
Generates geometry objects based on the given parameters.
Currently, if you set arraySize to a value greater than 1
or try to insert more than one `geometry point` element into a `geometry(point, 0)[]` column in MSSQL via drizzle-orm,
youâll encounter an error.
This bug is already in the backlog.
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, mssqlTable } from 'drizzle-orm/mssql-core';
const geometryTable = mssqlTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(3),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// currently arraySize with values > 1 are not supported
arraySize: 3,
}),
},
},
}));
```
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, mssqlTable } from 'drizzle-orm/mssql-core';
const geometryTable = mssqlTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(1),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// will work as expected
arraySize: 1,
}),
},
},
}));
```
Currently, if you set the SRID of a `geometry(point)` column to anything other than 0 (for example, 4326) in your drizzle-orm table declaration,
youâll encounter an error during the seeding process.
This bug is already in the backlog.
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, mssqlTable } from 'drizzle-orm/mssql-core';
const geometryTable = mssqlTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 4326 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, mssqlTable } from 'drizzle-orm/mssql-core';
const geometryTable = mssqlTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 0 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
| |`type` |`'point'` |`'point'`
| |`srid` |`4326` |`4326 \| 3857`
| |`decimalPlaces` |`6` |`1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryPointTuple: funcs.geometry({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 1,
// geometry type to generate; currently only `'point'` is supported;
type: "point",
// Spatial Reference System Identifier: determines what type of point will be generated - either `4326` or `3857`;
srid: 4326,
// number of decimal places for points when `srid` is `4326` (e.g., `decimalPlaces = 3` produces values like `'point(30.723 46.482)'`).
decimalPlaces: 5,
}),
},
},
}));
```
### `vector`
Generates vectors based on the provided parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`bit`
| |`arraySize` |-- |`number`
| |`decimalPlaces` |`2` |`number`
| |`dimensions` |`database columnâs dimensions` |`number`
| |`minValue` |`-1000` |`number`
| |`maxValue` |`1000` |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
vectorTable: {
columns: {
vector: funcs.vector({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// number of decimal places for each vector element (e.g., `decimalPlaces = 3` produces values like `1.123`);
decimalPlaces: 5,
// number of elements in each generated vector (e.g., `dimensions = 3` produces values like `[1,2,3]`);
dimensions: 12,
// minimum allowed value for each vector element;
minValue: -100,
// maximum allowed value for each vector element.
maxValue: 100,
}),
},
},
}));
```
Source: https://orm.drizzle.team/docs/mssql/seed-overview
import Npm from "@mdx/Npm.astro";
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
import Callout from '@mdx/Callout.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
# Drizzle Seed
`drizzle-seed` is a TypeScript library that helps you generate deterministic, yet realistic,
fake data to populate your database. By leveraging a seedable pseudorandom number generator (pRNG),
it ensures that the data you generate is consistent and reproducible across different runs.
This is especially useful for testing, development, and debugging purposes.
#### What is Deterministic Data Generation?
Deterministic data generation means that the same input will always produce the same output.
In the context of `drizzle-seed`, when you initialize the library with the same seed number,
it will generate the same sequence of fake data every time. This allows for predictable and repeatable data sets.
#### Pseudorandom Number Generator (pRNG)
A pseudorandom number generator is an algorithm that produces a sequence of numbers
that approximates the properties of random numbers. However, because it's based on an initial value
called a seed, you can control its randomness. By using the same seed, the pRNG will produce the
same sequence of numbers, making your data generation process reproducible.
#### Benefits of Using a pRNG:
- Consistency: Ensures that your tests run on the same data every time.
- Debugging: Makes it easier to reproduce and fix bugs by providing a consistent data set.
- Collaboration: Team members can share seed numbers to work with the same data sets.
With drizzle-seed, you get the best of both worlds: the ability to generate realistic fake data and the control to reproduce it whenever needed.
## Installation
drizzle-seed
## Basic Usage
In this example we will create 10 users with random names and ids
```ts {12}
import { mssqlTable, int, varchar } from "drizzle-orm/mssql-core";
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
const users = mssqlTable("users", {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users });
}
main();
```
## Options
**`count`**
By default, the `seed` function will create 10 entities.
However, if you need more for your tests, you can specify this in the seed options object
```ts
await seed(db, schema, { count: 1000 });
```
**`seed`**
If you need a seed to generate a different set of values for all subsequent runs, you can define a different number
in the `seed` option. Any new number will generate a unique set of values
```ts
await seed(db, schema, { seed: 12345 });
```
## Reset database
With `drizzle-seed`, you can easily reset your database and seed it with new values, for example, in your test suites
```ts
// path to a file with schema you want to reset
import * as schema from "./schema.ts";
import { reset } from "drizzle-seed";
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await reset(db, schema);
}
main();
```
Different dialects will have different strategies for database resetting. For MSSQL:
For MSSQL, the `drizzle-seed` package will first disable `FOREIGN_KEY_CHECKS` to ensure the next step won't fail, and then generate `TRUNCATE` statements to empty the content of all tables
```sql
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE tableName1;
TRUNCATE tableName2;
...
SET FOREIGN_KEY_CHECKS = 1;
```
## Refinements
In case you need to change the behavior of the seed generator functions that `drizzle-seed` uses by default, you can specify your own implementation and even use your own list of values for the seeding process
`.refine` is a callback that receives a list of all available generator functions from `drizzle-seed`. It should return an object with keys representing the tables you want to refine, defining their behavior as needed.
Each table can specify several properties to simplify seeding your database:
- `columns`: Refine the default behavior of each column by specifying the required generator function, or exclude the column from seeding by specifying `false`.
- `count`: Specify the number of rows to insert into the database. By default, it's 10. If a global count is defined in the `seed()` options, the count defined here will override it for this specific table.
- `with`: Define how many referenced entities to create for each parent table if you want to generate associated entities.
You can also specify a weighted random distribution for the number of referenced values you want to create. For details on this API, you can refer to [Weighted Random docs](#weighted-random) docs section
**API**
```ts
await seed(db, schema).refine((f) => ({
users: {
columns: {},
count: 10,
with: {
posts: 10
}
},
}));
```
Let's check a few examples with an explanation of what will happen:
```ts filename='schema.ts'
import { mssqlTable, int, varchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable("users", {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
});
export const posts = mssqlTable("posts", {
id: int().primaryKey().identity(),
description: varchar({ length: 255 }),
userId: int().references(() => users.id),
});
```
**Example 1**: Seed only the `users` table with 20 entities and with refined seed logic for the `name` column
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: f.fullName(),
},
count: 20
}
}));
}
main();
```
**Example 2**: Seed only the `users` table, excluding the `name` column from seeding
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: false, // the name column will not be seeded, allowing the database to use its default value.
}
}
}));
}
main();
```
**Example 3**: Seed the `users` table with 20 entities and add 10 `posts` for each `user` by seeding the `posts` table and creating a reference from `posts` to `users`
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 20,
with: {
posts: 10
}
}
}));
}
main();
```
**Example 4**: Seed the `users` table with 5 entities and populate the database with 100 `posts` without connecting them to the `users` entities. Refine `id` generation for `users` so
that it will give any int from `10000` to `20000` and remains unique, and refine `posts` to retrieve values from a self-defined array
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 5,
columns: {
id: f.int({
minValue: 10000,
maxValue: 20000,
isUnique: true,
}),
}
},
posts: {
count: 100,
columns: {
description: f.valuesFromArray({
values: [
"The sun set behind the mountains, painting the sky in hues of orange and purple",
"I can't believe how good this homemade pizza turned out!",
"Sometimes, all you need is a good book and a quiet corner.",
"Who else thinks rainy days are perfect for binge-watching old movies?",
"Tried a new hiking trail today and found the most amazing waterfall!",
// ...
],
})
}
}
}));
}
main();
```
There are many more possibilities that we will define in these docs, but for now, you can explore a few sections in this documentation. Check the [Generators](/docs/mssql/seed-functions) section to get familiar with all the available generator functions you can use.
A particularly great feature is the ability to use weighted randomization, both for generator values created for a column and for determining the number of related entities that can be generated by `drizzle-seed`.
Please check [Weighted Random docs](#weighted-random) for more info.
## Weighted Random
There may be cases where you need to use multiple datasets with a different priority that should be inserted into your database during the seed stage. For such cases, drizzle-seed provides an API called weighted random
The Drizzle Seed package has a few places where weighted random can be used:
- Columns inside each table refinements
- The `with` property, determining the amount of related entities to be created
Let's check an example for both:
```ts filename="schema.ts"
import { mssqlTable, int, varchar, numeric } from "drizzle-orm/mssql-core";
export const orders = mssqlTable(
"orders",
{
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
quantityPerUnit: varchar().notNull(),
unitPrice: numeric().notNull(),
unitsInStock: int().notNull(),
unitsOnOrder: int().notNull(),
reorderLevel: int().notNull(),
discontinued: int().notNull(),
}
);
export const details = mssqlTable(
"details",
{
unitPrice: numeric().notNull(),
quantity: int().notNull(),
discount: numeric().notNull(),
orderId: int()
.notNull()
.references(() => orders.id, { onDelete: "cascade" }),
}
);
```
**Example 1**: Refine the `unitPrice` generation logic to generate `5000` random prices, with a 30% chance of prices between 10-100 and a 70% chance of prices between 100-300
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
count: 5000,
columns: {
unitPrice: f.weightedRandom(
[
{
weight: 0.3,
value: f.int({ minValue: 10, maxValue: 100 })
},
{
weight: 0.7,
value: f.number({ minValue: 100, maxValue: 300, precision: 100 })
}
]
),
}
}
}));
}
main();
```
**Example 2**: For each order, generate 1 to 3 details with a 60% chance, 5 to 7 details with a 30% chance, and 8 to 10 details with a 10% chance
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/node-mssql";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3] },
{ weight: 0.3, count: [5, 6, 7] },
{ weight: 0.1, count: [8, 9, 10] },
]
}
}
}));
}
main();
```
## Complex example
```ts
import { seed } from "drizzle-seed";
import * as schema from "./schema.ts";
const main = async () => {
const titlesOfCourtesy = ["Ms.", "Mrs.", "Dr."];
const unitsOnOrders = [0, 10, 20, 30, 50, 60, 70, 80, 100];
const reorderLevels = [0, 5, 10, 15, 20, 25, 30];
const quantityPerUnit = [
"100 - 100 g pieces",
"100 - 250 g bags",
"10 - 200 g glasses",
"10 - 4 oz boxes",
"10 - 500 g pkgs.",
"10 - 500 g pkgs."
];
const discounts = [0.05, 0.15, 0.2, 0.25];
await seed(db, schema).refine((funcs) => ({
customers: {
count: 10000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" }),
fax: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
employees: {
count: 200,
columns: {
firstName: funcs.firstName(),
lastName: funcs.lastName(),
title: funcs.jobTitle(),
titleOfCourtesy: funcs.valuesFromArray({ values: titlesOfCourtesy }),
birthDate: funcs.date({ minDate: "2010-12-31", maxDate: "2010-12-31" }),
hireDate: funcs.date({ minDate: "2010-12-31", maxDate: "2024-08-26" }),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
country: funcs.country(),
homePhone: funcs.phoneNumber({ template: "(###) ###-####" }),
extension: funcs.int({ minValue: 428, maxValue: 5467 }),
notes: funcs.loremIpsum()
}
},
orders: {
count: 50000,
columns: {
shipVia: funcs.int({ minValue: 1, maxValue: 3 }),
freight: funcs.number({ minValue: 0, maxValue: 1000, precision: 100 }),
shipName: funcs.streetAddress(),
shipCity: funcs.city(),
shipRegion: funcs.state(),
shipPostalCode: funcs.postcode(),
shipCountry: funcs.country()
},
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3, 4] },
{ weight: 0.2, count: [5, 6, 7, 8, 9, 10] },
{ weight: 0.15, count: [11, 12, 13, 14, 15, 16, 17] },
{ weight: 0.05, count: [18, 19, 20, 21, 22, 23, 24, 25] },
]
}
},
suppliers: {
count: 1000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
products: {
count: 5000,
columns: {
name: funcs.companyName(),
quantityPerUnit: funcs.valuesFromArray({ values: quantityPerUnit }),
unitPrice: funcs.weightedRandom(
[
{
weight: 0.5,
value: funcs.int({ minValue: 3, maxValue: 300 })
},
{
weight: 0.5,
value: funcs.number({ minValue: 3, maxValue: 300, precision: 100 })
}
]
),
unitsInStock: funcs.int({ minValue: 0, maxValue: 125 }),
unitsOnOrder: funcs.valuesFromArray({ values: unitsOnOrders }),
reorderLevel: funcs.valuesFromArray({ values: reorderLevels }),
discontinued: funcs.int({ minValue: 0, maxValue: 1 })
}
},
details: {
columns: {
unitPrice: funcs.number({ minValue: 10, maxValue: 130 }),
quantity: funcs.int({ minValue: 1, maxValue: 130 }),
discount: funcs.weightedRandom(
[
{ weight: 0.5, value: funcs.valuesFromArray({ values: discounts }) },
{ weight: 0.5, value: funcs.default({ defaultValue: 0 }) }
]
)
}
}
}));
}
main();
```
```ts
import type { AnyColumn } from "drizzle-orm";
import { int, numeric, mssqlTable, text, datetime2, varchar } from "drizzle-orm/mssql-core";
export const customers = mssqlTable('customer', {
id: varchar({ length: 256 }).primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text(),
region: text(),
country: text().notNull(),
phone: text().notNull(),
fax: text(),
});
export const employees = mssqlTable(
'employee',
{
id: int().primaryKey().identity(),
lastName: text().notNull(),
firstName: text(),
title: text().notNull(),
titleOfCourtesy: text().notNull(),
birthDate: datetime2().notNull(),
hireDate: datetime2().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text().notNull(),
country: text().notNull(),
homePhone: text().notNull(),
extension: int().notNull(),
notes: text().notNull(),
reportsTo: int().references((): AnyColumn => employees.id),
photoPath: text(),
},
);
export const orders = mssqlTable('order', {
id: int().primaryKey().identity(),
orderDate: datetime2().notNull(),
requiredDate: datetime2().notNull(),
shippedDate: datetime2(),
shipVia: int().notNull(),
freight: numeric().notNull(),
shipName: text().notNull(),
shipCity: text().notNull(),
shipRegion: text(),
shipPostalCode: text(),
shipCountry: text().notNull(),
customerId: text().notNull().references(() => customers.id, { onDelete: 'cascade' }),
employeeId: int().notNull().references(() => employees.id, { onDelete: 'cascade' }),
});
export const suppliers = mssqlTable('supplier', {
id: int().primaryKey().identity(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
region: text(),
postalCode: text().notNull(),
country: text().notNull(),
phone: text().notNull(),
});
export const products = mssqlTable('product', {
id: int().primaryKey().identity(),
name: varchar({ length: 255 }).notNull(),
quantityPerUnit: text().notNull(),
unitPrice: numeric().notNull(),
unitsInStock: int().notNull(),
unitsOnOrder: int().notNull(),
reorderLevel: int().notNull(),
discontinued: int().notNull(),
supplierId: int().notNull().references(() => suppliers.id, { onDelete: 'cascade' }),
});
export const details = mssqlTable('order_detail', {
unitPrice: numeric().notNull(),
quantity: int().notNull(),
discount: numeric().notNull(),
orderId: int().notNull().references(() => orders.id, { onDelete: 'cascade' }),
productId: int().notNull().references(() => products.id, { onDelete: 'cascade' }),
});
```
## Limitations
#### Types limitations for `with`
Due to certain TypeScript limitations and the current API in Drizzle, it is not possible to properly infer references between tables, especially when circular dependencies between tables exist.
This means the `with` option will display all tables in the schema, and you will need to manually select the one that has a one-to-many relationship
The `with` option works for one-to-many relationships. For example, if you have one `user` and many `posts`, you can use users `with` posts, but you cannot use posts `with` users
#### Type limitations for the third parameter in Drizzle tables:
Currently, we do not have type support for the third parameter in Drizzle tables. While it will work at runtime, it will not function correctly at the type level
Source: https://orm.drizzle.team/docs/mssql/seed-versioning
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
import Callout from "@mdx/Callout.astro";
import TableWrapper from "@mdx/TableWrapper.astro";
# Versioning
`drizzle-seed` uses versioning to manage outputs for static and dynamic data. To ensure true
determinism, ensure that values remain unchanged when using the same `seed` number. If changes are made to
static data sources or dynamic data generation logic, the version will be updated, allowing
you to choose between sticking with the previous version or using the latest.
You can upgrade to the latest `drizzle-seed` version for new features, such as additional
generators, while maintaining deterministic outputs with a previous version if needed. This
is particularly useful when you need to rely on existing deterministic data while accessing new functionality.
```ts
await seed(db, schema, { version: '2' });
```
## History
| api version | npm version | Changed generators |
| :-------------- | :-------------- | :------------- |
| `v1` | `0.1.1` | |
| `v2` | `0.2.1` |`string()`, `interval({ isUnique: true })` |
| `v3` | `0.4.0` |`hash generating function` |
| `v4 (LTS) ` | `1.0.0-beta.8` |`uuid` |
> This is not an actual API change; it is just an example of how we will proceed with `drizzle-seed` versioning.
For example, `lastName` generator was changed, and new version, `V2`, of this generator became available.
Later, `firstName` generator was changed, making `V3` version of this generator available.
| | `V1` | `V2` | `V3(latest)` |
| :--------------: | :--------------: | :-------------: | :--------------: |
| **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | |
| **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` |
##### Use the `firstName` generator of version 3 and the `lastName` generator of version 2
```ts
await seed(db, schema);
```
If you are not ready to use latest generator version right away, you can specify max version to use
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 2
```ts
await seed(db, schema, { version: '2' });
```
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 1.
```ts
await seed(db, schema, { version: '1' });
```
## Version 2
#### Unique `interval` generator was changed
An older version of the generator could produce intervals like `1 minute 60 seconds` and `2 minutes 0 seconds`, treating them as distinct intervals.
However, when the `1 minute 60 seconds` interval is inserted into a MSSQL database, it is automatically converted to `2 minutes 0 seconds`. As a result, attempting to insert the `2 minutes 0 seconds` interval into a unique column afterwards will cause an error
You will be affected, if you use the unique `interval` generator in your seeding script, as shown in the script below:
```ts
import { binary, char, mssqlTable, text, varbinary, varchar } from 'drizzle-orm/mssql-core';
import { drizzle } from 'drizzle-orm/node-mssql';
import { seed } from "drizzle-seed";
const intervals = mssqlTable('intervals', {
interval1: char({ length: 255 }).unique(),
interval2: char({ length: 255 }),
interval3: varchar({ length: 255 }).unique(),
interval4: varchar({ length: 255 }),
interval5: binary({ length: 255 }).unique(),
interval6: binary({ length: 255 }),
interval7: varbinary({ length: 255 }).unique(),
interval8: varbinary({ length: 255 }),
interval9: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals }, { version: '2' }).refine((f) => ({
intervals: {
columns: {
interval: f.interval({ isUnique: true }),
interval1: f.interval({ isUnique: true }),
interval2: f.interval({ isUnique: true }),
interval3: f.interval({ isUnique: true }),
interval4: f.interval({ isUnique: true }),
interval5: f.interval({ isUnique: true }),
interval6: f.interval({ isUnique: true }),
interval7: f.interval({ isUnique: true }),
interval8: f.interval({ isUnique: true }),
interval9: f.interval({ isUnique: true }),
},
},
}));
}
main();
```
#### `string` generators were changed: both non-unique and unique
Ability to generate a unique string based on the length of the text column (e.g., `varchar(20)`)
You will be affected, if your table includes a column of a text-like type with a maximum length parameter or a unique column of a text-like type:
```ts
import { binary, char, mssqlTable, varbinary, varchar } from 'drizzle-orm/mssql-core';
import { drizzle } from 'drizzle-orm/node-mssql';
import { seed } from "drizzle-seed";
const strings = mssqlTable('strings', {
string1: char({ length: 255 }).unique(),
string2: char({ length: 255 }),
string3: varchar({ length: 255 }).unique(),
string4: varchar({ length: 255 }),
string5: binary({ length: 255 }).unique(),
string6: binary({ length: 255 }),
string7: varbinary({ length: 255 }).unique(),
string8: varbinary({ length: 255 }),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings });
}
main();
```
You will be affected, if you use the `string` generator in your seeding script, as shown in the script below:
```ts
import { binary, char, mssqlTable, text, varbinary, varchar } from 'drizzle-orm/mssql-core';
import { drizzle } from 'drizzle-orm/node-mssql';
import { seed } from "drizzle-seed";
const strings = mssqlTable('strings', {
string1: char({ length: 255 }).unique(),
string2: char({ length: 255 }),
string3: char({ length: 255 }),
string4: varchar({ length: 255 }).unique(),
string5: varchar({ length: 255 }),
string6: varchar({ length: 255 }),
string7: binary({ length: 255 }).unique(),
string8: binary({ length: 255 }),
string9: binary({ length: 255 }),
string10: varbinary({ length: 255 }).unique(),
string11: varbinary({ length: 255 }),
string12: varbinary({ length: 255 }),
string13: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings }).refine((f) => ({
strings: {
columns: {
string1: f.string({ isUnique: true }),
string2: f.string({ isUnique: true }),
string3: f.string(),
string4: f.string({ isUnique: true }),
string5: f.string({ isUnique: true }),
string6: f.string(),
string7: f.string({ isUnique: true }),
string8: f.string({ isUnique: true }),
string9: f.string(),
string10: f.string({ isUnique: true }),
string11: f.string({ isUnique: true }),
string12: f.string(),
string13: f.string({ isUnique: true }),
},
},
}));
}
main();
```
## Version 3
#### `hash generating function was changed`
The previous version of the hash generating function generated different hashes depending on whether Bun or Node.js was used, and hashes also varied across versions of Node.js.
The new hash generating function will generate the same hash regardless of the version of Node.js or Bun, resulting in deterministic data generation across all versions.
All generators will output different values compared to the previous version, even with the same seed number.
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '3' });
```
**Switch to the old version**
The previous version of hash generating function is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 hash generating function:
```ts
await seed(db, schema, { version: '2' });
```
## Version 4
#### `uuid` generator was changed
UUID values generated by the old version of the `uuid` generator fail Zodâs v4 UUID validation.
example
```ts
import { createSelectSchema } from 'drizzle-zod';
import { seed } from 'drizzle-seed';
await seed(db, { uuidTest: schema.uuidTest }, { count: 1 }).refine((funcs) => ({
uuidTest: {
columns: {
col1: funcs.uuid()
}
}
})
);
const uuidSelectSchema = createSelectSchema(schema.uuidTest);
const res = await db.select().from(schema.uuidTest);
// the line below will throw an error when using old version of uuid generator
uuidSelectSchema.parse(res[0]);
```
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '4' });
```
**Switch to the old version**
The previous version of `uuid` generator is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '2' });
```
To use the v3 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '3' });
```
Source: https://orm.drizzle.team/docs/mssql/select
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# SQL Select
Drizzle provides you the most SQL-like way to fetch data from your database, while remaining type-safe and composable.
It natively supports mostly every query feature and capability of every dialect,
and whatever it doesn't support yet, can be added by the user with the powerful [sql](/docs/mssql/sql) operator.
For the following examples, let's assume you have a `users` table defined like this:
```typescript
import { mssqlTable, text, int } from 'drizzle-orm/mssql-core';
export const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int(),
});
```
### Basic select
Select all rows from a table including all columns:
```typescript
const result = await db.select().from(users);
/*
{
id: number;
name: string;
age: number | null;
}[]
*/
```
```sql
select [id], [name], [age] from [users];
```
Notice that the result type is inferred automatically based on the table definition, including columns nullability.
Drizzle always explicitly lists columns in the `select` clause instead of using `select *`.
This is required internally to guarantee the fields order in the query result, and is also generally considered a good practice.
### Partial select
In some cases, you might want to select only a subset of columns from a table.
You can do that by providing a selection object to the `.select()` method:
```typescript copy
const result = await db.select({
field1: users.id,
field2: users.name,
}).from(users);
const { field1, field2 } = result[0];
```
```sql
select [id], [name] from [users];
```
Like in SQL, you can use arbitrary expressions as selection fields, not just table columns:
```typescript
const result = await db.select({
id: users.id,
lowerName: sql`lower(${users.name})`,
}).from(users);
```
```sql
select [id], lower([name]) from [users];
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `string`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a string), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/mssql/sql#sqlmapwith) method.
### Conditional select
You can have a dynamic selection object based on some condition:
```typescript
async function selectUsers(withName: boolean) {
return db
.select({
id: users.id,
...(withName ? { name: users.name } : {}),
})
.from(users);
}
const result = await selectUsers(true);
```
### Distinct select
You can use `.selectDistinct()` instead of `.select()` to retrieve only unique rows from a dataset:
```ts
await db.selectDistinct().from(users).orderBy(users.id, users.name);
await db.selectDistinct({ id: users.id }).from(users).orderBy(users.id);
```
```sql
select distinct [id], [name] from [users] order by [id], [name];
select distinct [id] from [users] order by [id];
```
### Advanced select
Powered by TypeScript, Drizzle APIs let you build your select queries in a variety of flexible ways.
Sneak peek of advanced partial select, for more detailed advanced usage examples - see our [dedicated guide](/docs/mssql/guides/include-or-exclude-columns).
```ts
import { getColumns, sql } from 'drizzle-orm';
await db.select({
...getColumns(posts),
titleLength: sql`length(${posts.title})`,
}).from(posts);
```
```ts
import { getColumns } from 'drizzle-orm';
const { content, ...rest } = getColumns(posts); // exclude "content" column
await db.select({ ...rest }).from(posts); // select all other columns
```
```ts
await db._query.posts.findMany({
columns: {
title: true,
},
});
```
```ts
await db._query.posts.findMany({
columns: {
content: false,
},
});
```
## ---
### Filters
You can filter the query results using the [filter operators](/docs/mssql/operators) in the `.where()` method:
```typescript copy
import { eq, lt, gte, ne } from 'drizzle-orm';
await db.select().from(users).where(eq(users.id, 42));
await db.select().from(users).where(lt(users.id, 42));
await db.select().from(users).where(gte(users.id, 42));
await db.select().from(users).where(ne(users.id, 42));
...
```
```sql
select [id], [name], [age] from [users] where [users].[id] = 42;
select [id], [name], [age] from [users] where [users].[id] < 42;
select [id], [name], [age] from [users] where [users].[id] >= 42;
select [id], [name], [age] from [users] where [users].[id] <> 42;
```
All filter operators are implemented using the [`sql`](/docs/mssql/sql) function.
You can use it yourself to write arbitrary SQL filters, or build your own operators.
For inspiration, you can check how the operators provided by Drizzle are [implemented](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/sql/expressions/conditions.ts).
```typescript copy
import { sql } from 'drizzle-orm';
import type { MsSqlColumn } from 'drizzle-orm/mssql-core';
function equals42(col: MsSqlColumn) {
return sql`${col} = 42`;
}
await db.select().from(users).where(sql`${users.id} < 42`);
await db.select().from(users).where(sql`${users.id} = 42`);
await db.select().from(users).where(equals42(users.id));
await db.select().from(users).where(sql`${users.id} >= 42`);
await db.select().from(users).where(sql`${users.id} <> 42`);
await db.select().from(users).where(sql`lower(${users.name}) = 'aaron'`);
```
```sql
select [id], [name], [age] from [users] where [users].[id] < 42;
select [id], [name], [age] from [users] where [users].[id] = 42;
select [id], [name], [age] from [users] where [users].[id] = 42;
select [id], [name], [age] from [users] where [users].[id] >= 42;
select [id], [name], [age] from [users] where [users].[id] <> 42;
select [id], [name], [age] from [users] where lower([users].[name]) = 'aaron';
```
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select [id], [name], [age] from [users] where [users].[id] = @par0; -- params: [42]
```
Inverting condition with a `not` operator:
```typescript copy
import { eq, not, sql } from 'drizzle-orm';
await db.select().from(users).where(not(eq(users.id, 42)));
await db.select().from(users).where(sql`not ${users.id} = 42`);
```
```sql
select [id], [name], [age] from [users] where not ([users].[id] = 42);
select [id], [name], [age] from [users] where not [users].[id] = 42;
```
You can safely alter schema, rename tables and columns
and it will be automatically reflected in your queries because of template interpolation,
as opposed to hardcoding column or table names when writing raw SQL.
### Combining filters
You can logically combine filter operators with `and()` and `or()` operators:
```typescript copy
import { eq, and, sql } from 'drizzle-orm';
await db.select().from(users).where(
and(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 and ${users.name} = 'Dan'`);
```
```sql
select [id], [name], [age] from [users] where (([users].[id] = 42) and ([users].[name] = 'Dan'));
select [id], [name], [age] from [users] where [users].[id] = 42 and [users].[name] = 'Dan';
```
```typescript copy
import { eq, or, sql } from 'drizzle-orm';
await db.select().from(users).where(
or(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 or ${users.name} = 'Dan'`);
```
```sql
select [id], [name], [age] from [users] where (([users].[id] = 42) or ([users].[name] = 'Dan'));
select [id], [name], [age] from [users] where [users].[id] = 42 or [users].[name] = 'Dan';
```
### Advanced filters
In combination with TypeScript, Drizzle APIs provide you powerful and flexible ways to combine filters in queries.
Sneak peek of conditional filtering, for more detailed advanced usage examples - see our [dedicated guide](/docs/mssql/guides/conditional-filters-in-query).
```ts
const searchPosts = async (term?: string) => {
await db
.select()
.from(posts)
.where(term ? like(posts.title, term) : undefined);
};
await searchPosts();
await searchPosts('AI');
```
```ts
const searchPosts = async (filters: SQL[]) => {
await db
.select()
.from(posts)
.where(and(...filters));
};
const filters: SQL[] = [];
filters.push(like(posts.title, 'AI'));
filters.push(inArray(posts.category, ['Tech', 'Art', 'Science']));
filters.push(gt(posts.views, 200));
await searchPosts(filters);
```
## ---
### Fetch & offset
In MSSQL, `FETCH` and `OFFSET` are part of the `ORDER BY` clause, so they can only be used after the `.orderBy()` function
```ts
await db.select().from(users).orderBy(asc(users.id)).offset(5);
await db.select().from(users).orderBy(asc(users.id)).offset(5).fetch(10);
```
```sql
select [id], [name], [age] from [users] offset 5 rows;
select [id], [name], [age] from [users] offset 5 rows fetch next 10 rows;
```
### Top
Limits the rows returned in a query result set to a specified number of rows
```ts
await db.select().top(10).from(users);
```
```sql
select top (10) [id], [name], [age] from [users];
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.select().from(users).orderBy(users.name);
await db.select().from(users).orderBy(desc(users.name));
// order by multiple fields
await db.select().from(users).orderBy(users.name, users.name2);
await db.select().from(users).orderBy(asc(users.name), desc(users.name2));
```
```sql
select [id], [name], [name2], [age] from [users] order by [users].[name];
select [id], [name], [name2], [age] from [users] order by [users].[name] desc;
select [id], [name], [name2], [age] from [users] order by [users].[name], [users].[name];
select [id], [name], [name2], [age] from [users] order by [users].[name] asc, [users].[name2] desc;
```
### Advanced pagination
Powered by TypeScript, Drizzle APIs let you implement all possible SQL pagination and sorting approaches.
Sneak peek of advanced pagination, for more detailed advanced
usage examples - see our dedicated [limit offset pagination](/docs/mssql/guides/limit-offset-pagination)
and [cursor pagination](/docs/mssql/guides/cursor-based-pagination) guides.
```ts
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.offset(4) // the number of rows to skip
.fetch(4) // the number of rows to fetch
```
```ts
const getUsers = async (page = 1, pageSize = 3) => {
return await db._query.users.findMany({
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
offset: (page - 1) * pageSize,
});
};
await getUsers();
```
```ts
const getUsers = async (page = 1, pageSize = 10) => {
const sq = db
.select({ id: users.id })
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.offset(pageSize) // the number of rows to skip
.fetch((page - 1) * pageSize) // the number of rows to fetch
.as('subquery');
return await db.select().from(users).innerJoin(sq, eq(users.id, sq.id)).orderBy(users.id);
};
```
```ts
const nextUserPage = async (cursor?: number, pageSize = 3) => {
return await db
.select()
.top(pageSize)
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined) // if cursor is provided, get rows after it
.orderBy(asc(users.id)); // ordering
};
// pass the cursor of the last row of the previous page (id)
await nextUserPage(3);
```
## ---
### WITH 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 sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
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];
```
To select arbitrary SQL values as fields in a CTE and reference them in other CTEs or in the main query,
you need to add aliases to them:
```typescript copy
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);
```
If you don't provide an alias, the field type will become `DrizzleTypeError` and you won't be able to reference it in other queries.
If you ignore the type error and still try to use the field,
you will get a runtime error, since there's no way to reference that field without an alias.
### Select from subquery
Just like in SQL, you can embed queries into other queries by using the subquery API:
```typescript copy
const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');
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 be used in any place where a table can be used, for example in joins:
```typescript copy
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));
```
```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];
```
## ---
### Aggregations
With Drizzle, you can do aggregations using functions like `sum`, `count`, `avg`, etc. by
grouping and filtering with `.groupBy()` and `.having()` respectfully, same as you would do in raw SQL:
```typescript
import { gt, sql } from "drizzle-orm";
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as int)`,
})
.from(users)
.groupBy(users.age);
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as int)`,
})
.from(users)
.groupBy(users.age)
.having(({ count }) => gt(count, 1));
```
```sql
select [age], cast(count([id]) as int)
from [users]
group by [users].[age];
select [age], cast(count([id]) as int)
from [users]
group by [users].[age]
having cast(count([users].[id]) as int) > 1;
```
As an alternative to `cast(... as int)`, you can use [`.mapWith(Number)`](/docs/mssql/sql#sqlmapwith) to cast the value to a number at runtime.
If you need count aggregation - we recommend using our [$count](/docs/mssql/select#count) API
### Aggregations helpers
Drizzle has a set of wrapped `sql` functions, so you don't need to write
`sql` templates for common cases in your app
Remember, aggregation functions are often used with the GROUP BY clause of the SELECT statement.
So if you are selecting using aggregating functions and other columns in one query,
be sure to use the `.groupBy` clause
**count**
Returns the number of values in `expression`.
```ts
import { count } from 'drizzle-orm'
await db.select({ value: count() }).from(users);
await db.select({ value: count(users.id) }).from(users);
```
```sql
select count(*) from [users];
select count([id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(*)`.mapWith(Number)
}).from(users);
await db.select({
value: sql`count(${users.id})`.mapWith(Number)
}).from(users);
```
**countDistinct**
Returns the number of non-duplicate values in `expression`.
```ts
import { countDistinct } from 'drizzle-orm'
await db.select({ value: countDistinct(users.id) }).from(users);
```
```sql
select count(distinct [id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(distinct ${users.id})`.mapWith(Number)
}).from(users);
```
**avg**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avg } from 'drizzle-orm'
await db.select({ value: avg(users.id) }).from(users);
```
```sql
select avg([id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(${users.id})`.mapWith(String)
}).from(users);
```
**avgDistinct**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avgDistinct } from 'drizzle-orm'
await db.select({ value: avgDistinct(users.id) }).from(users);
```
```sql
select avg(distinct [id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**sum**
Returns the sum of all non-null values in `expression`.
```ts
import { sum } from 'drizzle-orm'
await db.select({ value: sum(users.id) }).from(users);
```
```sql
select sum([id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(${users.id})`.mapWith(String)
}).from(users);
```
**sumDistinct**
Returns the sum of all non-null and non-duplicate values in `expression`.
```ts
import { sumDistinct } from 'drizzle-orm'
await db.select({ value: sumDistinct(users.id) }).from(users);
```
```sql
select sum(distinct [id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**max**
Returns the maximum value in `expression`.
```ts
import { max } from 'drizzle-orm'
await db.select({ value: max(users.id) }).from(users);
```
```sql
select max([id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`max(${expression})`.mapWith(users.id)
}).from(users);
```
**min**
Returns the minimum value in `expression`.
```ts
import { min } from 'drizzle-orm'
await db.select({ value: min(users.id) }).from(users);
```
```sql
select min([id]) from [users];
```
```ts
// It's equivalent to writing
await db.select({
value: sql`min(${users.id})`.mapWith(users.id)
}).from(users);
```
A more advanced example:
```typescript copy
const orders = mssqlTable('order', {
id: int('id').primaryKey(),
orderDate: datetime2('order_date').notNull(),
requiredDate: datetime2('required_date').notNull(),
shippedDate: datetime2('shipped_date'),
shipVia: int('ship_via').notNull(),
freight: numeric('freight').notNull(),
shipName: nvarchar('ship_name', { length: 256 }).notNull(),
shipCity: nvarchar('ship_city', { length: 256 }).notNull(),
shipRegion: nvarchar('ship_region', { length: 256 }),
shipPostalCode: nvarchar('ship_postal_code', { length: 256 }),
shipCountry: nvarchar('ship_country', { length: 256 }).notNull(),
customerId: nvarchar('customer_id', { length: 256 }).notNull(),
employeeId: int('employee_id').notNull(),
});
const details = mssqlTable('order_detail', {
unitPrice: numeric('unit_price').notNull(),
quantity: int('quantity').notNull(),
discount: numeric('discount').notNull(),
orderId: int('order_id').notNull(),
productId: int('product_id').notNull(),
});
await db.select({
id: orders.id,
shippedDate: orders.shippedDate,
shipName: orders.shipName,
shipCity: orders.shipCity,
shipCountry: orders.shipCountry,
productsCount: sql`cast(count(${details.productId}) as int)`,
quantitySum: sql`sum(${details.quantity})`,
totalPrice: sql`sum(${details.quantity} * ${details.unitPrice})`,
})
.from(orders)
.leftJoin(details, eq(orders.id, details.orderId))
.groupBy(
orders.id,
orders.shipName,
orders.shippedDate,
orders.shipCity,
orders.shipCountry,
)
.orderBy(asc(orders.id));
```
### $count
Currently not supported
## ---
### Iterator
If you need to return a very large amount of rows from a query and you don't want to load them all into memory, you can use `.iterator()` to convert the query into an async iterator:
```ts copy
const iterator = db.select().from(users).iterator();
for await (const row of iterator) {
console.log(row);
}
```
It also works with prepared statements:
```ts copy
const query = db.select().from(users).prepare();
const iterator = query.iterator();
for await (const row of iterator) {
console.log(row);
}
```
Source: https://orm.drizzle.team/docs/mssql/set-operations
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Set Operations
SQL set operations combine the results of multiple query blocks into a single result.
The SQL standard defines the following three set operations: `UNION`, `INTERSECT`, `EXCEPT`, `UNION ALL`.
### Union
Combine all results from two query blocks into a single result, omitting any duplicates.
Get all names from customers and users tables without duplicates.
```typescript copy
import { union } from 'drizzle-orm/mssql-core'
import { users, customers } from './schema'
const allNamesForUserQuery = db.select({ name: users.name }).from(users);
const result = await union(
allNamesForUserQuery,
db.select({ name: customers.name }).from(customers)
);
```
```sql
(select [name] from [sellers])
union
(select [name] from [customers])
```
```ts copy
import { users, customers } from './schema'
const result = await db
.select({ name: users.name })
.from(users)
.union(db.select({ name: customers.name }).from(customers));
```
```sql
(select [name] from [sellers])
union
(select [name] from [customers])
```
```typescript copy
import { int, mssqlTable, text, varchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable('sellers', {
id: int().primaryKey(),
name: varchar({ length: 256 }).notNull(),
address: text(),
});
export const customers = mssqlTable('customers', {
id: int().primaryKey(),
name: varchar({ length: 256 }).notNull(),
city: text(),
email: varchar({ length: 256 }).notNull()
});
```
### Union All
Combine all results from two query blocks into a single result, with duplicates.
Let's consider a scenario where you have two tables, one representing online sales and the other
representing in-store sales. In this case, you want to combine the data from both tables into a
single result set. Since there might be duplicate transactions,
you want to keep all the records and not eliminate duplicates.
```typescript copy
import { unionAll } from 'drizzle-orm/mssql-core'
import { onlineSales, inStoreSales } from './schema'
const onlineTransactions = db.select({ transaction: onlineSales.transactionId }).from(onlineSales);
const inStoreTransactions = db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales);
const result = await unionAll(onlineTransactions, inStoreTransactions);
```
```sql
(select [transaction_id] from [online_sales])
union all
(select [transaction_id] from [in_store_sales])
```
```ts copy
import { onlineSales, inStoreSales } from './schema'
const result = await db
.select({ transaction: onlineSales.transactionId })
.from(onlineSales)
.unionAll(
db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales)
);
```
```sql
(select [transaction_id] from [online_sales])
union all
(select [transaction_id] from [in_store_sales])
```
```typescript copy
import { int, mssqlTable, text, datetime2, varchar } from "drizzle-orm/mssql-core";
export const onlineSales = mssqlTable('online_sales', {
transactionId: int('transaction_id').primaryKey(),
productId: int('product_id').unique(),
quantitySold: int('quantity_sold'),
saleDate: datetime2('sale_date', { mode: 'date' }),
});
export const inStoreSales = mssqlTable('in_store_sales', {
transactionId: int('transaction_id').primaryKey(),
productId: int('product_id').unique(),
quantitySold: int('quantity_sold'),
saleDate: datetime2('sale_date', { mode: 'date' }),
});
```
### Intersect
Combine only those rows which the results of two query blocks have in common, omitting any duplicates.
Suppose you have two tables that store information about students' course enrollments.
You want to find the courses that are common between two different departments,
but you want distinct course names, and you're not interested in counting multiple
enrollments of the same course by the same student.
In this scenario, you want to find courses that are common between the two departments but don't want
to count the same course multiple times even if multiple students from the same department are enrolled in it.
```typescript copy
import { intersect } from 'drizzle-orm/mssql-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.courseName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.courseName }).from(depB);
const result = await intersect(departmentACourses, departmentBCourses);
```
```sql
(select [projects_name] from [department_a_projects])
intersect
(select [projects_name] from [department_b_projects])
```
```typescript copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.courseName })
.from(depA)
.intersect(db.select({ courseName: depB.courseName }).from(depB));
```
```sql
(select [projects_name] from [department_a_projects])
intersect
(select [projects_name] from [department_b_projects])
```
```typescript copy
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const depA = mssqlTable('department_a_courses', {
studentId: int('student_id'),
courseName: varchar('course_name', { length: 256 }).notNull(),
});
export const depB = mssqlTable('department_b_courses', {
studentId: int('student_id'),
courseName: varchar('course_name', { length: 256 }).notNull(),
});
```
### Except
For two query blocks A and B, return all results from A which are not also present in B, omitting any duplicates.
Suppose you have two tables that store information about employees' project assignments.
You want to find the projects that are unique to one department
and not shared with another department, excluding duplicates.
In this scenario, you want to identify the projects that are exclusive to one department and
not shared with the other department. You don't want to count the same project
multiple times, even if multiple employees from the same department are assigned to it.
```typescript copy
import { except } from 'drizzle-orm/mssql-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.projectsName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.projectsName }).from(depB);
const result = await except(departmentACourses, departmentBCourses);
```
```sql
(select [projects_name] from [department_a_projects])
except
(select [projects_name] from [department_b_projects])
```
```ts copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.projectsName })
.from(depA)
.except(db.select({ courseName: depB.projectsName }).from(depB));
```
```sql
(select [projects_name] from [department_a_projects])
except
(select [projects_name] from [department_b_projects])
```
```typescript
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const depA = mssqlTable('department_a_projects', {
employeeId: int('employee_id'),
projectsName: varchar('projects_name', { length: 256 }).notNull(),
});
export const depB = mssqlTable('department_b_projects', {
employeeId: int('employee_id'),
projectsName: varchar('projects_name', { length: 256 }).notNull(),
});
```
Source: https://orm.drizzle.team/docs/mssql/sql-schema-declaration
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle schema
Drizzle lets you define a schema in TypeScript with various models and properties supported by the underlying database.
When you define your schema, it serves as the source of truth for future modifications in queries (using Drizzle-ORM)
and migrations (using Drizzle-Kit).
If you are using Drizzle-Kit for the migration process, make sure to export all the models defined in your schema files so that Drizzle-Kit can import them and use them in the migration diff process.
```ts
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const usersTable = mssqlTable("users", {
id: int().primaryKey().identity(),
name: varchar({ length: 256 }).notNull(),
age: int().notNull(),
email: varchar({ length: 256 }).notNull().unique(),
});
```
```ts
import { mssqlTable } from "drizzle-orm/mssql-core";
export const usersTable = mssqlTable("users", (t) => ({
id: t.int().primaryKey().identity(),
name: t.varchar({ length: 256 }).notNull(),
age: t.int().notNull(),
email: t.varchar({ length: 256 }).notNull().unique(),
}));
```
```ts
import * as t from "drizzle-orm/mssql-core";
export const usersTable = t.mssqlTable("users", {
id: t.int().primaryKey().identity(),
name: t.varchar({ length: 256 }).notNull(),
age: t.int().notNull(),
email: t.varchar({ length: 256 }).notNull().unique(),
});
```
## Organize your schema files
You can declare your SQL schema directly in TypeScript either in a single `schema.ts` file,
or you can spread them around â whichever you prefer, all the freedom!
#### Schema in 1 file
The most common way to declare your schema with Drizzle is to put all your tables into one `schema.ts` file.
> Note: You can name your schema file whatever you like. For example, it could be `models.ts`, or something else.
This approach works well if you don't have too many table models defined, or if you're okay with keeping them all in one file
Example:
```plaintext
đĻ
â đ src
â đ db
â đ schema.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema file. With this configuration, Drizzle will
read from the `schema.ts` file and use this information during the migration generation process. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/mssql/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'mssql',
schema: './src/db/schema.ts'
})
```
#### Schema in multiple files
You can place your Drizzle models â such as tables, enums, sequences, etc. â not only in one file but in any file you prefer.
The only thing you must ensure is that you export all the models from those files so that the Drizzle kit can import
them and use them in migrations.
One use case would be to separate each table into its own file.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ countries.ts
â đ cities.ts
â đ products.ts
â đ clients.ts
â đ etc.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema folder. With this configuration, Drizzle will
read from the `schema` folder and find all the files recursively and get all the drizzle tables from there. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/mssql/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'mssql',
schema: './src/db/schema'
})
```
You can also group them in any way you like, such as creating groups for user-related tables, messaging-related tables, product-related tables, etc.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ messaging.ts
â đ products.ts
```
## Shape your data schema
Drizzle schema consists of several model types from database you are using. With drizzle you can specify:
- Tables with columns, constraints, etc.
- Schemas
- Views
- etc.
Let's go one by one and check how the schema should be defined with drizzle
#### **Tables and columns declaration**
A table in Drizzle should be defined with at least 1 column, the same as it should be done in database. There is one important thing to know,
there is no such thing as a common table object in Drizzle. You need to choose the MSSQL table object.

```ts copy
import { mssqlTable, int } from "drizzle-orm/mssql-core"
export const users = mssqlTable('users', {
id: int()
});
```
By default, Drizzle will use the TypeScript key names for columns in database queries.
Therefore, the schema and query from the example will generate the SQL query shown below
This example uses a db object, whose initialization is not covered in this part of the documentation. To learn how to connect to the database, please refer to the [Connections Docs](/docs/mssql/get-started-mssql)
\
**TypeScript key = database key**
```ts
// schema.ts
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable('users', {
id: int(),
first_name: varchar({ length: 256 })
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT [id], [first_name] FROM [users];
```
If you want to use different names in your TypeScript code and in the database, you can use column aliases
```ts
// schema.ts
import { int, mssqlTable, varchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable('users', {
id: int(),
firstName: varchar('first_name', { length: 256 })
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT [id], [first_name] FROM [users];
```
### Camel and Snake casing
Database model names often use `snake_case` conventions, while in TypeScript, it is common to use `camelCase` for naming models.
This can lead to a lot of alias definitions in the schema. To address this, Drizzle provides a way to automatically
map `camelCase` from TypeScript to `snake_case` in the database via a dedicated builder.
Use the `snakeCase` or `camelCase` builder from `drizzle-orm/mssql-core` to declare a table whose column keys are
automatically mapped to the chosen database naming convention
```ts {2,4,11,12}
// schema.ts
import { snakeCase, camelCase } from 'drizzle-orm/mssql-core';
export const users = snakeCase.table('users', {
id: int().primaryKey(),
fullName: text(), // â full_name in DB
createdAt: datetime2(), // â created_at in DB
});
// Available on all entity types:
snakeCase.table / snakeCase.view / snakeCase.schema
camelCase.table / camelCase.view / camelCase.schema
```
```ts
// db.ts
import { drizzle } from "drizzle-orm/node-mssql";
const db = drizzle({ connection: process.env.DATABASE_URL })
```
```ts
// query.ts
await db.select().from(users);
```
```sql
select [id], [full_name], [created_at] from [users]
```
### Advanced
There are a few tricks you can use with Drizzle ORM. As long as Drizzle is entirely in TypeScript files,
you can essentially do anything you would in a simple TypeScript project with your code.
One common feature is to separate columns into different places and then reuse them.
For example, consider the `updated_at`, `created_at`, and `deleted_at` columns. Many tables/models may need these
three fields to track and analyze the creation, deletion, and updates of entities in a system
We can define those columns in a separate file and then import and spread them across all the table objects you have
```ts
// columns.helpers.ts
export const timestamps = {
updated_at: datetime2(),
created_at: datetime2()..defaultGetDate().notNull(),
deleted_at: datetime2(),
}
```
```ts
// users.sql.ts
export const users = mssqlTable('users', {
id: int(),
...timestamps
})
```
```ts
// posts.sql.ts
export const posts = mssqlTable('posts', {
id: int(),
...timestamps
})
```
#### **Schemas**
\
In MSSQL, there is an entity called a `schema` (which we believe should be called `folders`). This creates a structure in MSSQL:

You can manage your PostgreSQL schemas with `mssqlSchema` and place any other models inside it.
Define the schema you want to manage using Drizzle
```ts
import { mssqlSchema } from "drizzle-orm/mssql-core"
export const customSchema = mssqlSchema('custom');
```
Then place the table inside the schema object
```ts {5-7}
import { int, mssqlSchema } from "drizzle-orm/mssql-core";
export const customSchema = mssqlSchema('custom');
export const users = customSchema.table('users', {
id: int()
})
```
### Example
Once you know the basics, let's define a schema example for a real project to get a better view and understanding
> All examples will use `generateUniqueString`. The implementation for it will be provided after all the schema examples
```ts copy
import type { AnyMsSqlColumn } from "drizzle-orm/mssql-core";
import { mssqlTable as table } from "drizzle-orm/mssql-core";
import * as t from "drizzle-orm/mssql-core";
export const users = table(
"users",
{
id: t.int().primaryKey().identity(),
firstName: t.varchar("first_name", { length: 256 }),
lastName: t.varchar("last_name", { length: 256 }),
email: t.varchar({ length: 256 }).notNull(),
invitee: t.int().references((): AnyMsSqlColumn => users.id),
role: t.varchar({ length: 20, enum: ["guest", "admin", "user"] }).default("guest"),
},
(table) => [
t.uniqueIndex("email_idx").on(table.email)
]
);
export const posts = table(
"posts",
{
id: t.int().primaryKey().identity(),
slug: t.varchar({ length: 256 }).$default(() => generateUniqueString(16)),
title: t.varchar({ length: 256 }),
ownerId: t.int("owner_id").references(() => users.id),
},
(table) => [
t.uniqueIndex("slug_idx").on(table.slug),
t.index("title_idx").on(table.title),
]
);
export const comments = table("comments", {
id: t.int().primaryKey().identity(),
text: t.varchar({ length: 256 }),
postId: t.int("post_id").references(() => posts.id),
ownerId: t.int("owner_id").references(() => users.id),
});
```
**`generateUniqueString` implementation:**
```ts
function generateUniqueString(length: number = 12): string {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let uniqueString = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
uniqueString += characters[randomIndex];
}
return uniqueString;
}
```
#### What's next?
Source: https://orm.drizzle.team/docs/mssql/sql
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Magical `sql` operator đĒ
When working with an ORM library, there may be cases where you find it challenging to write a
specific query using the provided ORM syntax. In such situations, you can resort to using
raw queries, which involve constructing a query as a raw string. However, raw queries often
lack the benefits of type safety and query parameterization.
To address this, many libraries have introduced the concept of an `sql` template. This template
allows you to write more type-safe and parameterized queries, enhancing the overall safety and
flexibility of your code. Drizzle, being a powerful ORM library, also supports the sql template.
With Drizzle's `sql` template, you can go even further in crafting queries. If you encounter
difficulties in writing an entire query using the library's query builder, you can selectively
use the `sql` template within specific sections of the Drizzle query. This flexibility enables you
to employ the sql template in partial SELECT statements, WHERE clauses, ORDER BY clauses, HAVING
clauses, GROUP BY clauses, and even in relational query builders.
By leveraging the capabilities of the sql template in Drizzle, you can maintain the advantages
of type safety and query parameterization while achieving the desired query structure and complexity.
This empowers you to create more robust and maintainable code within your application.
## sql`` template
One of the most common usages you may encounter in other ORMs as well
is the ability to use `sql` queries as-is for raw queries.
```typescript copy
import { sql } from 'drizzle-orm'
const id = 69;
await db.execute(sql`select * from ${usersTable} where ${usersTable.id} = ${id}`)
```
It will generate the current query
```sql
select * from [users] where [users].[id] = @par0 -- params: [69]
```
Any tables and columns provided to the sql parameter are automatically mapped to their corresponding SQL
syntax with escaped names for tables, and the escaped table names are appended to column names.
Additionally, any dynamic parameters such as `${id}` will be mapped to the @par0 placeholder,
and the corresponding values will be moved to an array of values that are passed separately to the database.
This approach effectively prevents any potential SQL Injection vulnerabilities.
## `sql`
Please note that `sql` does not perform any runtime mapping. The type you define using `sql` is
purely a helper for Drizzle. It is important to understand that there is no feasible way to
determine the exact type dynamically, as SQL queries can be highly versatile and customizable.
You can define a custom type in Drizzle to be used in places where fields require a specific type other than `unknown`.
This feature is particularly useful in partial select queries, ensuring consistent typing for selected fields:
```typescript
// without sql type defined
const response: { lowerName: unknown }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
// with sql type defined
const response: { lowerName: string }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
```
## `sql``.mapWith()`
For the cases you need to make a runtime mapping for values passed from database driver to drizzle you can use `.mapWith()`
This function accepts different values, that will map response in runtime.
You can replicate a specific column mapping strategy as long as
the interface inside mapWith is the same interface that is implemented by Column.
```typescript
import { mssqlTable, int, text } from 'drizzle-orm/mssql-core';
const usersTable = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
});
// at runtime this values will be mapped same as `text` column is mapped in drizzle
sql`...`.mapWith(usersTable.name);
```
You can also pass your own implementation for the `DriverValueDecoder` interface:
```ts
sql``.mapWith({
mapFromDriverValue: (value: any) => {
const mappedValue = value;
// mapping you want to apply
return mappedValue;
},
});
// or
sql``.mapWith(Number);
```
## `sql``.as()`
In different cases, it can sometimes be challenging to determine how to name a custom field that you want to use.
You may encounter situations where you need to explicitly specify an alias for a
field that will be selected. This can be particularly useful when dealing with complex queries.
To address these scenarios, we have introduced a helpful `.as('alias_name')` helper, which allows
you to define an alias explicitly. By utilizing this feature, you can provide a clear and meaningful
name for the field, making your queries more intuitive and readable.
```typescript
sql`lower(${usersTable.name})`.as('lower_name')
```
```sql
... [users].[name] as [lower_name] ...
```
## `sql.raw()`
There are cases where you may not need to create parameterized values from input or map tables/columns to escaped ones.
Instead, you might simply want to generate queries as they are. For such situations, we provide the `sql.raw()` function.
The `sql.raw()` function allows you to include raw SQL statements within your queries without any additional processing or escaping.
This can be useful when you have pre-constructed SQL statements or when you need to incorporate complex or dynamic
SQL code directly into your queries.
```typescript
sql.raw(`select * from users where id = ${12}`);
// vs
sql`select * from users where id = ${12}`;
```
```sql
select * from users where id = 12;
--> vs
select * from users where id = @par0; --> [12]
```
You can also utilize `sql.raw()` within the sql function, enabling you to include any raw string
without escaping it through the main `sql` template function.
By using `sql.raw()` inside the `sql` function, you can incorporate unescaped raw strings
directly into your queries. This can be particularly useful when you have specific
SQL code or expressions that should remain untouched by the template function's automatic escaping or modification.
```typescript
sql`select * from ${usersTable} where id = ${12}`;
// vs
sql`select * from ${usersTable} where id = ${sql.raw(12)}`;
```
```sql
select * from [users] where id = @par0; --> [12]
--> vs
select * from [users] where id = 12;
```
## sql.fromList()
The `sql` template generates sql chunks, which are arrays of SQL parts that will be concatenated
into the query and params after applying the SQL to the database or query in Drizzle.
In certain scenarios, you may need to aggregate these chunks into an array using custom business
logic and then concatenate them into a single SQL statement that can be passed to the database or query.
For such cases, the fromList function can be quite useful.
The fromList function allows you to combine multiple SQL chunks into a single SQL statement.
You can use it to aggregate and concatenate the individual SQL parts according to your specific
requirements and then obtain a unified SQL query that can be executed.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql` or `);
}
const finalSql: SQL = sql.fromList(sqlChunks)
```
```sql
select * from users where id = @par0 or id = @par1 or id = @par2 or id = @par3 or id = @par4; --> [0, 1, 2, 3, 4]
```
## sql.join()
Indeed, the `sql.join` function serves a similar purpose to the fromList helper.
However, it provides additional flexibility when it comes to handling spaces between
SQL chunks or specifying custom separators for concatenating the SQL chunks.
With `sql.join`, you can concatenate SQL chunks together using a specified separator.
This separator can be any string or character that you want to insert between the chunks.
This is particularly useful when you have specific requirements for formatting or delimiting
the SQL chunks. By specifying a custom separator, you can achieve the desired structure and formatting
in the final SQL query.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql`where`);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql`or`);
}
const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
```
```sql
select * from users where id = @par0 or id = @par1 or id = @par2 or id = @par3 or id = @par4; --> [0, 1, 2, 3, 4]
```
## sql.append()
If you have already generated SQL using the `sql` template, you can achieve the same behavior as `fromList`
by using the append function to directly add a new chunk to the generated SQL.
By using the append function, you can dynamically add additional SQL chunks to the existing SQL string,
effectively concatenating them together. This allows you to incorporate custom logic or business
rules for aggregating the chunks into the final SQL query.
```typescript
const finalSql = sql`select * from users`;
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = @par0 or id = @par1 or id = @par2 or id = @par3 or id = @par4; --> [0, 1, 2, 3, 4]
```
## sql.empty()
By using sql.empty(), you can start with a blank SQL object and then dynamically append SQL chunks to it as needed. This allows you to construct the SQL query incrementally, applying custom logic or conditions to determine the contents of each chunk.
Once you have initialized the SQL object using sql.empty(), you can take advantage of the full range
of sql template features such as parameterization, composition, and escaping.
This empowers you to construct the SQL query in a flexible and controlled manner,
adapting it to your specific requirements.
```typescript
const finalSql = sql.empty();
// some logic
finalSql.append(sql`select * from users`);
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = @par0 or id = @par1 or id = @par2 or id = @par3 or id = @par4; --> [0, 1, 2, 3, 4]
```
## Convert `sql` to string and params
In all the previous examples, you observed the usage of SQL template syntax in TypeScript along with
the generated SQL output.
If you need to obtain the query string and corresponding parameters generated from the SQL template,
you must specify the database dialect you intend to generate the query for. Different databases have
varying syntax for parameterization and escaping, so selecting the appropriate dialect is crucial.
Once you have chosen the dialect, you can utilize the corresponding implementation's functionality
to convert the SQL template into the desired query string and parameter format. This ensures
compatibility with the specific database system you are working with.
```typescript copy
import { MsSqlDialect } from 'drizzle-orm/mssql-core';
const mssqlDialect = new MsSqlDialect();
mssqlDialect.sqlToQuery(sql`select * from ${usersTable} where ${usersTable.id} = ${12}`);
```
```sql
select * from [users] where [users].[id] = @par0; --> [ 12 ]
```
## `sql` select
You can use the sql functionality in partial select queries as well. Partial select queries allow you to
retrieve specific fields or columns from a table rather than fetching the entire row.
For more detailed information about partial select queries, you can refer to the Core API
documentation available at **[Core API docs](/docs/mssql/select#basic-and-partial-select)**.
**Select different custom fields from table**
Here you can see a usage for **[`sql`](/docs/mssql/sql#sqlt)**, **[`sql``.mapWith()`](/docs/mssql/sql#sqlmapwith)**, **[`sql``.as()`](/docs/mssql/sql#sqlast)**.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select({
id: usersTable.id,
lowerName: sql`lower(${usersTable.name})`,
aliasedName: sql`lower(${usersTable.name})`.as('aliased_column'),
count: sql`count(*)`.mapWith(Number)
}).from(usersTable)
```
```sql
select [id], lower([name]), lower([name]) as [aliased_column], count(*) from [users];
```
## `sql` in where
Indeed, Drizzle provides a set of available expressions that you can use within the sql template.
However, it is true that databases often have a wider range of expressions available,
including those provided through extensions or other means.
To ensure flexibility and enable you to utilize any expressions that are not natively
supported by Drizzle, you have the freedom to write the SQL template
directly using the sql function. This allows you to leverage the full power of
SQL and incorporate any expressions or functionalities specific to your target database.
By using the sql template, you are not restricted to only the predefined expressions in Drizzle.
Instead, you can express complex queries and incorporate any supported expressions that
the underlying database system provides.
**Filtering by `id` but with sql**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
const id = 77
await db.select()
.from(usersTable)
.where(sql`${usersTable.id} = ${id}`)
```
```sql
select * from [users] where [users].[id] = @par0; --> [ 77 ]
```
**Advanced fulltext search where statement**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
const searchParam = "%Ale%"
await db.select()
.from(usersTable)
.where(sql`lower(${usersTable.name}) like lower(${searchPattern})`)
```
```sql
select * from [users] where lower([users].[name]) like lower(@par0); --> [ "%Ale%" ]
```
## `sql` in orderBy
The `sql` template can indeed be used in the ORDER BY clause when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select().from(usersTable).orderBy(sql`${usersTable.id} desc`)
```
```sql
select * from [users] order by [users].[id] desc;
```
## `sql` in having and groupBy
The `sql` template can indeed be used in the HAVING and GROUP BY clauses when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from './schema'
await db.select({
projectId: usersTable.projectId,
count: sql`count(${usersTable.id})`.mapWith(Number)
}).from(usersTable)
.groupBy(sql`${usersTable.projectId}`)
.having(sql`count(${usersTable.id}) > 300`)
```
```sql
select [project_id], count([id]) from [users] group by [users].[project_id] having count([users].[id]) > 300;
```
Source: https://orm.drizzle.team/docs/mssql/transactions
# Transactions
SQL transaction is a grouping of one or more SQL statements that interact with a database.
A transaction in its entirety can commit to a database as a single logical unit
or rollback (become undone) as a single logical unit.
Drizzle ORM provides APIs to run SQL statements in transactions:
```ts copy
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
Drizzle ORM supports `savepoints` with nested transactions API:
```ts copy {7-9}
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
await tx.transaction(async (tx2) => {
await tx2.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan"));
});
});
```
You can embed business logic to the transaction and rollback whenever needed:
```ts copy {7}
const db = drizzle(...)
await db.transaction(async (tx) => {
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
if (account.balance < 100) {
// This throws an exception that rollbacks the transaction.
tx.rollback()
}
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
You can return values from the transaction:
```ts copy {8}
const db = drizzle(...)
const newBalance: number = await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
return account.balance;
});
```
You can use transactions with **[relational queries](/docs/mssql/rqb)**:
```ts
const db = drizzle({ schema })
await db.transaction(async (tx) => {
await tx._query.users.findMany({
with: {
accounts: true
}
});
});
```
We provide dialect-specific transaction configuration APIs:
```ts {6}
await db.transaction(
async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, "Dan"));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, "Andrew"));
}, {
isolationLevel: "read committed",
}
);
interface MsSqlTransactionConfig {
isolationLevel?:
| "read uncommitted"
| "read committed"
| "repeatable read"
| "serializable"
| "snapshot";
}
```
Source: https://orm.drizzle.team/docs/mssql/typebox-legacy
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox-legacy
### Install the dependencies
drizzle-orm@rc @sinclair/typebox
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { mssqlView } from 'drizzle-orm/mssql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const usersView = mssqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = Value.Parse(usersViewSchema, ...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createInsertSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = Value.Parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = Value.Parse(userInsertSchema, user); // Will parse successfully
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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createUpdateSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
import { eq } from "drizzle-orm";
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = Value.Parse(userUpdateSchema, user); // Will parse successfully
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 Typebox schema will overwrite it.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Type } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
bio: text(),
preferences: text()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => Type.String({ ...schema, maxLength: 20 }), // Extends schema
bio: (schema) => Type.String({ ...schema, maxLength: 1000 }), // Extends schema before becoming nullable/optional
preferences: Type.Object({ theme: Type.String() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = Value.Parse(userSelectSchema, ...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Typebox instance**
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSchemaFactory } from 'drizzle-orm/typebox';
import { t } from 'elysia'; // Extended Typebox instance
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const { createInsertSchema } = createSchemaFactory({ typeboxInstance: t });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => t.Number({ ...schema, error: "`name` must be a string" }),
});
```
### Data type reference
MSSQL data type mappings follow the MSSQL column builders. See the [MSSQL data types](/docs/mssql/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/mssql/typebox
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox
#### Install the dependencies
drizzle-orm@rc typebox
Allows you to generate [typebox](https://sinclairzx81.github.io/typebox/#/) schemas from Drizzle ORM schemas
**Features**
- Create a select schema for tables.
- Create insert and update schemas for tables.
- Supported dialect: MSSQL.
#### Usage
```ts
import { int, mssqlTable, text, datetime2 } from 'drizzle-orm/mssql-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/typebox';
import { Type } from 'typebox';
import { Value } from 'typebox/value';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
email: text().notNull(),
role: text({ enum: ['admin', 'user'] }).notNull(),
createdAt: datetime2('created_at').notNull(),
});
// 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) => Type.Number({ ...schema, minimum: 0 }),
role: Type.String(),
});
// Usage
const isUserValid: boolean = Value.Check(insertUserSchema, {
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
```
Source: https://orm.drizzle.team/docs/mssql/update
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# SQL Update
All the values provided to `.set()`are parameterized automatically.
For example, this query:
```ts
await db.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan"));
```
will be translated to:
```sql
update [users] set [name] = @par0 where [users].[name] = @par1; -- params: ['Mr. Dan', 'Dan']
```
The object that you pass to `update` should have keys that match column names in your database schema.
Values of `undefined` are ignored in the object: to set a column to `null`, pass `null`.
```typescript copy
await db.update(users)
.set({ name: 'Mr. Dan' })
.where(eq(users.name, 'Dan'));
```
```typescript copy
await db.update(users)
.set({ name: null })
.where(eq(users.name, 'Dan'));
```
You can pass SQL as a value to be used in the update object, like this:
```typescript copy
await db.update(users)
.set({ updatedAt: sql`NOW()` })
.where(eq(users.name, 'Dan'));
```
### Output
This method allows you to return values from the rows affected by the query. MSSQL supports returning inserted (new row values) and deleted (old row values) values.
If no fields are specified, all inserted values will be returned by default.
```ts
// Update cars and return all new values
const updatedCars: Car[] = await db.update(cars)
.set({ color: 'red' })
.output()
.where(eq(cars.color, 'green'));
// Update cars and return all new values
const updatedCars: Car[] = await db.update(cars)
.set({ color: 'red' })
.output({ inserted: true })
.where(eq(cars.color, 'green'));
// Update cars and return all old values
const updatedCarsIds: { deleted: Car }[] = await db.update(cars)
.set({ color: 'red' })
.output({ deleted: true })
.where(eq(cars.color, 'green'));
// Update cars and return partial old and new values
const beforeAndAfter: { deleted: { oldColor: string }, inserted: { newColor: string } }[] = await db.update(cars)
.set({ color: 'red' })
.output({
deleted: { oldColor: cars.color },
inserted: { newColor: cars.color }
})
.where(eq(cars.color, 'green'));
```
```sql
update [cars] set [color] = 'red' output INSERTED.[name], INSERTED.[color] where [cars].[color] = 'green';
update [cars] set [color] = 'red' output INSERTED.[name], INSERTED.[color] where [cars].[color] = 'green';
update [cars] set [color] = 'red' output DELETED.[name], DELETED.[color] where [cars].[color] = 'green';
update [cars] set [color] = 'red' output INSERTED.[color], DELETED.[color] where [cars].[color] = 'green';
```
Source: https://orm.drizzle.team/docs/mssql/valibot
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# valibot
### Install the dependencies
drizzle-orm@rc valibot
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).top(1).from(users);
const parsed: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().top(1).from(users);
const parsed: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { pgEnum, pgView } from 'drizzle-orm/mssql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const usersView = mssqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = parse(usersViewSchema, ...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createInsertSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = parse(userInsertSchema, user); // Will parse successfully
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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createUpdateSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = parse(userUpdateSchema, user); // Will parse successfully
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 Valibot schema will overwrite it.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse, pipe, maxLength, object, string } from 'valibot';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
bio: text(),
preferences: text()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => pipe(schema, maxLength(20)), // Extends schema
bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = parse(userSelectSchema, ...);
```
### Data type reference
MSSQL data type mappings follow the MSSQL column builders. See the [MSSQL data types](/docs/mssql/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/mssql/views
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Views
There're several ways you can declare views with Drizzle ORM.
You can declare views that have to be created or you can declare views that already exist in the database.
You can declare views statements with an inline `query builder` syntax, with `standalone query builder` and with raw `sql` operators.
When views are created with either inlined or standalone query builders, view columns schema will be automatically inferred,
yet when you use `sql` you have to explicitly declare view columns schema.
### Declaring views
```ts filename="schema.ts" copy {14-15}
import { int, mssqlTable, mssqlView, nvarchar, datetime2 } from "drizzle-orm/mssql-core";
import { eq } from "drizzle-orm";
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
name: nvarchar({ length: 256 }),
email: nvarchar({ length: 256 }),
password: nvarchar({ length: 256 }),
role: nvarchar({ length: 256 }).$type<"admin" | "customer">(),
createdAt: datetime2("created_at"),
updatedAt: datetime2("updated_at"),
});
export const userView = mssqlView("user_view").as((qb) => qb.select().from(user));
export const customersView = mssqlView("customers_view").as((qb) => qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
CREATE VIEW [customers_view] AS select ... from [user] where [user].[role] = 'customer';
CREATE VIEW [user_view] AS select ... from [user];
```
You can also declare views using `standalone query builder`, it works exactly the same way:
```ts filename="schema.ts" copy {4,16-17}
import { int, mssqlTable, mssqlView, nvarchar, datetime2, QueryBuilder } from "drizzle-orm/mssql-core";
import { eq } from "drizzle-orm";
const qb = new QueryBuilder();
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
name: nvarchar({ length: 256 }),
email: nvarchar({ length: 256 }),
password: nvarchar({ length: 256 }),
role: nvarchar({ length: 256 }).$type<"admin" | "customer">(),
createdAt: datetime2("created_at"),
updatedAt: datetime2("updated_at"),
});
export const userView = mssqlView("user_view").as(qb.select().from(user));
export const customersView = mssqlView("customers_view").as(qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
CREATE VIEW [customers_view] AS select ... from [user] where [user].[role] = 'customer';
CREATE VIEW [user_view] AS select ... from [user];
```
### Declaring views with raw SQL
Whenever you need to declare view using a syntax that is not supported by the query builder,
you can directly use `sql` operator and explicitly specify view columns schema.
```ts copy
import { eq, sql } from "drizzle-orm";
import { int, mssqlTable, mssqlView, nvarchar } from "drizzle-orm/mssql-core";
export const users = mssqlTable("users", {
id: int().identity().primaryKey(),
name: nvarchar({ length: 256 }),
cityId: int("city_id"),
});
export const newYorkers = mssqlView("new_yorkers", {
id: int().primaryKey(),
name: nvarchar({ length: 256 }).notNull(),
cityId: int("city_id").notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);
```
```sql
CREATE VIEW [new_yorkers] AS select * from [users] where [users].[city_id] = 1;
```
### Declaring existing views
When you're provided with a read only access to an existing view in the database you should use `.existing()` view configuration,
`drizzle-kit` will ignore and will not generate a `create view` statement in the generated migration.
```ts {17}
import { int, mssqlTable, mssqlView, nvarchar, datetime2 } from "drizzle-orm/mssql-core";
export const user = mssqlTable("user", {
id: int().identity().primaryKey(),
name: nvarchar({ length: 256 }),
email: nvarchar({ length: 256 }),
password: nvarchar({ length: 256 }),
role: nvarchar({ length: 256 }).$type<"admin" | "customer">(),
createdAt: datetime2("created_at"),
updatedAt: datetime2("updated_at"),
});
export const trimmedUser = mssqlView("trimmed_user", {
id: int("id"),
name: nvarchar("name", { length: 256 }),
email: nvarchar("email", { length: 256 }),
}).existing();
```
### Extended example
All the parameters inside the query will be inlined, instead of being parameterised.
```ts copy
export const newYorkers = mssqlView('new_yorkers')
.with({
encryption: true,
schemaBinding: false,
viewMetadata: true,
checkOption: true,
})
.as((qb) => {
const sq = qb.$with('sq').as(
qb
.select({
userId: users.id.as('user_id'),
cityId: cities.id.as('city_id'),
})
.from(users)
.leftJoin(cities, eq(cities.id, users.homeCity))
.where(sql`${users.age1} > 18 and ${users.homeCity} = 1`),
);
return qb.with(sq).select().from(sq);
});
```
```sql
CREATE VIEW [new_yorkers]
WITH ENCRYPTION, VIEW_METADATA
AS
WITH
[sq] AS (
SELECT
[users].[id] AS [user_id],
[cities].[id] AS [city_id]
FROM
[users]
LEFT JOIN [cities] ON [cities].[id] = [users].[home_city]
WHERE
[users].[age1] > 18
AND [users].[home_city] = 1
)
SELECT
[user_id],
[city_id]
FROM
[sq]
WITH
CHECK
OPTION;
```
Source: https://orm.drizzle.team/docs/mssql/zod
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# zod
### Install the dependencies
drizzle-orm@rc zod
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/zod';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).top(1).from(users);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().top(1).from(users);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { createSelectSchema } from 'drizzle-orm/zod';
import { mssqlView } from 'drizzle-orm/mssql-core';
const usersView = mssqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = usersViewSchema.parse(...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createInsertSchema } from 'drizzle-orm/zod';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Will parse successfully
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 { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createUpdateSchema } from 'drizzle-orm/zod';
import { eq } from 'drizzle-orm';
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema.parse(user); // Will parse successfully
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 Zod schema will overwrite it.
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSelectSchema } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = mssqlTable('users', {
id: int().primaryKey(),
name: text().notNull(),
bio: text(),
preferences: text()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => schema.max(20), // Extends schema
bio: (schema) => schema.max(1000), // Extends schema before becoming nullable/optional
preferences: z.object({ theme: z.string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = userSelectSchema.parse(...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Zod instance**
```ts copy
import { int, mssqlTable, text } from 'drizzle-orm/mssql-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from '@hono/zod-openapi'; // Extended Zod instance
const users = mssqlTable('users', {
id: int().primaryKey().identity(),
name: text().notNull(),
age: int().notNull()
});
const { createInsertSchema } = createSchemaFactory({ zodInstance: z });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => schema.openapi({ example: 'John' })
});
```
**Use case: Type coercion**
```ts copy
import { mssqlTable, datetime2 } from 'drizzle-orm/mssql-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = mssqlTable('users', {
...,
createdAt: datetime2().notNull()
});
const { createInsertSchema } = createSchemaFactory({
// This configuration will only coerce dates. Set `coerce` to `true` to coerce all data types or specify others
coerce: {
date: true
}
});
const userInsertSchema = createInsertSchema(users);
// The above is the same as this:
const userInsertSchema = z.object({
...,
createdAt: z.coerce.date()
});
```
### Data type reference
MSSQL data type mappings follow the MSSQL column builders. See the [MSSQL data types](/docs/mssql/column-types) page for the full column reference.
Source: https://orm.drizzle.team/docs/mysql/aliases
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, mysqlTable, int, text } from "drizzle-orm/mysql-core";
const employees = mysqlTable("employees", {
id: int().autoincrement().primaryKey(),
name: text(),
managerId: int("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` `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);
```
```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));
```
```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);
```
```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);
```
```sql
with `sq` as (select upper(`name`) as `name` from `users`)
select `name` from `sq`;
```
Source: https://orm.drizzle.team/docs/mysql/arktype
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# arktype
### Install the dependencies
drizzle-orm@rc arktype
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import type { ArkErrors } from "arktype";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().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 are also supported.
```ts copy
import { mysqlView } from 'drizzle-orm/mysql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/arktype';
const usersView = mysqlView('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 { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createInsertSchema } from 'drizzle-orm/arktype';
import type { ArkErrors } from "arktype";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: ArkErrors | { id?: number | undefined, name: string, age: number } = userInsertSchema(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: ArkErrors | { id?: number | undefined, 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 { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { parse } from 'arktype';
import { ArkErrors } from "arktype";
import { eq } from "drizzle-orm";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: ArkErrors | { id?: number | undefined, 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 { int, json, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors, type } from 'arktype';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
bio: text(),
preferences: json()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => pipe(schema, maxLength(20)), // Extends schema
bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});
const parsed: ArkErrors | {
id: number;
name: string,
bio?: string | undefined;
preferences: {
theme: string;
};
} = userSelectSchema(...);
```
### Data type reference
```ts
mysql.boolean();
// Schema
type.boolean;
```
```ts
mysql.mysqlEnum('name', ['val1', 'val2']);
// Schema
type.enumerated('val1', 'val2')
```
```ts
mysql.date({ mode: 'date' });
mysql.datetime({ mode: 'date' });
mysql.timestamp({ mode: 'date' });
// Schema
type.Date;
```
```ts
mysql.binary();
mysql.date({ mode: 'string' });
mysql.datetime({ mode: 'string' });
mysql.decimal();
mysql.time();
mysql.timestamp({ mode: 'string' });
mysql.varbinary();
// Schema
type.string;
```
```ts
mysql.tinyblob({ mode: 'string' });
mysql.tinyblob();
type.string.atMostLength(255)
```
```ts
mysql.blob({ mode: 'string' });
mysql.blob();
type.string.atMostLength(65_535)
```
```ts
mysql.mediumblob({ mode: 'string' });
mysql.mediumblob();
type.string.atMostLength(16_777_215)
```
```ts
mysql.longblob({ mode: 'string' });
mysql.longblob();
type.string.atMostLength(4_294_967_295)
```
```ts
mysql.char({ length: ... });
// Schema
type.string.exactlyLength(length);
```
```ts
mysql.varchar({ length: ... });
// Schema
type.string.atMostLength(length);
```
```ts
binary({ length: ... });
// Schema
type(`/^[01]{0,${length ?? "*"}}$/`)
```
```ts
mysql.varbinary({ length: ... });
// Schema
type(`/^[01]{0,${length ?? "*"}}$/`)
```
```ts
mysql.tinytext();
// Schema
type.string.atMostLength(255); // unsigned 8-bit integer limit
```
```ts
mysql.text();
// Schema
type.string.atMostLength(65_535); // unsigned 16-bit integer limit
```
```ts
mysql.mediumtext();
// Schema
type.string.atMostLength(16_777_215); // unsigned 24-bit integer limit
```
```ts
mysql.longtext();
// Schema
type.string.atMostLength(4_294_967_295); // unsigned 32-bit integer limit
```
```ts
mysql.tinytext({ enum: ... });
mysql.mediumtext({ enum: ... });
mysql.text({ enum: ... });
mysql.longtext({ enum: ... });
mysql.char({ enum: ... });
mysql.varchar({ enum: ... });
// Schema
type.enumerated(...enum);
```
```ts
mysql.tinyint();
// Schema
type.keywords.number.integer.atLeast(-128).atMost(127); // 8-bit integer lower and upper limit
```
```ts
mysql.tinyint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(255); // unsigned 8-bit integer lower and upper limit
```
```ts
mysql.smallint();
// Schema
type.keywords.number.integer.atLeast(-32_768).atMost(32_767); // 16-bit integer lower and upper limit
```
```ts
mysql.smallint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(65_535); // unsigned 16-bit integer lower and upper limit
```
```ts
mysql.float();
// Schema
type.number.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.mediumint();
// Schema
type.keywords.number.integer.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.float({ unsigned: true });
// Schema
type.number.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.mediumint({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(16_777_215); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.int();
// Schema
type.keywords.number.integer.atLeast(-2_147_483_648).atMost(2_147_483_647); // 32-bit integer lower and upper limit
```
```ts
mysql.int({ unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(4_294_967_295); // unsgined 32-bit integer lower and upper limit
```
```ts
mysql.double();
mysql.real();
// Schema
type.number.atLeast(-140_737_488_355_328).atMost(140_737_488_355_327); // 48-bit integer lower and upper limit
```
```ts
mysql.double({ unsigned: true });
// Schema
type.number.atLeast(0).atMost(281_474_976_710_655); // unsigned 48-bit integer lower and upper limit
```
```ts
mysql.decimal({ mode: 'number' });
// Schema
type.number.atLeast(9_007_199_254_740_991).atMost(9_007_199_254_740_991)
```
```ts
mysql.decimal({ mode: 'bigint' });
// Schema
type.bigint.narrow(
(value, ctx) => value < -9_223_372_036_854_775_808n ? ctx.mustBe('greater than') : value > 9_223_372_036_854_775_807n ? ctx.mustBe('less than') : true
); // 64-bit integer lower and upper limit
```
```ts
mysql.decimal({ mode: 'number', unsigned: true });
// Schema
type.number.atLeast(0).atMost(9_007_199_254_740_991)
```
```ts
mysql.decimal({ mode: 'bigint', unsigned: true });
// Schema
type.bigint.narrow(
(value, ctx) => value < 0n ? ctx.mustBe('greater than') : value > 18_446_744_073_709_551_615n ? ctx.mustBe('less than') : true
); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'number' });
// Schema
type.keywords.number.integer.atLeast(-9_007_199_254_740_991).atMost(9_007_199_254_740_991); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'number', unsigned: true });
// Schema
type.keywords.number.integer.atLeast(0).atMost(9_007_199_254_740_991); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'string' });
// Schema
type.string.narrow((v, ctx) => {
if (typeof v !== 'string') {
return ctx.mustBe('a string');
}
if (!(/^-?\d+$/.test(v))) {
return ctx.mustBe('a string representing a number');
}
const bigint = BigInt(v);
if (bigint < -9_223_372_036_854_775_808n) {
return ctx.mustBe('greater than');
}
if (bigint > 9_223_372_036_854_775_807n) {
return ctx.mustBe('less than');
}
return true;
});
```
```ts
mysql.bigint({ mode: 'string', unsigned: true });
// Schema
type.string.narrow((v, ctx) => {
if (typeof v !== 'string') {
return ctx.mustBe('a string');
}
if (!(/^\d+$/.test(v))) {
return ctx.mustBe('a string representing a number');
}
const bigint = BigInt(v);
if (bigint < 0) {
return ctx.mustBe('greater than');
}
if (bigint > 9_223_372_036_854_775_807n) {
return ctx.mustBe('less than');
}
return true;
});
```
```ts
mysql.bigint({ mode: 'bigint' });
// Schema
type.bigint.narrow(
(value, ctx) => value < -9_223_372_036_854_775_808n ? ctx.mustBe('greater than') : value > 9_223_372_036_854_775_807n ? ctx.mustBe('less than') : true
); // 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'bigint', unsigned: true });
// Schema
type.bigint.narrow(
(value, ctx) => value < 0n ? ctx.mustBe('greater than') : value > 18_446_744_073_709_551_615n ? ctx.mustBe('less than') : true
); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.serial();
// Schema
type.keywords.number.integer.atLeast(0).atMost(9_007_199_254_740_991); // Javascript max. safe integer
```
```ts
mysql.year();
// Schema
type.keywords.number.integer.atLeast(1_901).atMost(2_155);
```
```ts
mysql.json();
// Schema
type('string | number | boolean | null').or(type('unknown.any[] | Record'));
```
Source: https://orm.drizzle.team/docs/mysql/cache
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/mysql2";
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/mysql2";
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/mysql2";
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: "cacheman@upstash.com" });
```
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/mysql2";
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() });
```
```ts
import Keyv from "keyv";
export class TestGlobalCache extends Cache {
private globalTtl: number = 1000;
// This object will be used to store which query keys were used
// for a specific table, so we can later use it for invalidation.
private usedTablesPerKey: Record = {};
constructor(private kv: Keyv = new Keyv()) {
super();
}
// For the strategy, we have two options:
// - 'explicit': The cache is used only when .$withCache() is added to a query.
// - 'all': All queries are cached globally.
// The default behavior is 'explicit'.
override strategy(): "explicit" | "all" {
return "all";
}
// This function accepts query and parameters that cached into key param,
// allowing you to retrieve response values for this query from the cache.
override async get(key: string): Promise {
const res = (await this.kv.get(key)) ?? undefined;
return res;
}
// This function accepts several options to define how cached data will be stored:
// - 'key': A hashed query and parameters.
// - 'response': An array of values returned by Drizzle from the database.
// - 'tables': An array of tables involved in the select queries. This information is needed for cache invalidation.
// - 'isTag': A boolean flag indicating whether this cache entry represents a tag-based query.
// When true, the entry is associated with a specific tag rather than table-level invalidation,
// allowing you to invalidate cached queries by tag name. See the UpstashCache:
// https://github.com/drizzle-team/drizzle-orm/blob/beta/drizzle-orm/src/cache/upstash/cache.ts for an example
//
// For example, if a query uses the "users" and "posts" tables, you can store this information. Later, when the app executes
// any mutation statements on these tables, you can remove the corresponding key from the cache.
// If you're okay with eventual consistency for your queries, you can skip this option.
override async put(
key: string,
response: any,
tables: string[],
isTag: boolean = false,
config?: CacheConfig,
): Promise {
const ttl = config?.px ?? (config?.ex ? config.ex * 1000 : this.globalTtl);
await this.kv.set(key, response, ttl);
for (const table of tables) {
const keys = this.usedTablesPerKey[table];
if (keys === undefined) {
this.usedTablesPerKey[table] = [key];
} else {
keys.push(key);
}
}
}
// This function is called when insert, update, or delete statements are executed.
// You can either skip this step or invalidate queries that used the affected tables.
//
// The function receives an object with two keys:
// - 'tags': Used for queries labeled with a specific tag, allowing you to invalidate by that tag.
// - 'tables': The actual tables affected by the insert, update, or delete statements,
// helping you track which tables have changed since the last cache update.
override async onMutate(params: {
tags: string | string[];
tables: string | string[] | Table | Table[];
}): Promise {
const tagsArray = params.tags
? Array.isArray(params.tags)
? params.tags
: [params.tags]
: [];
const tablesArray = params.tables
? Array.isArray(params.tables)
? params.tables
: [params.tables]
: [];
const keysToDelete = new Set();
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
const keys = this.usedTablesPerKey[tableName] ?? [];
for (const key of keys) keysToDelete.add(key);
}
if (keysToDelete.size > 0 || tagsArray.length > 0) {
for (const tag of tagsArray) {
await this.kv.delete(tag);
}
for (const key of keysToDelete) {
await this.kv.delete(key);
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
this.usedTablesPerKey[tableName] = [];
}
}
}
}
}
```
## Limitations
#### Queries that won't be handled by the `cache` extension:
- Using cache with raw queries, such as:
```ts
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
```ts
await db.query.users.findMany();
```
- Using cache with views
Source: https://orm.drizzle.team/docs/mysql/column-types
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
We have native support for all of them, yet if that's not enough for you, feel free to create **[custom types](/docs/mysql/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 API` to define a mapping strategy for Drizzle.
You can read more about it [here](/docs/mysql/sql-schema-declaration#shape-your-data-schema)
All integer types (`int`, `tinyint`, `smallint`, `mediumint`, `bigint`) are `signed` by default, allowing both negative and positive values.
Adding `{ unsigned: true }` restricts the column to non-negative values only but doubles the upper range.
### integer
A `4`-byte integer with a range of `-2,147,483,648` to `2,147,483,647` signed, or `0` to `4,294,967,295` unsigned
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/integer-types.html)**
```typescript
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
int: int(),
int2: int({ unsigned: true })
});
```
```sql
CREATE TABLE `table` (
`int` int,
`int2` int unsigned
);
```
### tinyint
A `1`-byte integer with a range of `-128` to `127` signed, or `0` to `255` unsigned
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/integer-types.html)**
```typescript
import { tinyint, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
tinyint: tinyint(),
tinyint2: tinyint({ unsigned: true })
});
```
```sql
CREATE TABLE `table` (
`tinyint` tinyint,
`tinyint2` tinyint unsigned
);
```
### smallint
A `2`-byte integer with a range of `-32,768` to `32,767` signed, or `0` to `65,535` unsigned
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/integer-types.html)**
```typescript
import { smallint, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
smallint: smallint(),
smallint2: smallint({ unsigned: true }),
});
```
```sql
CREATE TABLE `table` (
`smallint` smallint,
`smallint2` smallint unsigned
);
```
### mediumint
A `3`-byte integer with a range of `-8,388,608` to `8,388,607` signed, or `0` to `16,777,215` unsigned
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/integer-types.html)**
```typescript
import { mediumint, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
mediumint: mediumint(),
mediumint2: mediumint({ unsigned: true })
});
```
```sql
CREATE TABLE `table` (
`mediumint` mediumint,
`mediumint2` mediumint unsigned
);
```
### bigint
An `8`-byte integer with a range of `-2^63` to `2^63-1` signed, or `0` to `2^64-1` unsigned
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/integer-types.html)**
```typescript
import { bigint, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
bigint: bigint({ mode: 'number' }),
bigintUnsigned: bigint({ mode: 'number', unsigned: true })
});
bigint("...", { mode: 'number' | 'bigint' | 'string' });
// You can also specify unsigned option for bigint
bigint("...", { mode: 'number' | 'bigint' | 'string', unsigned: true });
```
```sql
CREATE TABLE `table` (
`bigint` bigint,
`bigintUnsigned` bigint unsigned
);
```
We've omitted config of `M` in `bigint(M)`, since it indicates the display width of the numeric type
## ---
### real
A floating-point number
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html)**
```typescript
import { real, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
real: real()
});
```
```sql
CREATE TABLE `table` (
`real` real
);
```
```typescript
import { real, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
realPrecision: real({ precision: 1 }),
realPrecisionScale: real({ precision: 1, scale: 1 }),
});
```
```sql
CREATE TABLE `table` (
`realPrecision` real(1),
`realPrecisionScale` real(1, 1)
);
```
### decimal
An exact fixed-point number. `DECIMAL(M, D)` stores up to `M` total digits with `D` digits after the decimal point. Maximum precision is `65` digits
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html)**
```typescript
import { decimal, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
decimal: decimal(),
decimalNum: decimal({ precision: 30, mode: 'number' }),
decimalBig: decimal({ precision: 30, mode: 'bigint' }),
decimal2: decimal({ precision: 30, scale: 10 }),
});
```
```sql
CREATE TABLE `table` (
`decimal` decimal,
`decimalNum` decimal(30),
`decimalBig` decimal(30),
`decimal2` decimal(30,10)
);
```
### double
An `8`-byte double-precision floating-point number representing approximate numeric data values
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html)**
```typescript
import { double, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
double: double()
});
```
```sql
CREATE TABLE `table` (
`double` double
);
```
```typescript
import { double, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
doublePrecision: double({ precision: 1 }),
doublePrecisionScale: double({ precision: 1, scale: 1 })
});
```
```sql
CREATE TABLE `table` (
`doublePrecision` double(1),
`doublePrecisionScale` double(1,1)
);
```
### float
A `4`-byte single-precision floating-point number representing approximate numeric data values
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html)**
```typescript
import { float, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
float: float()
});
```
```sql
CREATE TABLE `table` (
`float` float
);
```
## ---
### serial
`SERIAL` is an alias for `BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE`.
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html)**
```typescript
import { serial, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
serial: serial()
});
```
```sql
CREATE TABLE `table` (
`serial` serial
);
```
## ---
### binary
`BINARY(M)` stores a fixed-length byte string of exactly M bytes (If omitted, M defaults to 1).
On insert, shorter values are right-padded with `0x00` bytes to reach M bytes; on retrieval, no padding is stripped.
All bytes are significant in comparisons, including `ORDER BY` and `DISTINCT` operations
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/binary-varbinary.html)**
```typescript
import { binary, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
binary: binary(),
binary2: binary({ length: 10 }),
});
```
```sql
CREATE TABLE `table` (
`binary` binary,
`binary2` binary(10)
);
```
### varbinary
`VARBINARY(M)` stores a variable-length byte string of up to M bytes (M as required).
For `VARBINARY`, there is no padding for inserts and no bytes are stripped for retrievals.
All bytes are significant in comparisons, including `ORDER BY` and `DISTINCT` operations
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/binary-varbinary.html)**
```typescript
import { varbinary, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
varbinary: varbinary({ length: 2 }),
});
```
```sql
CREATE TABLE `table` (
`varbinary` varbinary(2)
);
```
## ---
### blob
A `BLOB` is a binary large object that can hold a variable amount of data with a maximum length of `65,535` bytes (`64` KB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
```typescript
import { blob, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
blob: blob()
});
```
```sql
CREATE TABLE `table` (
`blob` blob
);
```
You can specify either `buffer` or `string` mode:
```typescript
// will be inferred as `Buffer`
blob: blob({ mode: "buffer" }),
// will be inferred as `string`
blob: blob({ mode: "string" }),
```
> The `buffer` mode is the default way to work with blobs.
> Drizzle will represent the blob value as a `Buffer` instance
> The `string` mode represents blob values as UTF-8 encoded strings,
> which can be useful when storing text data in blob columns
### tinyblob
A `BLOB` column with a maximum length of `255` bytes
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
```typescript
import { tinyblob, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
tinyblob: tinyblob()
});
```
```sql
CREATE TABLE `table` (
`tinyblob` tinyblob
);
```
You can specify either `buffer` or `string` mode:
```typescript
// will be inferred as `Buffer`
tinyblob: tinyblob({ mode: "buffer" }),
// will be inferred as `string`
tinyblob: tinyblob({ mode: "string" }),
```
> The `buffer` mode is the default way to work with blobs.
> Drizzle will represent the blob value as a `Buffer` instance
> The `string` mode represents blob values as UTF-8 encoded strings,
> which can be useful when storing text data in blob columns
### mediumblob
A `BLOB` column with a maximum length of `16,777,215` bytes (`16` MB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
```typescript
import { mediumblob, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
mediumblob: mediumblob()
});
```
```sql
CREATE TABLE `table` (
`mediumblob` mediumblob
);
```
You can specify either `buffer` or `string` mode:
```typescript
// will be inferred as `Buffer`
mediumblob: mediumblob({ mode: "buffer" }),
// will be inferred as `string`
mediumblob: mediumblob({ mode: "string" }),
```
> The `buffer` mode is the default way to work with blobs.
> Drizzle will represent the blob value as a `Buffer` instance
> The `string` mode represents blob values as UTF-8 encoded strings,
> which can be useful when storing text data in blob columns
### longblob
A `BLOB` column with a maximum length of `4,294,967,295` bytes (`4` GB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
```typescript
import { longblob, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
longblob: longblob()
});
```
```sql
CREATE TABLE `table` (
`longblob` longblob
);
```
You can specify either `buffer` or `string` mode:
```typescript
// will be inferred as `Buffer`
longblob: longblob({ mode: "buffer" }),
// will be inferred as `string`
longblob: longblob({ mode: "string" }),
```
> The `buffer` mode is the default way to work with blobs.
> Drizzle will represent the blob value as a `Buffer` instance
> The `string` mode represents blob values as UTF-8 encoded strings,
> which can be useful when storing text data in blob columns
## ---
### char
The length of a `CHAR(M)` column is fixed to the length that you declare when you create the table. `M` can be any value from `0` to `255`, default is `1`
When `CHAR` values are stored, they are right-padded with spaces to the specified length. When `CHAR` values are retrieved, trailing spaces are removed unless the [PAD_CHAR_TO_FULL_LENGTH](https://dev.mysql.com/doc/refman/9.7/en/sql-mode.html#sqlmode_pad_char_to_full_length) SQL mode is enabled.
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/char.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer insert and select types, it won't check runtime values.
```typescript
import { char, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
char: char(),
});
// will be inferred as char: "value1" | "value2" | null
char: char({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE `table` (
`char` char
);
```
### varchar
Values in `VARCHAR` columns are variable-length strings.
`VARCHAR(M)` stores up to `M` characters, where `M` ranges from `0` to `65,535`
`VARCHAR` values are not padded when they are stored. Trailing spaces are retained when values are stored and retrieved, in conformance with standard SQL
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/char.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { varchar, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
varchar: varchar({ length: 2 }),
});
// will be inferred as text: "value1" | "value2" | null
varchar: varchar({ length: 6, enum: ["value1", "value2"] })
```
```sql
CREATE TABLE `table` (
`varchar` varchar(2)
);
```
### text
A `TEXT` column with a maximum length of `65,535` characters (`64` KB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { text, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
text: text(),
});
// will be inferred as text: "value1" | "value2" | null
text: text({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE `table` (
`text` text
);
```
### tinytext
A `TEXT` column with a maximum length of `255` characters
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { tinytext, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
tinytext: tinytext(),
});
// will be inferred as tinytext: "value1" | "value2" | null
tinytext: tinytext({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE `table` (
`tinytext` tinytext
);
```
### mediumtext
A `TEXT` column with a maximum length of `16,777,215` characters (`16` MB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { mediumtext, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
mediumtext: mediumtext(),
});
// will be inferred as mediumtext: "value1" | "value2" | null
mediumtext: mediumtext({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE `table` (
`mediumtext` mediumtext
);
```
### longtext
A `TEXT` column with a maximum length of `4,294,967,295` characters (`4` GB)
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/blob.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { longtext, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
longtext: longtext(),
});
// will be inferred as longtext: "value1" | "value2" | null
longtext: longtext({ enum: ["value1", "value2"] });
```
```sql
CREATE TABLE `table` (
`longtext` longtext
);
```
## ---
### boolean
`BOOLEAN` is a synonym for `TINYINT(1)`. A value of `0` is considered false, non-zero values are considered true
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html)**
```typescript
import { boolean, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
boolean: boolean(),
});
```
```sql
CREATE TABLE `table` (
`boolean` boolean
);
```
## ---
### date
A date value in `'YYYY-MM-DD'` format, with a range of `'1000-01-01'` to `'9999-12-31'`
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/datetime.html)**
```typescript
import { date, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('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" }),
```
### datetime
A date and time value in `'YYYY-MM-DD hh:mm:ss'` format, with a range of `'1000-01-01 00:00:00'` to `'9999-12-31 23:59:59'`. Supports fractional seconds up to `6` digits
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/datetime.html)**
```typescript
import { datetime, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
datetime: datetime(),
});
datetime('...', { mode: 'date' | "string"}),
datetime('...', { fsp : 0..6}),
```
```sql
CREATE TABLE `table` (
`datetime` datetime
);
```
```typescript
import { datetime, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
datetime: datetime({ mode: 'date', fsp: 6 }),
});
```
```sql
CREATE TABLE `table` (
`datetime` datetime(6)
);
```
### time
A time value in `'hh:mm:ss'` format, with a range of `'-838:59:59'` to `'838:59:59'`. Can represent time of day or elapsed time. Supports fractional seconds up to `6` digits
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/time.html)**
```typescript
import { time, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
time: time(),
timefsp: time({ fsp: 6 }),
});
time('...', { fsp: 0..6 }),
```
```sql
CREATE TABLE `table` (
`time` time,
`timefsp` time(6)
);
```
### year
A `1`-byte type for year values in `YYYY` format, with a range of `1901` to `2155` and `0000`
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/year.html)**
```typescript
import { year, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
year: year(),
});
```
```sql
CREATE TABLE `table` (
`year` year
);
```
### timestamp
A date and time value in `'YYYY-MM-DD hh:mm:ss'` format, with a range of `'1970-01-01 00:00:01'` UTC to `'2038-01-19 03:14:07'` UTC. MySQL converts `TIMESTAMP` values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. Supports fractional seconds up to `6` digits
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/datetime.html)**
```typescript
import { timestamp, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
timestamp: timestamp(),
});
timestamp('...', { mode: 'date' | "string"}),
timestamp('...', { fsp : 0..6}),
```
```sql
CREATE TABLE `table` (
`timestamp` timestamp
);
```
```typescript
import { timestamp, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
timestamp: timestamp({ mode: 'date', fsp: 6 }),
});
```
```sql
CREATE TABLE `table` (
`timestamp` timestamp(6)
);
```
```typescript
import { timestamp, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
timestamp: timestamp().defaultNow(),
});
```
```sql
CREATE TABLE `table` (
`timestamp` timestamp DEFAULT (now())
);
```
## ---
### json
A native `JSON` data type that enables efficient access to data in JSON documents. JSON documents are automatically validated and stored in an optimized binary format that permits quick read access to document elements
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/json.html)**
```typescript
import { json, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
json: json(),
});
```
```sql
CREATE TABLE `table` (
`json` json
);
```
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 }
json: json().$type<{ foo: string }>();
// will be inferred as string[]
json: json().$type();
// won't compile
json: json().$type().default({});
```
## ---
### enum
A string object with a value chosen from a list of permitted values that are enumerated at table creation time.
For more info please refer to the official MySQL **[docs.](https://dev.mysql.com/doc/refman/8.0/en/enum.html)**
```typescript
import { mysqlEnum, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
popularity: mysqlEnum(['unknown', 'known', 'popular']),
});
```
```sql
CREATE TABLE `table` (
`popularity` enum('unknown','known','popular')
);
```
## ---
### 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
import { int, json, mysqlTable } from "drizzle-orm/mysql-core";
type UserId = number & { __brand: "user_id" };
type Data = {
foo: string;
bar: number;
};
export const users = mysqlTable("users", {
id: int().$type().primaryKey(),
jsonField: json().$type(),
});
```
### Not null
`NOT NULL` constraint dictates that the associated column may not contain a `NULL` value.
```typescript
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
int: int().notNull(),
});
```
```sql
CREATE TABLE `table` (
`int` int NOT NULL
);
```
### 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 { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable("table", {
int: int().default(3),
int2: int().default(sql`3`),
});
```
```sql
CREATE TABLE `table` (
`int` int DEFAULT 3,
`int2` int DEFAULT (3)
);
```
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 { varchar, mysqlTable } from "drizzle-orm/mysql-core";
import { createId } from '@paralleldrive/cuid2';
export const table = mysqlTable('table', {
id: varchar({ length: 128 }).$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 { text, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
alwaysNull: text().$type().$onUpdate(() => null),
});
```
### Primary key
```typescript
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
int: int().primaryKey(),
});
```
```sql
CREATE TABLE `table` (
`int` int PRIMARY KEY NOT NULL
);
```
### Auto increment
```typescript
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
int: int().autoincrement(),
});
```
```sql
CREATE TABLE `table` (
`int` int AUTO_INCREMENT
);
```
Source: https://orm.drizzle.team/docs/mysql/connect-aws-data-api-mysql
import Callout from '@mdx/Callout.astro';
# Drizzle \<\> AWS Data API MySQL
Currently AWS Data API for MySQL is not implemented in Drizzle ORM
Source: https://orm.drizzle.team/docs/mysql/connect-bun-sql
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Steps from '@mdx/Steps.astro';
import WhatsNextMySQL from "@mdx/WhatsNextMySQL.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> Bun SQL
- Database [connection basics](/docs/connect-overview) with Drizzle
- Bun - [website](https://bun.sh/docs)
- Bun SQL - native bindings for working with MySQL databases - [read here](https://bun.sh/docs/api/sql)
According to the **[official website](https://bun.sh/)**, Bun is a fast all-in-one JavaScript runtime.
Drizzle ORM natively supports **[`bun sql`](https://bun.sh/docs/api/sql)** module and it's crazy fast đ
#### Step 1 - Install packages
drizzle-orm@rc
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/bun-sql/mysql';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/bun-sql/mysql';
import { SQL } from 'bun';
const client = new SQL(process.env.DATABASE_URL!);
const db = drizzle({ client });
```
#### What's next?
Source: https://orm.drizzle.team/docs/mysql/connect-drizzle-proxy
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import Section from "@mdx/Section.astro";
# Drizzle HTTP proxy
- Database [connection basics](/docs/mysql/connect-overview) with Drizzle
How an HTTP Proxy works and why you might need it
Drizzle Proxy is used when you need to implement your own driver communication with the database.
It can be used in several cases, such as adding custom logic at the query stage with existing drivers.
The most common use is with an HTTP driver, which sends queries to your server with the database, executes the query
on your database, and responds with raw data that Drizzle ORM can then map to results
```
âââââââââââââââââââââââââââââ âââââââââââââââââââââââââââââââ
â Drizzle ORM â â HTTP Server with Database â
âââŦââââââââââââââââââââââââââ âââââââââââââââââââââââââââŦââââ
â ^ â
â-- 1. Build query 2. Send built query --â â
â â â
â âââââââââââââââââââââââââââââ â â
ââââââââââââââ>â âââââââ â
â HTTP Proxy Driver â â
ââââââââââââââââ â<ââââââââââââââŦââââââââââââ
â âââââââââââââââââââââââââââââ â
â 3. Execute a query + send raw results back
â-- 4. Map data and return
â
v
```
Drizzle ORM also supports simply using asynchronous callback function for executing SQL.
- `sql` is a query string with placeholders.
- `params` is an array of parameters.
- One of the following values will set for `method` depending on the SQL statement - `all`, `execute`.
Drizzle always waits for `{rows: string[][]}` or `{rows: string[]}` for the return value.
- When the `method` is `execute`, you should return a value as `{rows: string[]}`.
- Otherwise, you should return `{rows: string[][]}`.
```typescript copy
// Example of driver implementation
import { drizzle } from 'drizzle-orm/mysql-proxy';
import axios from "axios";
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post('http://localhost:3000/query', { sql, params, method });
return { rows: rows.data };
} catch (e: any) {
console.error('Error from mysql proxy server: ', e.response.data)
return { rows: [] };
}
});
```
```ts
// Example of server implementation
import * as mysql from 'mysql2/promise';
import express from 'express';
const app = express();
app.use(express.json());
const port = 3000;
const main = async () => {
const connection = await mysql.createConnection('mysql://root:mysql@127.0.0.1:5432/drizzle');
app.post('/query', async (req, res) => {
const { sql, params, method } = req.body;
// prevent multiple queries
const sqlBody = sql.replace(/;/g, '');
try {
const result = await connection.query({
sql: sqlBody,
values: params,
rowsAsArray: method === 'all',
typeCast: function(field: any, next: any) {
if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') {
return field.string();
}
return next();
},
});
} catch (e: any) {
res.status(500).json({ error: e });
}
if (method === 'all') {
res.send(result[0]);
} else if (method === 'execute') {
res.send(result);
}
res.status(500).json({ error: 'Unknown method value' });
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
}
main();
```
Source: https://orm.drizzle.team/docs/mysql/connect-overview
# Database connection with Drizzle
Use these MySQL drivers and providers with Drizzle ORM.
## MySQL connection docs
- [MySQL](/docs/mysql/get-started-mysql)
- [PlanetScale MySQL](/docs/mysql/connect-planetscale)
- [TiDB](/docs/mysql/connect-tidb)
- [AWS Data API MySQL](/docs/mysql/connect-aws-data-api-mysql)
- [Bun SQL](/docs/mysql/connect-bun-sql)
- [Drizzle HTTP proxy](/docs/mysql/connect-drizzle-proxy)
Source: https://orm.drizzle.team/docs/mysql/connect-planetscale
import Npm from "@mdx/Npm.astro";
import Callout from "@mdx/Callout.astro";
import Tabs from "@mdx/Tabs.astro";
import Tab from "@mdx/Tab.astro";
import AnchorCards from "@mdx/AnchorCards.astro";
import WhatsNextMySQL from "@mdx/WhatsNextMySQL.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> PlanetScale MySQL
- Database [connection basics](/docs/mysql/connect-overview) with Drizzle
- PlanetScale database - [website](https://planetscale.com/docs)
- PlanetScale http driver - [GitHub](https://github.com/planetscale/database-js)
- Drizzle MySQL drivers - [docs](/docs/mysql/get-started-mysql)
PlanetScale offers both MySQL (Vitess) and PostgreSQL databases. This page covers connecting to PlanetScale MySQL.
For PlanetScale Postgres, see the [PlanetScale Postgres connection guide](/docs/connect-planetscale-postgres).
With Drizzle ORM you can access PlanetScale MySQL over http
through their official **[`database-js`](https://github.com/planetscale/database-js)**
driver from serverless and serverfull environments with our `drizzle-orm/planetscale-serverless` package.
You can also access PlanetScale MySQL through TCP with `mysql2` driver â **[see here.](/docs/mysql/get-started-mysql)**
#### Step 1 - Install packages
drizzle-orm@rc @planetscale/database
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy"
import { drizzle } from "drizzle-orm/planetscale-serverless";
const db = drizzle({ connection: {
host: process.env["DATABASE_HOST"],
username: process.env["DATABASE_USERNAME"],
password: process.env["DATABASE_PASSWORD"],
}});
const response = await db.select().from(...)
```
If you need to provide your existing driver
```typescript copy"
import { drizzle } from "drizzle-orm/planetscale-serverless";
import { Client } from "@planetscale/database";
const client = new Client({
host: process.env["DATABASE_HOST"],
username: process.env["DATABASE_USERNAME"],
password: process.env["DATABASE_PASSWORD"],
});
const db = drizzle({ client });
```
Make sure to checkout the PlanetScale official **[MySQL courses](https://planetscale.com/courses/mysql-for-developers)**,
we think they're outstanding đ
#### What's next?
Source: https://orm.drizzle.team/docs/mysql/connect-tidb
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Tabs from '@mdx/Tabs.astro';
import Tab from '@mdx/Tab.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import WhatsNextMySQL from "@mdx/WhatsNextMySQL.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> TiDB Serverless
- Database [connection basics](/docs/mysql/connect-overview) with Drizzle
- TiDB database - [website](https://docs.pingcap.com/)
- TiDB HTTP Driver - [website](https://docs.pingcap.com/developer/serverless-driver)
- Drizzle MySQL drivers - [docs](/docs/mysql/get-started-mysql)
According to the **[official website](https://www.pingcap.com/tidb-serverless/)**,
TiDB Serverless is a fully-managed, autonomous DBaaS with split-second cluster provisioning and consumption-based pricing.
TiDB Serverless is compatible with MySQL, so you can use [MySQL connection guide](/docs/mysql/get-started-mysql) to connect to it.
TiDB Serverless provides an [HTTP driver](https://docs.pingcap.com/developer/serverless-driver/) for edge environments. It is natively supported by Drizzle ORM via `drizzle-orm/tidb-serverless` package.
#### Step 1 - Install packages
drizzle-orm@rc @tidbcloud/serverless
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/tidb-serverless';
const db = drizzle({ connection: { url: process.env.TIDB_URL }});
const response = await db.select().from(...)
```
If you need to provide your existing driver:
```typescript copy"
import { connect } from '@tidbcloud/serverless';
import { drizzle } from 'drizzle-orm/tidb-serverless';
const client = connect({ url: process.env.TIDB_URL });
const db = drizzle({ client });
```
#### What's next?
Source: https://orm.drizzle.team/docs/mysql/custom-types
# Custom types
Custom types let you define your own column types with custom SQL data types and JS/DB value transforms
MySQL exposes `customType` from `drizzle-orm/mysql-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.
#### Int
```ts
import { customType, mysqlTable } from "drizzle-orm/mysql-core";
const customInt = customType<{ data: number }>({
dataType() {
return "int";
},
});
export const users = mysqlTable("users", {
id: customInt(),
});
```
#### Text
```ts
import { customType, mysqlTable } from 'drizzle-orm/mysql-core';
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});
export const users = mysqlTable("users", {
name: customText(),
});
```
#### Timestamp
```ts
import { customType, mysqlTable } from "drizzle-orm/mysql-core";
const customTimestamp = customType<{
data: Date;
driverData: string;
config: { fsp?: number };
configRequired: true;
}>({
dataType(config) {
const precision = typeof config.fsp !== "undefined" ? ` (${config.fsp})` : "";
return `timestamp${precision}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
});
export const users = mysqlTable("users", {
createdAt: customTimestamp("created_at", { fsp: 4 }),
});
```
Usage for all types will be same as defined functions in Drizzle ORM. For example:
```ts
const usersTable = mysqlTable("users", {
id: customInt().primaryKey(),
name: customText().notNull(),
createdAt: customTimestamp("created_at", { fsp: 4 })
.notNull()
.default(sql`now()`),
});
```
### TS-doc for type definitions
You can check `ts-doc` for types, param definition and examples
```ts
interface 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 returning for specific database data type
*
* Needed only in case driver's output and input for type differ
*
* Defaults to {@link driverData}
*/
driverOutput?: unknown;
/**
* Type helper, that represents what type database driver is accepting for specific database data type
*/
driverData?: unknown;
/**
* Type helper, that represents what type field returns after being aggregated to JSON
*/
jsonData?: unknown;
/**
* What config type should be used for {@link CustomTypeParams} `dataType` generation
*/
config?: Record;
/**
* 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;
}
interface CustomTypeParams {
/**
* 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 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-07 03:25:16.635";
* }
* ```
* to:
* ```
* {
* customField: new Date("2025-04-07 03:25:16.635");
* }
* ```
*/
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, 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
*
* 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: `binary`, `varbinary`, `time`, `datetime`, `decimal`, `float`, `bigint`
* @example
* For example, when using bigint we need to cast field to text to preserve data integrity
* ```
* forJsonSelect(identifier: SQL, sql: SQLGenerator): SQL {
* return sql`cast(${identifier} as char)`
* },
* ```
*
* This will change query from:
* ```
* SELECT
* json_build_object('bigint', `t`.`bigint`)
* FROM
* (
* SELECT
* `table`.`custom_bigint` AS `bigint`
* FROM
* `table`
* ) AS `t`
* ```
* to:
* ```
* SELECT
* json_build_object('bigint', `t`.`bigint`)
* FROM
* (
* SELECT
* cast(`table`.`custom_bigint` as char) 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) => SQL;
}
```
Source: https://orm.drizzle.team/docs/mysql/data-querying
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/mysql/sql-schema-declaration)
- How to connect to the database - [Connection Fundamentals](/docs/mysql/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.
```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
```ts
await db.insert(users).values({ email: 'user@gmail.com' })
```
```sql
INSERT INTO `users` (`email`) VALUES ('user@gmail.com')
```
```ts
await db.update(users)
.set({ email: 'user@gmail.com' })
.where(eq(users.id, 1))
```
```sql
UPDATE `users`
SET `email` = 'user@gmail.com'
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(like(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?
Source: https://orm.drizzle.team/docs/mysql/delete
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'));
```
### Limit
Use `.limit()` to add `limit` clause to the query - for example:
```typescript
await db.delete(users).where(eq(users.name, 'Dan')).limit(2);
```
```sql
delete from `users` where `users`.`name` = 'Dan' limit 2;
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.delete(users).where(eq(users.name, 'Dan')).orderBy(users.name);
await db.delete(users).where(eq(users.name, 'Dan')).orderBy(desc(users.name));
// order by multiple fields
await db.delete(users).where(eq(users.name, 'Dan')).orderBy(users.name, users.name2);
await db.delete(users).where(eq(users.name, 'Dan')).orderBy(asc(users.name), desc(users.name2));
```
```sql
delete from `users` where `users`.`name` = 'Dan' order by `users`.`name`;
delete from `users` where `users`.`name` = 'Dan' order by `users`.`name` desc;
delete from `users` where `users`.`name` = 'Dan' order by `users`.`name`, `users`.`name2`;
delete from `users` where `users`.`name` = 'Dan' order by `users`.`name` asc, `users`.`name2` desc;
```
Source: https://orm.drizzle.team/docs/mysql/drizzle-config-file
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](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-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: "mysql",
schema: "./src/schema.ts",
out: "./drizzle",
});
```
```js
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
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: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname",
},
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
},
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: "mysql",
schema: "./src/schema/*",
out: "./drizzle",
});
```
## ---
### `dialect`
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export` |
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
});
```
### `schema`
[`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 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",
});
```
## ---
### `dbCredentials`
Database connection credentials in a form of `url`,
`user:password@host:port/db` params or exceptions drivers() specific connection options.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "mysql",
dbCredentials: {
url: "mysql://user:password@host:port/db",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "mysql",
dbCredentials: {
host: "host",
port: 3306,
user: "user",
password: "password",
database: "dbname",
ssl: "...", // can be: string | SslOptions from mysql2
}
});
```
### `migrations`
When running `drizzle-kit migrate` - drizzle will records about
successfully applied migrations in your database in log table named `__drizzle_migrations`.
`migrations` config options lets you change both migrations log `table` name and `schema`.
| | |
| :------------ | :----------------- |
| type | `{ table: string }` |
| default | `{ table: "__drizzle_migrations" }` |
| commands | `migrate`, `push`, `pull` |
```ts
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` 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` |
```ts
import * as p from "drizzle-orm/mysql-core"
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
firstName: p.varchar("first-name", { length: 255 }),
lastName: p.varchar("LastName", { length: 255 }),
email: p.varchar("email", { length: 255 }),
phoneNumber: p.varchar("phone_number", { length: 255 }),
});
```
```sql
SHOW COLUMNS FROM users;
```
```
Field | Type
--------------+--------------
id | int
first-name | varchar(255)
LastName | varchar(255)
email | varchar(255)
phone_number | varchar(255)
```
```ts
import * as p from "drizzle-orm/mysql-core"
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
"first-name": p.varchar("first-name", { length: 255 }),
LastName: p.varchar("LastName", { length: 255 }),
email: p.varchar("email", { length: 255 }),
phone_number: p.varchar("phone_number", { length: 255 }),
});
```
```sql
SHOW COLUMNS FROM users;
```
```
Field | Type
--------------+--------------
id | int
first-name | varchar(255)
LastName | varchar(255)
email | varchar(255)
phone_number | varchar(255)
```
## ---
### `tablesFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/mysql/goodies#multi-project-schema).
`drizzle-kit push` and `drizzle-kit pull` will manage all tables by default.
You can configure the list of tables via `tablesFilter`.
`tablesFilter` 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 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: "mysql",
tablesFilter: ["users", "posts", "project1_*"],
});
```
## ---
### `verbose`
Print all SQL statements during `drizzle-kit push` command.
| | |
| :------------ | :----------------- |
| type | `boolean` |
| default | `false` |
| commands | `pull` |
```ts
export default defineConfig({
dialect: "mysql",
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: "mysql",
breakpoints: false,
});
```
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-check
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit check`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mysql/drizzle-kit-generate)
`drizzle-kit check` command lets you check consistency of your generated SQL migrations history.
That's extremely useful when you have multiple developers working on the project and
altering database schema on different branches - read more about [migrations for teams](/docs/mysql/kit-migrations-for-teams).
`drizzle-kit check` command requires you to specify `dialect` param,
you can provide it either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options
```ts {5}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
});
```
```shell
npx drizzle-kit check
```
```shell
npx drizzle-kit check --dialect=mysql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit check --config=drizzle-dev.config.ts
drizzle-kit check --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit check --dialect=mysql
drizzle-kit check --dialect=mysql --out=./migrations-folder
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-export
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit export`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
`drizzle-kit export` lets you export SQL representation of Drizzle schema and print in console SQL DDL representation on it.
Drizzle Kit `export` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Based on the current json snapshot it will generate SQL DDL statements
3. Output SQL DDL statements to console
It's designed to cover [codebase first](/docs/mysql/migrations) approach of managing Drizzle migrations.
You can export the SQL representation of the Drizzle schema, allowing external tools like Atlas to handle all the migrations for you
`drizzle-kit export` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit export
```
```shell
npx drizzle-kit export --dialect=mysql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 export --config=drizzle-dev.config.ts
drizzle-kit export --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of available configurations
`drizzle-kit export` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `--sql` | generating SQL representation of Drizzle Schema |
By default, Drizzle Kit outputs SQL files, but in the future, we want to support different formats
drizzle-kit export --sql=true
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
### Example
Example of how to export drizzle schema to console with Drizzle schema located in `./src/schema.ts`
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ configs
â â đ drizzle.config.ts
â đ src
â â đ schema.ts
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
});
```
```ts filename='schema.ts'
import { mysqlTable, int, text } from 'drizzle-orm/mysql-core'
export const users = mysqlTable('users', {
id: int('id').primaryKey().autoincrement(),
email: text('email').notNull(),
name: text('name')
});
```
Now let's run
```shell
npx drizzle-kit export --config=./configs/drizzle.config.ts
```
And it will successfully output SQL representation of drizzle schema
```bash
CREATE TABLE "users" (
`id` int AUTO_INCREMENT PRIMARY KEY,
`email` varchar(255) NOT NULL,
"name" text
);
```
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-generate
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit generate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
`drizzle-kit generate` lets you generate SQL migrations based on your Drizzle schema upon declaration or on subsequent schema changes.
Drizzle Kit `generate` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. It will read through your previous migrations folders and compare current json snapshot to the most recent one
3. Based on json differences it will generate SQL migrations
4. Save `migration.sql` and `snapshot.json` in migration folder under current timestamp
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ migration.sql
â â đ snapshot.json
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255),
`email` varchar(255),
CONSTRAINT `email_unique` UNIQUE INDEX(`email`)
);
```
It's designed to cover [code first](/docs/mysql/migrations) approach of managing Drizzle migrations.
You can apply generated migrations using [`drizzle-kit migrate`](/docs/mysql/drizzle-kit-migrate), using drizzle-orm's `migrate()`,
using external migration tools like [bytebase](https://www.bytebase.com/) or running migrations yourself directly on the database.
`drizzle-kit generate` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit generate
```
```shell
npx drizzle-kit generate --dialect=mysql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Custom migration file name
You can set custom migration file names by providing `--name` CLI option
```shell
npx drizzle-kit generate --name=init
```
```plaintext {3}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ snapshot.json
â â đ migration.sql
â đ src
â âĻ
```
### Multiple configuration files in one project
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
```
### Custom migrations
You can generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding. Extended docs on custom migrations - [see here](/docs/mysql/kit-custom-migrations)
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed-users/migration.sql
INSERT INTO `users` (`name`) VALUES('Dan');
INSERT INTO `users` (`name`) VALUES('Andrew');
INSERT INTO `users` (`name`) VALUES('Dandrew');
```
### Ignore conflicts
In case you need `generate` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit generate --ignore-conflicts
```
### Extended list of available configurations
`drizzle-kit generate` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `custom` | generate empty SQL for custom migration |
| `name` | generate migration with custom name |
| `ignore-conflicts` | skip commutativity conflict checks, default is `false` |
drizzle-kit generate --name=init
drizzle-kit generate --name=seed_users --custom
drizzle-kit generate --ignore-conflicts
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `out` | | Migrations output folder, default is `./drizzle` |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `breakpoints` | | SQL statements breakpoints, default is `true` |
### Extended example
Example of how to create a custom MySQL migration file named `0001_seed-users.sql`
with Drizzle schema located in `./src/schema.ts` and migrations folder named `./migrations` instead of default `./drizzle`.
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ migrations
â đ configs
â â đ drizzle.config.ts
â đ src
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
out: "./migrations",
});
```
Now let's run
```shell
npx drizzle-kit generate --config=./configs/drizzle.config.ts --name=seed-users --custom
```
And it will successfully generate
```plaintext {5}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed-users/migration.sql
INSERT INTO `users` (`name`) VALUES('Dan');
INSERT INTO `users` (`name`) VALUES('Andrew');
INSERT INTO `users` (`name`) VALUES('Dandrew');
```
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-migrate
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit migrate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mysql/drizzle-kit-generate)
`drizzle-kit migrate` lets you apply SQL migrations generated by [`drizzle-kit generate`](/docs/mysql/drizzle-kit-generate).
It's designed to cover [code first(option 3)](/docs/mysql/migrations) approach of managing Drizzle migrations.
Drizzle Kit `migrate` command triggers a sequence of events:
1. Reads through migration folder and read all `.sql` migration files
2. Connects to the database and fetches entries from drizzle migrations log table
3. Based on previously applied migrations it will decide which new migrations to run
4. Runs SQL migrations and logs applied migrations to drizzle migrations table
```plaintext
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ 20242409135510_delicate_professor_xavie
â âĻ
```
```plaintext
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. reads migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
`drizzle-kit migrate` command requires you to specify both `dialect` and database connection credentials,
you can provide them either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit migrate
```
```shell
npx drizzle-kit migrate --dialect=mysql --url=mysql://user:password@host:port/dbname
```
### Applied migrations log in the database
Upon running migrations Drizzle Kit will persist records about successfully applied migrations in your database.
It will store them in migrations log table named `__drizzle_migrations`.
You can customise the **table** name of that log table via drizzle config file:
```ts filename="drizzle.config.ts" {8}
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname"
},
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
},
});
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended example
Let's generate SQL migration and apply it to our database using `drizzle-kit generate` and `drizzle-kit migrate` commands
```plaintext
đĻ
â đ drizzle
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname"
},
migrations: {
table: 'journal',
},
});
```
```ts
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
})
```
Now let's run
```shell
npx drizzle-kit generate --name=init
```
it will generate
```plaintext {4}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â âĻ
```
```sql
-- ./20242409125510_init/0000_init.sql
CREATE TABLE `users`(
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255)
)
```
Now let's run
```shell
npx drizzle-kit migrate
```
and our SQL migration is now successfully applied to the database â
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-pull
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 Prerequisites from "@mdx/Prerequisites.astro";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import Npm from "@mdx/Npm.astro"
import Npx from "@mdx/Npx.astro"
# `drizzle-kit pull`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file) docs
`drizzle-kit pull` lets you literally pull(introspect) your existing database schema and generate `schema.ts` drizzle schema file,
it is designed to cover [database first](/docs/mysql/migrations) approach of Drizzle migrations.
When you run Drizzle Kit `pull` command it will:
1. Pull database schema(DDL) from your existing database
2. Generate `schema.ts` drizzle schema file and save it to `out` folder
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââ
â â <--- CREATE TABLE `users` (
ââââââââââââââââââââââââââââ â â `id` int auto_increment primary key,
â ~ drizzle-kit pull â â â `name` varchar(255),
âââŦâââââââââââââââââââââââââ â DATABASE â `email` varchar(255) unique
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable(
"users",
{
id: p.int().autoincrement().primaryKey(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }),
},
(table) => [p.uniqueIndex("email").on(table.email)]
);
```
It is a great approach if you need to manage database schema outside of your TypeScript project or
you're using database, which is managed by somebody else.
`drizzle-kit pull` requires you to specify `dialect` and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname",
},
});
```
```shell
npx drizzle-kit pull
```
```shell
npx drizzle-kit pull --dialect=mysql --url=mysql://user:password@host:3306/dbname
```
### Multiple configuration files in one project
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 pull --config=drizzle-dev.config.ts
drizzle-kit pull --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Initial pull
You can use the `--init` flag to mark the pulled schema as an applied migration in your database,
so that all subsequent migrations are diffed against the initial one
```shell
npx drizzle-kit pull --init
```
### Including tables
`drizzle-kit pull` will introspect all MySQL tables by default.
You can configure the list of tables via `tablesFilter`.
| | |
| :------------- | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
Let's configure drizzle-kit to introspect **all tables** in the connected MySQL database.
```ts filename="drizzle.config.ts" {8}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname",
},
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit pull
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `out` | | Migrations output folder path, default is `./drizzle` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `introspect-casing` | | Strategy for JS keys creation in columns, tables, etc. `preserve` `camel` |
| `tablesFilter` | | Table name filter |
drizzle-kit pull --dialect=mysql --url=mysql://user:password@host:3306/dbname
drizzle-kit pull --dialect=mysql --tablesFilter='user*' url=mysql://user:password@host:3306/dbname
{/* TODO update gifs */}

Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-push
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import DriversExamples from "@mdx/DriversExamples.mdx"
# `drizzle-kit push`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file) docs
`drizzle-kit push` lets you literally push your schema and subsequent schema changes directly to the
database while omitting SQL files generation, it's designed to cover [code first](/docs/mysql/migrations)
approach of Drizzle migrations.
When you run Drizzle Kit `push` command it will:
1. Read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Pull(introspect) database schema
3. Based on differences between those two it will generate SQL migrations
4. Apply SQL migrations to the database
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
});
```
```
âââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´âââââââââââââââââ
create table `users` (`id` int AUTO_INCREMENT PRIMARY KEY, `name` varchar(255));
```
It's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
It pairs exceptionally well with blue/green deployment strategy and serverless databases like
[Planetscale](https://planetscale.com), [Neon](https://neon.tech), [Turso](https://turso.tech/drizzle) and others.
`drizzle-kit push` requires you to specify `dialect`, path to the `schema` file(s) and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname",
},
});
```
```shell
npx drizzle-kit push
```
```shell
npx drizzle-kit push --dialect=mysql --schema=./src/schema.ts --url=mysql://user:password@host:3306/dbname
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 push --config=drizzle-dev.config.ts
drizzle-kit push --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Including tables
`drizzle-kit push` will manage all MySQL tables by default.
You can configure the list of tables via `tablesFilter`.
| | |
| :------------- | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
Let's configure drizzle-kit to operate with **all tables** in the connected MySQL database.
```ts filename="drizzle.config.ts" {9}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname",
},
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit push
```
### Extended list of configurations
`drizzle-kit push` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `verbose` | print all SQL statements prior to execution |
| `explain` | print the planned SQL changes, without applying them (dry run) |
| `force` | auto-accept all data-loss statements |
drizzle-kit push --explain --verbose --force
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `tablesFilter` | | Table name filter |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit push dialect=mysql schema=src/schema.ts url=mysql://user:password@host:3306/dbname
drizzle-kit push dialect=mysql schema=src/schema.ts --tablesFilter='user*' url=mysql://user:password@host:3306/dbname
### Extended example
Let's declare drizzle schema in the project and push it to the database via `drizzle-kit push` command
```plaintext
đĻ
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname"
},
});
```
```ts
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
})
```
Now let's run
```shell
npx drizzle-kit push
```
it will pull existing(empty) schema from the database and generate SQL migration and apply it under the hood
```sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255)
);
```
DONE â
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-studio
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit studio`
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
- Drizzle Studio, our database browser - [read here](/drizzle-studio/overview)
`drizzle-kit studio` command spins up a server for [Drizzle Studio](/drizzle-studio/overview) hosted on [local.drizzle.studio](https://local.drizzle.studio).
It requires you to specify database connection credentials via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file.
By default it will start a Drizzle Studio server on `127.0.0.1:4983`
```ts {6}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
dbCredentials: {
url: "mysql://user:password@host:3306/dbname"
},
});
```
```shell
npx drizzle-kit studio
```
### Configuring `host` and `port`
By default Drizzle Studio server starts on `127.0.0.1:4983`,
you can config `host` and `port` via CLI options
drizzle-kit studio --port=3000
drizzle-kit studio --host=0.0.0.0
drizzle-kit studio --host=0.0.0.0 --port=3000
### Logging
You can enable logging of every SQL statement by providing `verbose` flag
drizzle-kit studio --verbose
### Safari and Brave support
Safari and Brave block access to localhost by default.
You need to install [mkcert](https://github.com/FiloSottile/mkcert) and generate self-signed certificate:
1. Follow the mkcert [installation steps](https://github.com/FiloSottile/mkcert#installation)
2. Run `mkcert -install`
3. Restart your `drizzle-kit studio`
### Embeddable version of Drizzle Studio
While hosted version of Drizzle Studio for local development is free forever and meant to just enrich Drizzle ecosystem,
we have a B2B offering of an embeddable version of Drizzle Studio for businesses.
**Drizzle Studio component** - is a pre-bundled framework agnostic web component of Drizzle Studio
which you can embed into your UI `React` `Vue` `Svelte` `VanillaJS` etc.
That is an extremely powerful UI element that can elevate your offering
if you provide Database as a SaaS or a data centric SaaS solutions based
on SQL or for private non-customer facing in-house usage.
Database platforms using Drizzle Studio:
- [Turso](https://turso.tech/), our first customers since Oct 2023!
- [Neon](https://neon.tech/), [launch post](https://neon.tech/docs/changelog/2024-05-24)
- [Hydra](https://www.hydra.so/)
Data centric platforms using Drizzle Studio:
- [Nuxt Hub](https://hub.nuxt.com/), SÊbastien Chopin's [launch post](https://x.com/Atinux/status/1768663789832929520)
- [Deco.cx](https://deco.cx/)
You can read a detailed overview [here](https://www.npmjs.com/package/@drizzle-team/studio) and
if you're interested - hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Drizzle Studio chrome extension
Drizzle Studio [chrome extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd)
lets you browse your [PlanetScale](https://planetscale.com),
[Cloudflare](https://developers.cloudflare.com/d1/) and [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres)
serverless databases directly in their vendor admin panels!
### Limitations
Our hosted version Drizzle Studio is meant to be used for local development and not meant to be used on remote (VPS, etc).
If you want to deploy Drizzle Studio to your VPS - we have an alpha version of Drizzle Studio Gateway,
hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Is it open source?
No. Drizzle ORM and Drizzle Kit are fully open sourced, while Studio is not.
Drizzle Studio for local development is free to use forever to enrich Drizzle ecosystem,
open sourcing one would've break our ability to provide B2B offerings and monetise it, unfortunately.
Source: https://orm.drizzle.team/docs/mysql/drizzle-kit-up
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit up`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mysql/drizzle-kit-generate)
`drizzle-kit up` command lets you upgrade drizzle schema snapshots to a newer version.
It's required whenever we introduce breaking changes to the json snapshots of the schema and upgrade the internal version.
`drizzle-kit up` command requires you to specify `dialect` param,
you can provide it either via [drizzle.config.ts](/docs/mysql/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
});
```
```shell
npx drizzle-kit up
```
```shell
npx drizzle-kit up --dialect=mysql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/mysql/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit up --dialect=mysql
drizzle-kit up --dialect=mysql --out=./migrations-folder
{/* TODO update gifs */}

Source: https://orm.drizzle.team/docs/mysql/dynamic-query-building
import Callout from '@mdx/Callout.astro';
# Dynamic query building
By default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once.
For example, in a `SELECT` statement there might only be one `WHERE` clause, so you can only invoke `.where()` once:
```ts
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // â Type error - where() can only be invoked once
```
In the previous ORM versions, when such restrictions weren't implemented, this example in particular was a source of confusion for many users, as they expected the query builder to "merge" multiple `.where()` calls into a single condition.
This behavior is useful for conventional query building, i.e. when you create the whole query at once.
However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it.
To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once.
To enable it, you need to call `.$dynamic()` on a query builder.
Let's see how it works by implementing a simple `withPagination` function that adds `LIMIT` and `OFFSET` clauses to a query based on the provided page number and an optional page size:
```ts
function withPagination(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // â Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // â
OK
```
Note that the `withPagination` function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:
```ts
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
This is possible, because `MySqlSelect` and other similar types are specifically designed to be used in dynamic query building. They can only be used in dynamic mode.
Here is the list of all types that can be used as generic parameters in dynamic query building:
| Query | Type |
|:-------|:----------------------------------------------|
| Select | `MySqlSelect` / `MySqlSelectQueryBuilder` |
| Insert | `MySqlInsert` |
| Update | `MySqlUpdate` |
| Delete | `MySqlDelete` |
The `...QueryBuilder` types are for usage with [standalone query builder
instances](/docs/mysql/goodies#standalone-query-builder). DB query builders are
subclasses of them, so you can use them as well.
```ts
import { QueryBuilder } from 'drizzle-orm/mysql-core';
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
Source: https://orm.drizzle.team/docs/mysql/effect-schema
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# effect-schema
Allows you to generate [effect](https://effect.website/) schemas from Drizzle ORM schemas.
**Features**
- Create a select schema for tables and views.
- Create insert and update schemas for tables.
- Supported dialect: MySQL.
#### Usage
```ts
import { int, mysqlTable, text, timestamp } from 'drizzle-orm/mysql-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/effect-schema';
import { Effect, Schema } from "effect";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
email: text().notNull(),
role: text({ enum: ['admin', 'user'] }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// Schema for inserting a user - can be used to validate API requests
const UserInsert = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const UserUpdate = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const UserSelect = createSelectSchema(users);
// Overriding the fields
const UserInsert = createInsertSchema(users, {
role: Schema.String,
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const UserInsert = createInsertSchema(users, {
id: (schema) => schema.check(Schema.isGreaterThanOrEqualTo(0)),
role: Schema.String,
});
// Usage
const program = Effect.gen(function*() {
const parsedUser = yield* Schema.decodeUnknownEffect(UserInsert)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
});
```
Source: https://orm.drizzle.team/docs/mysql/extensions
import Callout from '@mdx/Callout.astro';
Currently, there are no MySQL extensions natively supported by Drizzle. Once those are added, we will have them here!
Source: https://orm.drizzle.team/docs/mysql/generated-columns
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Generated Columns
1. Virtual (or non-persistent) Generated Columns: These columns are computed dynamically whenever they are queried. They do not occupy storage space in the database.
2. Stored (or persistent) Generated Columns: These columns are computed when a row is inserted or updated and their values are stored in the database. This allows them to be indexed and can improve query performance since the values do not need to be recomputed for each query.
#### Database side
**Types**: `VIRTUAL`, `STORED`
When no type is specified, `VIRTUAL` is used by default
**How It Works**
- Defined with an expression in the table schema.
- Virtual columns are computed during read operations.
- Stored columns are computed during write operations and stored.
**Capabilities**
- Used in SELECT, INSERT, UPDATE, and DELETE statements.
- Can be indexed, both virtual and stored.
- Can specify NOT NULL and other constraints.
**Limitations**
- Cannot directly insert or update values in a generated column
For more info, please check [MySQL Alter Generated](https://dev.mysql.com/doc/refman/8.4/en/alter-table-generated-columns.html) docs and [MySQL create generated](https://dev.mysql.com/doc/refman/8.4/en/create-table-generated-columns.html) docs
#### Drizzle side
In Drizzle you can specify `.generatedAlwaysAs()` function on any column type and add a supported sql query,
that will generate this column data for you.
#### Features
This function can accept generated expression in 2 ways:
**`sql`** tag - if you want drizzle to escape some values for you
```ts {2,6}
export const test_1 = mysqlTable("test_1", {
generatedName: text("gen_name").generatedAlwaysAs(sql`'hello "world"!'`),
});
export const test_2 = mysqlTable("test_2", {
generatedName: text("gen_name").generatedAlwaysAs(sql`'hello "world"!'`, { mode: "stored" }),
});
```
```sql {2,6}
CREATE TABLE `test_1` (
`gen_name` text GENERATED ALWAYS AS ('hello "world"!') VIRTUAL
);
CREATE TABLE `test_2` (
`gen_name` text GENERATED ALWAYS AS ('hello "world"!') STORED
);
```
**`callback`** - if you need to reference columns from a table
```ts {4,10}
export const test_1 = mysqlTable("test_1", {
name: text("first_name"),
generatedName: text("gen_name")
.generatedAlwaysAs((): SQL => sql`concat('hi, ', ${test_1.name}, '!')`),
});
export const test_2 = mysqlTable("test_2", {
name: text("first_name"),
generatedName: text("gen_name")
.generatedAlwaysAs((): SQL => sql`concat('hi, ', ${test_2.name}, '!')`, { mode: "stored" }),
});
```
```sql {3,8}
CREATE TABLE `test_1` (
`first_name` text,
`gen_name` text GENERATED ALWAYS AS (concat('hi, ', `test_1`.`first_name`, '!')) VIRTUAL
);
CREATE TABLE `test_2` (
`first_name` text,
`gen_name` text GENERATED ALWAYS AS (concat('hi, ', `test_2`.`first_name`, '!')) STORED
);
```
#### Limitations
Drizzle Kit will also have limitations for `push` command:
1. You can't change the generated constraint expression and type using `push`. Drizzle-kit will ignore this change. To make it work, you would need to `drop the column`, `push`, and then `add a column with a new expression`. This was done due to the complex mapping from the database side, where the schema expression will be modified on the database side and, on introspection, we will get a different string. We can't be sure if you changed this expression or if it was changed and formatted by the database. As long as these are generated columns and `push` is mostly used for prototyping on a local database, it should be fast to `drop` and `create` generated columns. Since these columns are `generated`, all the data will be restored
2. `generate` should have no limitations
```typescript copy
export const users = mysqlTable("users", {
id: int(),
id2: int(),
name: text(),
storedGenerated: text("stored_gen").generatedAlwaysAs(
(): SQL => sql`concat(${users.name}, ' ', 'hello')`,
{ mode: "stored" }
),
virtualGenerated: text("virtual_gen").generatedAlwaysAs(
(): SQL => sql`concat(${users.name}, ' ', 'hello')`,
{ mode: "virtual" }
),
});
```
```sql
CREATE TABLE `users` (
`id` int,
`id2` int,
`name` text,
`stored_gen` text GENERATED ALWAYS AS (concat(`users`.`name`, ' ', 'hello')) STORED,
`virtual_gen` text GENERATED ALWAYS AS (concat(`users`.`name`, ' ', 'hello')) VIRTUAL
);
```
Source: https://orm.drizzle.team/docs/mysql/get-started-mysql
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextMySQL from "@mdx/WhatsNextMySQL.astro";
# Drizzle \<\> MySQL
To use Drizzle with a MySQL database, you should use the `mysql2` driver
According to the **[official website](https://github.com/sidorares/node-mysql2)**,
`mysql2` is a MySQL client for Node.js with focus on performance.
Drizzle ORM natively supports `mysql2` with `drizzle-orm/mysql2` package.
#### Step 1 - Install packages
drizzle-orm@rc mysql2
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import { drizzle } from "drizzle-orm/mysql2";
const db = drizzle(process.env.DATABASE_URL);
const response = await db.select().from(...)
```
```typescript copy
import { drizzle } from "drizzle-orm/mysql2";
// You can specify any property from the mysql2 connection options
const db = drizzle({ connection:{ uri: process.env.DATABASE_URL }});
const response = await db.select().from(...)
```
If you need to provide your existing driver:
```typescript copy
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
const connection = await mysql.createConnection({
host: "host",
user: "user",
database: "database",
...
});
const db = drizzle({ client: connection });
```
```typescript copy
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
const poolConnection = mysql.createPool({
host: "host",
user: "user",
database: "database",
...
});
const db = drizzle({ client: poolConnection });
```
For the built in `migrate` function with DDL migrations we and drivers strongly encourage you to use single `client` connection.
For querying purposes feel free to use either `client` or `pool` based on your business demands.
#### What's next?
Source: https://orm.drizzle.team/docs/mysql/goodies
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
## Type API
To retrieve a type from your table schema for `select` and `insert` queries, you can make use of our type helpers.
```ts
import { int, text, mysqlTable } from 'drizzle-orm/mysql-core';
import { type InferSelectModel, type InferInsertModel } from 'drizzle-orm'
const users = mysqlTable('users', {
id: int().primaryKey(),
name: text().notNull(),
});
type SelectUser = typeof users.$inferSelect;
type InsertUser = typeof users.$inferInsert;
// or
type SelectUser = InferSelectModel;
type InsertUser = InferInsertModel;
```
## Logging
To enable default query logging, just pass `{ logger: true }` to the `drizzle` initialization function:
```typescript copy
import { drizzle } from 'drizzle-orm/mysql2';
const db = drizzle({ logger: true });
```
You can change the logs destination by creating a `DefaultLogger` instance and providing a custom `writer` to it:
```typescript copy
import { DefaultLogger, type LogWriter } from "drizzle-orm/logger";
import { drizzle } from "drizzle-orm/mysql2";
class MyLogWriter implements LogWriter {
write(message: string) {
// Write to file, stdout, etc.
}
}
const logger = new DefaultLogger({ writer: new MyLogWriter() });
const db = drizzle(process.env.DB_URL, { logger });
```
You can also create a custom logger:
```typescript copy
import { type Logger } from 'drizzle-orm/logger';
import { drizzle } from 'drizzle-orm/mysql2';
class MyLogger implements Logger {
logQuery(query: string, params: unknown[]): void {
console.log({ query, params });
}
}
const db = drizzle(process.env.DB_URL, { logger: new MyLogger() });
```
## Multi-project schema
**Table creator** API lets you define customize table names.
It's very useful when you need to keep schemas of different projects in one database.
```ts {3}
import { int, text, mysqlTableCreator } from 'drizzle-orm/mysql-core';
const mysqlTable = mysqlTableCreator((name) => `project1_${name}`);
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: text().notNull(),
});
```
```ts {10}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/schema/*",
out: "./drizzle",
dialect: "mysql",
dbCredentials: {
url: process.env.DATABASE_URL,
}
tablesFilter: ["project1_*"],
});
```
You can apply multiple `or` filters:
```ts
tablesFilter: ["project1_*", "project2_*"]
```
## Printing SQL query
You can print SQL queries with `db` instance or by using **[`standalone query builder`](#standalone-query-builder)**.
```typescript copy
const query = db
.select({ id: users.id, name: users.name })
.from(users)
.groupBy(users.id)
.toSQL();
// query:
{
sql: 'select `id`, `name` from `users` group by `users`.`id`',
params: [],
}
```
## Raw SQL queries execution
If you have some complex queries to execute and `drizzle-orm` can't handle them yet,
you can use the `db.execute` method to execute raw `parametrized` queries.
```typescript copy
import { ..., type MySqlRawQueryResult } from "drizzle-orm/mysql2";
const userId = 11;
const statement = sql`select * from ${users} where ${users.id} = ${userId}`;
const res: MySqlRawQueryResult = await db.execute(statement);
```
## Standalone query builder
Drizzle ORM provides a standalone query builder that allows you to build queries
without creating a database instance and get generated SQL.
```typescript copy
import { QueryBuilder } from 'drizzle-orm/mysql-core';
const qb = new QueryBuilder();
const query = qb.select().from(users).where(eq(users.name, 'Dan'));
const { sql, params } = query.toSQL();
```
## Get typed columns
You can get a typed columns map,
very useful when you need to omit certain columns upon selection.
```ts
import { getColumns } from "drizzle-orm";
import { user } from "./schema";
const { password, role, ...rest } = getColumns(user);
await db.select({ ...rest }).from(user);
```
```ts
import { int, text, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
});
```
## Get table information
```ts copy
import { getTableConfig, mysqlTable } from 'drizzle-orm/mysql-core';
export const table = mysqlTable(...);
const {
columns,
indexes,
foreignKeys,
checks,
primaryKeys,
name,
schema,
} = getTableConfig(table);
```
## Compare objects types (instanceof alternative)
You can check if an object is of a specific Drizzle type using the `is()` function.
You can use it with any available type in Drizzle.
You should always use `is()` instead of `instanceof`
**Few examples**
```ts
import { Column, is } from 'drizzle-orm';
if (is(value, Column)) {
// value's type is narrowed to Column
}
```
### Mock Driver
This API is a successor to an undefined `drizzle({} as any)` API which we've used internally in Drizzle tests and rarely recommended to external developers.
We decided to build and expose a proper API, every `drizzle` driver now has `drizzle.mock()`:
```ts
import { drizzle } from "drizzle-orm/mysql2";
const db = drizzle.mock();
```
you can provide schema if necessary for types
```ts
import { drizzle } from "drizzle-orm/mysql2";
import { relations } from "./relations"
const db = drizzle.mock({ relations });
```
Source: https://orm.drizzle.team/docs/mysql/guides/count-rows
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
- Get started with [MySQL](/docs/mysql/get-started-mysql)
- [Select statement](/docs/mysql/select)
- [Filters](/docs/mysql/operators) and [sql operator](/docs/mysql/sql)
- [Aggregations](/docs/mysql/select#aggregations) and [Aggregation helpers](/docs/mysql/select#aggregations-helpers)
- [Joins](/docs/mysql/joins)
To count all rows in table you can use `count()` function or `sql` operator like below:
```ts copy {6,9}
import { count, sql } from 'drizzle-orm';
import { products } from './schema';
const db = drizzle(...);
await db.select({ count: count() }).from(products);
// Under the hood, the count() function casts its result to a number at runtime.
await db.select({ count: sql`count(*)`.mapWith(Number) }).from(products);
```
```ts
// result type
type Result = {
count: number;
}[];
```
```sql
select count(*) from products;
```
```ts
import { int, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
export const products = mysqlTable('products', {
id: int('id').primaryKey().autoincrement(),
name: varchar('name', { length: 255 }).notNull(),
discount: int('discount'),
price: int('price').notNull(),
});
```
To count rows where the specified column contains non-NULL values you can use `count()` function with a column:
```ts copy {1}
await db.select({ count: count(products.discount) }).from(products);
```
```ts
// result type
type Result = {
count: number;
}[];
```
```sql
select count(`discount`) from products;
```
Drizzle has simple and flexible API, which lets you create your custom solutions. In MySQL, `count()` can be returned as a string by the driver, so cast it to a number when you need a numeric result:
```ts copy {5,7,11,12}
import { AnyColumn, sql } from 'drizzle-orm';
const customCount = (column?: AnyColumn) => {
if (column) {
return sql`cast(count(${column}) as unsigned)`; // In MySQL cast to unsigned integer
} else {
return sql`cast(count(*) as unsigned)`; // In MySQL cast to unsigned integer
}
};
await db.select({ count: customCount() }).from(products);
await db.select({ count: customCount(products.discount) }).from(products);
```
```sql
select cast(count(*) as unsigned) from products;
select cast(count(`discount`) as unsigned) from products;
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `number`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a number), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/mysql/sql#sqlmapwith) method.
To count rows that match a condition you can use `.where()` method:
```ts copy {4,6}
import { count, gt } from 'drizzle-orm';
await db
.select({ count: count() })
.from(products)
.where(gt(products.price, 100));
```
```sql
select count(*) from products where price > 100
```
This is how you can use `count()` function with joins and aggregations:
```ts copy {8,11,12,13}
import { count, eq } from 'drizzle-orm';
import { countries, cities } from './schema';
// Count cities in each country
await db
.select({
country: countries.name,
citiesCount: count(cities.id),
})
.from(countries)
.leftJoin(cities, eq(countries.id, cities.countryId))
.groupBy(countries.id)
.orderBy(countries.name);
```
```sql
select countries.name, count(`cities`.`id`) from countries
left join cities on countries.id = cities.country_id
group by countries.id
order by countries.name;
```
```ts
import { int, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
export const countries = mysqlTable('countries', {
id: int('id').primaryKey().autoincrement(),
name: varchar('name', { length: 255 }).notNull(),
});
export const cities = mysqlTable('cities', {
id: int('id').primaryKey().autoincrement(),
name: varchar('name', { length: 255 }).notNull(),
countryId: int('country_id').notNull().references(() => countries.id),
});
```
Source: https://orm.drizzle.team/docs/mysql/indexes-constraints
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Indexes & Constraints
## Constraints
SQL constraints are the rules enforced on table columns. They are used to prevent invalid data from being entered into the database.
This ensures the accuracy and reliability of your data in the database.
### Default
The `DEFAULT` clause specifies a default value to use for the column if no value 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 { int, time, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable("table", {
int: int().default(42),
time: time().default(sql`cast("14:06:10" AS TIME)`),
});
```
```sql
CREATE TABLE `table` (
`int` int DEFAULT 42,
`time` time DEFAULT (cast("14:06:10" AS TIME))
);
```
### Not null
By default, a column can hold **NULL** values. The `NOT NULL` constraint enforces a column to **NOT** accept **NULL** values.
This enforces a field to always contain a value, which means that you cannot insert a new record,
or update a record without adding a value to this field.
```typescript
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const table = mysqlTable('table', {
int: int().notNull(),
});
```
```sql
CREATE TABLE `table` (
`int` int NOT NULL
);
```
### Unique
The `UNIQUE` constraint ensures that all values in a column are different.
Both the `UNIQUE` and `PRIMARY KEY` constraints provide a guarantee for uniqueness for a column or set of columns.
A `PRIMARY KEY` constraint automatically has a `UNIQUE` constraint.
You can have many `UNIQUE` constraints per table, but only one `PRIMARY KEY` constraint per table.
In MySQL, a `unique constraint` and a `unique index` are essentially the same thing.
Under the hood, MySQL implements a unique constraint as a unique index.
There is no separate constraint object like you might find in some other databases.
```sql
-- These three all produce the exact same result in MySQL:
ALTER TABLE users ADD CONSTRAINT uq_email UNIQUE (email);
ALTER TABLE users ADD UNIQUE INDEX uq_email (email);
CREATE UNIQUE INDEX uq_email ON users(email);
```
To drop a `unique constraint`, the same syntax is used as dropping an index: `ALTER TABLE users DROP INDEX uq_email;`. There's no separate `DROP CONSTRAINT statement` for it in MySQL.
That is why Drizzle always treats any `.unique()` as `UNIQUE INDEX`
```typescript
import { int, varchar, unique, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable('user', {
id: int().unique(),
});
export const table = mysqlTable('table', {
id: int().unique('custom_name'),
});
export const composite = mysqlTable('composite_example', {
id: int(),
name: varchar(, { length: 256 }),
}, (t) => [
unique().on(t.id, t.name),
unique('custom_name').on(t.id, t.name)
]);
```
```sql
CREATE TABLE `user` (
`id` int,
CONSTRAINT `id_unique` UNIQUE INDEX(`id`)
);
CREATE TABLE `table` (
`id` int,
CONSTRAINT `custom_name` UNIQUE INDEX(`id`)
);
CREATE TABLE `composite_example` (
`id` int,
`name` varchar(256),
CONSTRAINT `id_name_unique` UNIQUE INDEX(`id`,`name`),
CONSTRAINT `custom_name` UNIQUE INDEX(`id`,`name`)
);
```
### Check
The `CHECK` constraint is used to limit the value range that can be placed in a column.
If you define a `CHECK` constraint on a column it will allow only certain values for this column.
If you define a `CHECK` constraint on a table it can limit the values in certain columns based on values in other columns in the row.
```typescript copy
import { sql } from "drizzle-orm";
import { check, int, mysqlTable, text } from "drizzle-orm/mysql-core";
export const users = mysqlTable(
"users",
{
id: int().primaryKey(),
username: text().notNull(),
age: int(),
},
(table) => [
check("age_check1", sql`${table.age} > 21`)
]
);
```
```sql
CREATE TABLE `users` (
`id` int PRIMARY KEY,
`username` text NOT NULL,
`age` int,
CONSTRAINT `age_check1` CHECK(`users`.`age` > 21)
);
```
### Primary Key
The `PRIMARY KEY` constraint uniquely identifies each record in a table.
Primary keys must contain `UNIQUE` values, and cannot contain `NULL` values.
A table can have only **ONE** primary key; and in the table, this primary key can consist of single or multiple columns (fields).
```typescript
import { int, text, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int().autoincrement().primaryKey(),
})
export const table = mysqlTable("table", {
cuid: text().primaryKey(),
})
```
```sql
CREATE TABLE `user` (
`id` int AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE `table` (
`cuid` text PRIMARY KEY
);
```
### Composite Primary Key
Just like `PRIMARY KEY`, composite primary key uniquely identifies each record in a table using multiple fields.
Drizzle ORM provides a standalone `primaryKey` operator for that:
```typescript {18, 19}
import { int, text, primaryKey, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int("id").autoincrement().primaryKey(),
name: text("name"),
});
export const book = mysqlTable("book", {
id: int().autoincrement().primaryKey(),
name: text(),
});
export const booksToAuthors = mysqlTable("books_to_authors", {
authorId: int(),
bookId: int(),
}, (table) => [
primaryKey({ columns: [table.bookId, table.authorId] }),
// Or PK with custom name
primaryKey({ name: 'custom_name', columns: [table.bookId, table.authorId] })
]);
```
```sql {6}
...
CREATE TABLE `books_to_authors` (
`author_id` int,
`book_id` int,
PRIMARY KEY(`book_id`,`author_id`)
);
```
### Foreign key
The `FOREIGN KEY` constraint is used to prevent actions that would destroy links between tables.
A `FOREIGN KEY` is a field (or collection of fields) in one table, that refers to the `PRIMARY KEY` in another table.
The table with the foreign key is called the child table, and the table with the primary key is called the referenced or parent table.
Drizzle ORM provides several ways to declare foreign keys.
You can declare them in a column declaration statement:
```typescript {11}
import { int, text, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
});
export const book = mysqlTable("book", {
id: int().primaryKey().autoincrement(),
name: text(),
authorId: int("author_id").references(() => user.id)
});
```
If you want to do a self reference, due to a TypeScript limitations you will have to either explicitly
set return type for reference callback or use a standalone `foreignKey` operator.
```typescript {6,15-19}
import { int, text, foreignKey, type AnyMySqlColumn, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
parentId: int("parent_id").references((): AnyMySqlColumn => user.id),
});
// or
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
parentId: int("parent_id")
}, (table) => [
foreignKey({
columns: [table.parentId],
foreignColumns: [table.id],
name: "custom_fk"
})
]);
```
To declare multi-column foreign keys you can use a dedicated `foreignKey` operator:
```typescript copy {4-5,7,15-19}
import { int, text, primaryKey, foreignKey, mysqlTable, type AnyMySqlColumn } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
firstName: text("firstName"),
lastName: text("lastName"),
}, (table) => [
primaryKey({ columns: [table.firstName, table.lastName]})
]);
export const profile = mysqlTable("profile", {
id: int().autoincrement().primaryKey(),
userFirstName: text("user_first_name"),
userLastName: text("user_last_name"),
}, (table) => [
foreignKey({
columns: [table.userFirstName, table.userLastName],
foreignColumns: [user.firstName, user.lastName],
name: "custom_name"
})
]);
```
## Indexes
Drizzle ORM provides API for both `index` and `unique index` declaration:
```typescript copy {8-9}
import { int, text, index, uniqueIndex, mysqlTable } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int("id").primaryKey().autoincrement(),
name: text("name"),
email: text("email"),
}, (table) => [
index("name_idx").on(table.name),
uniqueIndex("email_idx").on(table.email),
]);
```
```sql {5-6}
CREATE TABLE `user` (
...
);
CREATE INDEX `name_idx` ON `user` (`name`);
CREATE UNIQUE INDEX `email_idx` ON `user` (`email`);
```
Drizzle ORM provides set of all params for index creation:
```typescript
// Index declaration reference
index("name")
.on(table.name)
.algorithm("default") // "default" | "copy" | "inplace"
.using("btree") // "btree" | "hash"
.lock("default") // "none" | "default" | "exclusive" | "shared"
```
Source: https://orm.drizzle.team/docs/mysql/insert
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
# SQL Insert
Drizzle ORM provides you the most SQL-like way to insert rows into the database tables.
Inserting data with Drizzle is extremely straightforward and sql-like. See for yourself:
```typescript copy
await db.insert(users).values({ name: 'Andrew' });
```
```sql
insert into `users` (`name`) values ('Andrew');
```
If you need insert type for a particular table you can use `typeof usersTable.$inferInsert` syntax.
```typescript copy
type NewUser = typeof users.$inferInsert;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);
```
## $returningId
MySQL itself doesn't have native support for `RETURNING` after using `INSERT`. There is only one way to do it for `primary keys` with `autoincrement` (or `serial`) types, where you can access `insertId` and `affectedRows` fields. We've prepared an automatic way for you to handle such cases with Drizzle and automatically receive all inserted IDs as separate objects
```ts
import { boolean, int, text, mysqlTable } from 'drizzle-orm/mysql-core';
const usersTable = mysqlTable('users', {
id: int().autoincrement().primaryKey(),
name: text().notNull(),
verified: boolean().notNull().default(false),
});
const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { id: number }[]
```
Also with Drizzle, you can specify a `primary key` with `$default` function that will generate custom primary keys at runtime. We will also return those generated keys for you in the `$returningId()` call
```ts
import { varchar, text, mysqlTable } from 'drizzle-orm/mysql-core';
import { createId } from '@paralleldrive/cuid2';
const usersTableDefFn = mysqlTable('users_default_fn', {
customId: varchar({ length: 256 }).primaryKey().$defaultFn(createId),
name: text().notNull(),
});
const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { customId: string }[]
```
> If there is no primary keys -> type will be `{}[]` for such queries
## Insert multiple rows
```typescript copy
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);
```
## Upserts and conflicts
Drizzle ORM provides simple interfaces for handling upserts and conflicts.
### On duplicate key update
MySQL supports [`ON DUPLICATE KEY UPDATE`](https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html) instead of `ON CONFLICT` clauses. MySQL will automatically determine the conflict target based on the primary key and unique indexes, and will update the row if *any* unique index conflicts.
Drizzle supports this through the `onDuplicateKeyUpdate` method:
```typescript
// Note that MySQL automatically determines targets based on the primary key and unique indexes
await db.insert(users)
.values({ id: 1, name: 'John' })
.onDuplicateKeyUpdate({ set: { name: 'John' } });
```
While MySQL does not directly support doing nothing on conflict, you can perform a no-op by setting any column's value to itself and achieve the same effect:
```typescript
import { sql } from 'drizzle-orm';
await db.insert(users)
.values({ id: 1, name: 'John' })
.onDuplicateKeyUpdate({ set: { id: sql`id` } });
```
## Insert into ... select
As the MySQL documentation mentions:
With INSERT ... SELECT, you can quickly insert many rows into a table from the result of a SELECT statement, which can select from one or many tables
Drizzle supports the current syntax for all dialects, and all of them share the same syntax. Let's review some common scenarios and API usage.
There are several ways to use select inside insert statements, allowing you to choose your preferred approach:
- You can pass a query builder inside the select function.
- You can use a query builder inside a callback.
- You can pass an SQL template tag with any custom select query you want to use
```ts
const insertedEmployees = await db
.insert(employees)
.select(
db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
const qb = new QueryBuilder();
await db.insert(employees).select(
qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
() => db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
(qb) => qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
sql`select users.id as id, users.name as name from users where users.role = 'employee'`
);
```
```ts
await db.insert(employees).select(
() => sql`select users.id as id, users.name as name from users where users.role = 'employee'`
);
```
Source: https://orm.drizzle.team/docs/mysql/joins
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Joins [SQL]
Join clause in SQL is used to combine 2 or more tables, based on related columns between them.
Drizzle ORM joins syntax is a balance between the SQL-likeness and type safety.
## Join types
Drizzle ORM has APIs for `INNER JOIN [LATERAL]`, `LEFT JOIN [LATERAL]`, `RIGHT JOIN`, and `CROSS JOIN [LATERAL]`.
Lets have a quick look at examples based on below table schemas:
```typescript copy
export const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
age: int(),
});
export const pets = mysqlTable('pets', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
ownerId: int('owner_id').notNull().references(() => users.id),
})
```
### Left Join
```typescript copy
const result = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from `users` left join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Left Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).leftJoinLateral(subquery, sql`true`)
```
```sql
select ... from `users`
left join lateral (select ... from `pets` where `users`.`age` >= 16) `userPets` on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Right Join
```typescript copy
const result = await db.select().from(users).rightJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from `users` right join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join
```typescript copy
const result = await db.select().from(users).innerJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from `users` inner join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).innerJoinLateral(subquery, sql`true`)
```
```sql
select ... from `users` inner join lateral (select ... from `pets` where `users`.`age` >= 16) `userPets` on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Cross Join
```typescript copy
const result = await db.select().from(users).crossJoin(pets)
```
```sql
select ... from `users` cross join `pets`
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Cross Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).crossJoinLateral(subquery)
```
```sql
select ... from `users` cross join lateral (select ... from `pets` where `users`.`age` >= 16) `userPets`
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
## Partial select
If you need to select a particular subset of fields or to have a flat response type, Drizzle ORM
supports joins with partial select and will automatically infer return type based on `.select({ ... })` structure.
```typescript copy
await db.select({
userId: users.id,
petId: pets.id,
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select `users`.`id`, `pets`.`id` from `users` left join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
}[];
```
You might've noticed that `petId` can be null now, it's because we're left joining and there can be users without a pet.
It's very important to keep in mind when using `sql` operator for partial selection fields and aggregations when needed,
you should to use `sql` for proper result type inference, that one is on you!
```typescript copy
const result = await db.select({
userId: users.id,
petId: pets.id,
petName1: sql`upper(${pets.name})`,
petName2: sql`upper(${pets.name})`,
//Ëwe should explicitly tell 'string | null' in type, since we're left joining that field
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select `users`.`id`, `pets`.`id`, upper(`pets`.`name`), upper(`pets`.`name`) from `users` left join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
petName1: unknown;
petName2: string | null;
}[];
```
To avoid plethora of nullable fields when joining tables with lots of columns we can utilise our **nested select object syntax**,
our smart type inference will make whole object nullable instead of making all table fields nullable!
```typescript copy
await db.select({
userId: users.id,
userName: users.name,
pet: {
id: pets.id,
name: pets.name,
upperName: sql`upper(${pets.name})`
}
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from `users` left join `pets` on `users`.`id` = `pets`.`owner_id`
```
```typescript
// result type
const result: {
userId: number;
userName: string;
pet: {
id: number;
name: string;
upperName: string;
} | null;
}[];
```
## Aliases & Selfjoins
Drizzle ORM supports table aliases which comes really handy when you need to do selfjoins.
Lets say you need to fetch users with their parents:
```typescript copy
import { user } from "./schema";
import { alias } from "drizzle-orm/mysql-core";
const parent = alias(user, "parent");
const result = await db
.select()
.from(user)
.leftJoin(parent, eq(parent.id, user.parentId));
```
```sql
select ... from `user` left join `user` `parent` on `parent`.`id` = `user`.`parent_id`
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
parentId: number;
};
parent: {
id: number;
name: string;
parentId: number;
} | null;
}[];
```
```typescript
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
parentId: int("parent_id").notNull().references((): AnyMySqlColumn => user.id),
});
```
## Aggregating results
Drizzle ORM delivers name-mapped results from the driver without changing the structure.
You're free to operate with results the way you want, here's an example of mapping many-one relational data:
```typescript
type User = typeof users.$inferSelect;
type Pet = typeof pets.$inferSelect;
const rows = await db
.select({
user: users,
pet: pets,
})
.from(users)
.leftJoin(pets, eq(users.id, pets.ownerId));
const result = rows.reduce>((acc, row) => {
const user = row.user;
const pet = row.pet;
if (typeof acc[user.id] === "undefined") {
acc[user.id] = { user, pets: [] };
}
if (pet) {
acc[user.id]!.pets.push(pet);
}
return acc;
}, {});
// result type
const result: Record
```
## Many-to-one example
```typescript
import { mysqlTable, varchar, int } from 'drizzle-orm/mysql-core';
import { drizzle } from 'drizzle-orm/mysql2';
const cities = mysqlTable('cities', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }),
});
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }),
cityId: int('city_id').references(() => cities.id)
});
const db = drizzle();
const result = await db.select().from(cities).leftJoin(users, eq(cities.id, users.cityId));
```
## Many-to-many example
```typescript
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }),
});
const chatGroups = mysqlTable('chat_groups', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }),
});
const usersToChatGroups = mysqlTable('usersToChatGroups', {
userId: int('user_id').notNull().references(() => users.id),
groupId: int('group_id').notNull().references(() => chatGroups.id),
});
// querying user group with id 1 and all the participants(users)
await db.select()
.from(usersToChatGroups)
.leftJoin(users, eq(usersToChatGroups.userId, users.id))
.leftJoin(chatGroups, eq(usersToChatGroups.groupId, chatGroups.id))
.where(eq(chatGroups.id, 1));
```
Source: https://orm.drizzle.team/docs/mysql/kit-custom-migrations
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
- Drizzle Kit [overview](/docs/mysql/kit-overview) and [config file](/docs/mysql/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/mysql/drizzle-kit-generate)
- `drizzle-kit migrate` command - [read here](/docs/mysql/drizzle-kit-migrate)
Drizzle lets you generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding, which you can then run with [`drizzle-kit migrate`](/docs/mysql/drizzle-kit-migrate) command.
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init_sql
â â đ 20242409135510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409135510_seed-users.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
### Running JavaScript and TypeScript migrations
We will add ability to run custom JavaScript and TypeScript migration/seeding scripts in the upcoming release, you can follow [github discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2832).
Source: https://orm.drizzle.team/docs/mysql/kit-overview
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/mysql/get-started)
- Drizzle schema fundamentals - [read here](/docs/mysql/sql-schema-declaration)
- Database connection basics - [read here](/docs/mysql/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/mysql/migrations)
**Drizzle Kit** is a CLI tool for managing SQL database migrations with Drizzle.
-D drizzle-kit
Make sure to first go through Drizzle [get started](/docs/mysql/get-started) and [migration fundamentals](/docs/mysql/migrations) and pick SQL migration flow that suits your business needs best.
Based on your schema, Drizzle Kit let's you generate and run SQL migration files,
push schema directly to the database, pull schema from database, spin up drizzle studio and has a couple of utility commands.
drizzle-kit generate
drizzle-kit migrate
drizzle-kit push
drizzle-kit pull
drizzle-kit check
drizzle-kit up
drizzle-kit studio
drizzle-kit export
| | |
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`drizzle-kit generate`](/docs/mysql/drizzle-kit-generate) | lets you generate SQL migration files based on your Drizzle schema either upon declaration or on subsequent changes, [see here](/docs/mysql/drizzle-kit-generate). |
| [`drizzle-kit migrate`](/docs/mysql/drizzle-kit-migrate) | lets you apply generated SQL migration files to your database, [see here](/docs/mysql/drizzle-kit-migrate). |
| [`drizzle-kit pull`](/docs/mysql/drizzle-kit-pull) | lets you pull(introspect) database schema, convert it to Drizzle schema and save it to your codebase, [see here](/docs/mysql/drizzle-kit-pull) |
| [`drizzle-kit push`](/docs/mysql/drizzle-kit-push) | lets you push your Drizzle schema to database either upon declaration or on subsequent schema changes, [see here](/docs/mysql/drizzle-kit-push) |
| [`drizzle-kit studio`](/docs/mysql/drizzle-kit-studio) | will connect to your database and spin up proxy server for Drizzle Studio which you can use for convenient database browsing, [see here](/docs/mysql/drizzle-kit-studio) |
| [`drizzle-kit check`](/docs/mysql/drizzle-kit-check) | will walk through all generate migrations and check for any race conditions(collisions) of generated migrations, [see here](/docs/mysql/drizzle-kit-check) |
| [`drizzle-kit up`](/docs/mysql/drizzle-kit-up) | used to upgrade snapshots of previously generated migrations, [see here](/docs/mysql/drizzle-kit-up) |
| [`drizzle-kit export`](/docs/mysql/drizzle-kit-export) | used converting TS schema into raw SQL DDL and print it out [see here](/docs/mysql/drizzle-kit-export) |
Drizzle Kit is configured through [drizzle.config.ts](/docs/mysql/drizzle-config-file) configuration file or via CLI params.
It's required to at least provide SQL `dialect` and `schema` path for Drizzle Kit to know how to generate migrations.
```
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle.config.ts <--- Drizzle config file
â đ package.json
â đ tsconfig.json
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/schema.ts",
});
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
dialect: "mysql",
schema: "./src/schema.ts",
dbCredentials: {
url: "./database/",
},
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
},
breakpoints: true,
verbose: true,
});
```
You can provide Drizzle Kit config path via CLI param, it's very useful when you have multiple database stages or multiple databases or different databases on the same project:
drizzle-kit push --config=drizzle-dev.drizzle.config
drizzle-kit push --config=drizzle-prod.drizzle.config
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
Source: https://orm.drizzle.team/docs/mysql/migrations
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
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 Npm from '@mdx/Npm.astro';
import Tag from '@mdx/Tag.astro'
# Drizzle migrations fundamentals
SQL databases require you to specify a **strict schema** of entities you're going to store upfront
and if (when) you need to change the shape of those entities - you will need to do it via **schema migrations**.
There're multiple production grade ways of managing database migrations.
Drizzle is designed to perfectly suits all of them, regardless of you going **database first** or **codebase first**.
**Database first** is when your database schema is a source of truth. You manage your database schema either directly on the database or
via database migration tools and then you pull your database schema to your codebase application level entities.
**Codebase first** is when database schema in your codebase is a source of truth and is under version control. You declare and manage your database schema in JavaScript/TypeScript
and then you apply that schema to the database itself either with Drizzle, directly or via external migration tools.
#### How can Drizzle help?
We've built [**drizzle-kit**](/docs/mysql/kit-overview) - CLI app for managing migrations with Drizzle.
```shell
drizzle-kit migrate
drizzle-kit generate
drizzle-kit push
drizzle-kit pull
drizzle-kit export
```
It is designed to let you choose how to approach migrations based on your current business demands.
It fits in both database and codebase first approaches, it lets you **push your schema** or **generate SQL migration** files or **pull the schema** from database.
It is perfect wether you work alone or in a team.
**Now let's pick the best option for your project:**
**Option 1**
> I manage database schema myself using external migration tools or by running SQL migrations directly on my database.
> From Drizzle I just need to get current state of the schema from my database and save it as TypeScript schema file.
That's a **database first** approach. You have your database schema as a **source of truth** and
Drizzle lets you pull database schema to TypeScript using [`drizzle-kit pull`](/docs/mysql/drizzle-kit-pull) command.
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââââââââââââââ
â â <--- CREATE TABLE `users` (
ââââââââââââââââââââââââââââ â â `id` int AUTO_INCREMENT PRIMARY KEY,
â ~ drizzle-kit pull â â â `name` text,
âââŦâââââââââââââââââââââââââ â DATABASE â `age` int,
â â â CONSTRAINT ... UNIQUE INDEX(`age`)
â Pull datatabase schema -----> â â );
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as m from "drizzle-orm/mysql-core";
export const users = m.mysqlTable("users", {
id: m.int().primaryKey().autoincrement(),
name: m.text(),
age: m.int(),
},
(table) => [m.uniqueIndex("users_age_unique_idx").on(table.age)]
);
```
**Option 2**
> I want to have database schema in my TypeScript codebase,
> I don't wanna deal with SQL migration files.
> I want Drizzle to "push" my schema directly to the database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a **source of truth** and
Drizzle lets you push schema changes to the database using [`drizzle-kit push`](/docs/mysql/drizzle-kit-push) command.
That's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
```typescript {6} filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).notNull(), // <--- added column
});
```
```
Add column to `users` table
âââââââââââââââââââââââââââââââââââââââââââââââ
â + email: varchar({ length: 255 }).notNull() â
âââŦââââââââââââââââââââââââââââââââââââââââââââ
â
v
ââââââââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦâââââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´ââââââââââââââââââ
ALTER TABLE `users` ADD `email` varchar(255) NOT NULL;
```
**Option 3**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and apply them to my database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mysql/drizzle-kit-generate)
and then apply them to the database with [`drizzle-kit migrate`](/docs/mysql/drizzle-kit-migrate) commands.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255),
`email` varchar(255),
CONSTRAINT `email_unique` UNIQUE INDEX(`email`)
);
```
```
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. read migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 4**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and I want Drizzle to apply them during runtime
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mysql/drizzle-kit-generate) and then
you can apply them to the database during runtime of your application.
This approach is widely used for **monolithic** applications when you apply database migrations
during zero downtime deployment and rollback DDL changes if something fails.
This is also used in **serverless** deployments with migrations running in **custom resource** once during deployment process.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255),
`email` varchar(255),
CONSTRAINT `email_unique` UNIQUE INDEX(`email`)
);
```
```ts
// index.ts
import { drizzle } from "drizzle-orm/mysql2"
import { migrate } from 'drizzle-orm/mysql2/migrator';
const db = drizzle(process.env.DATABASE_URL);
await migrate(db);
```
```
âââââââââââââââââââââââââ
â npx tsx src/index.ts â
âââŦââââââââââââââââââââââ
â
â 1. init database connection ââââââââââââââââââââââââââââ
â 2. read migration.sql files in migrations folder â â
3. fetch migration history from database -------------> â â
â 4. pick previously unapplied migrations <-------------- â DATABASE â
â 5. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 5**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me,
> but I will apply them to my database myself or via external migration tools
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/mysql/drizzle-kit-generate) and then
you can apply them to the database either directly or via external migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous scheama
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255),
`email` varchar(255),
CONSTRAINT `email_unique` UNIQUE INDEX(`email`)
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
directly to the database
â ââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââŦâââ>â â
â â â Database â
or via external tools â â â
â â ââââââââââââââââââââââ
â ââââââââââââââââââââââ â
ââââ Bytebase ââââââââââââââ
ââââââââââââââââââââââ¤
â Liquibase â
ââââââââââââââââââââââ¤
â Atlas â
ââââââââââââââââââââââ¤
â etcâĻ â
ââââââââââââââââââââââ
[â] done!
```
**Option 6**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to output the SQL representation of my Drizzle schema to the console,
> and I will apply them to my database via [Atlas](https://atlasgo.io/guides/orms/drizzle)
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you export SQL statements based on your schema changes with [`drizzle-kit export`](/docs/mysql/drizzle-kit-generate) and then
you can apply them to the database via [Atlas](https://atlasgo.io/guides/orms/drizzle) or other external SQL migration tools.
```typescript filename="src/schema.ts"
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().primaryKey().autoincrement(),
name: p.varchar({ length: 255 }),
email: p.varchar({ length: 255 }).unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit export â
âââŦâââââââââââââââââââââââ
â
â 1. read your drizzle schema
2. generated SQL representation of your schema
â 3. outputs to console
â
â
v
```
```sql
CREATE TABLE `users` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255),
`email` varchar(255),
CONSTRAINT `email_unique` UNIQUE INDEX(`email`)
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
via Atlas
â ââââââââââââââââ
â ââââââââââââââââââââââ â â
ââââ Atlas ââââââââââââ>â Database â
ââââââââââââââââââââââ â â
ââââââââââââââââ
[â] done!
```
Source: https://orm.drizzle.team/docs/mysql/operators
import Section from '@mdx/Section.astro';
# Filter and conditional operators
We natively support all dialect specific filter and conditional operators.
You can import all filter & conditional from `drizzle-orm`:
```typescript copy
import { eq, ne, gt, gte, ... } from "drizzle-orm";
```
### eq
Value equal to `n`
```typescript copy
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column, 5));
```
```sql copy
SELECT * FROM `table` WHERE `table`.`column` = 5
```
```typescript
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` = `table`.`column2`
```
### ne
Value is not equal to `n`
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column, 5));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` <> 5
```
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` <> `table`.`column2`
```
## ---
### gt
Value is greater than `n`
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column, 5));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` > 5
```
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` > `table`.`column2`
```
### gte
Value is greater than or equal to `n`
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column, 5));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` >= 5
```
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` >= `table`.`column2`
```
### lt
Value is less than `n`
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column, 5));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` < 5
```
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` < `table`.`column2`
```
### lte
Value is less than or equal to `n`.
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column, 5));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` <= 5
```
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column1, table.column2));
```
```sql
SELECT * FROM `table` WHERE `table`.`column1` <= `table`.`column2`
```
## ---
### exists
Value exists
```typescript
import { exists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(exists(query));
```
```sql
SELECT * FROM `table` WHERE EXISTS (SELECT * FROM `table2`)
```
### notExists
```typescript
import { notExists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(notExists(query));
```
```sql
SELECT * FROM `table` WHERE NOT EXISTS (SELECT * FROM `table2`)
```
### isNull
Value is `null`
```typescript
import { isNull } from "drizzle-orm";
db.select().from(table).where(isNull(table.column));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` IS NULL
```
### isNotNull
Value is not `null`
```typescript
import { isNotNull } from "drizzle-orm";
db.select().from(table).where(isNotNull(table.column));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` IS NOT NULL
```
## ---
### inArray
Value is in array of values
```typescript
import { inArray } from "drizzle-orm";
db.select().from(table).where(inArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` IN (1, 2, 3, 4)
```
```typescript
import { inArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(inArray(table.column, query));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` IN (SELECT `table2`.`column` FROM `table2`)
```
### notInArray
Value is not in array of values
```typescript
import { notInArray } from "drizzle-orm";
db.select().from(table).where(notInArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` NOT IN (1, 2, 3, 4)
```
```typescript
import { notInArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(notInArray(table.column, query));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` NOT IN (SELECT `table2`.`column` FROM `table2`)
```
## ---
### between
Value is between two values
```typescript
import { between } from "drizzle-orm";
db.select().from(table).where(between(table.column, 2, 7));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` BETWEEN 2 AND 7
```
### notBetween
Value is not between two value
```typescript
import { notBetween } from "drizzle-orm";
db.select().from(table).where(notBetween(table.column, 2, 7));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` NOT BETWEEN 2 AND 7
```
## ---
### like
Value is like other value, case sensitive
```typescript
import { like } from "drizzle-orm";
db.select().from(table).where(like(table.column, "%llo wor%"));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` LIKE '%llo wor%'
```
### notLike
Value is not like some other value, case insensitive
```typescript
import { notLike } from "drizzle-orm";
db.select().from(table).where(notLike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM `table` WHERE `table`.`column` NOT LIKE '%llo wor%'
```
## ---
### not
All conditions must return `false`.
```typescript
import { eq, not } from "drizzle-orm";
db.select().from(table).where(not(eq(table.column, 5)));
```
```sql
SELECT * FROM `table` WHERE NOT (`table`.`column` = 5)
```
### and
All conditions must return `true`.
```typescript
import { gt, lt, and } from "drizzle-orm";
db.select().from(table).where(and(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM `table` WHERE (`table`.`column` > 5 AND `table`.`column` < 7)
```
### or
One or more conditions must return `true`.
```typescript
import { gt, lt, or } from "drizzle-orm";
db.select().from(table).where(or(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM `table` WHERE (`table`.`column` > 5 OR `table`.`column` < 7)
```
## ---
Source: https://orm.drizzle.team/docs/mysql/overview
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro'
# Drizzle ORM
Drizzle ORM is a headless TypeScript ORM with a head. đ˛
> Drizzle is a good friend who's there for you when necessary and doesn't bother when you need some space.
It looks and feels simple, performs on day _1000_ of your project,\
lets you do things your way, and is there when you need it.
**It's the only ORM with both [relational](/docs/mysql/rqb) and [SQL-like](/docs/mysql/select) query APIs**,
providing you the best of both worlds when it comes to accessing your relational data.
Drizzle is lightweight, performant, typesafe, non-lactose, gluten-free, sober, flexible and **serverless-ready by design**.
Drizzle is not just a library, it's an experience. đ¤Š
[](https://bestofjs.org/projects/drizzle-orm)
## Headless ORM?
First and foremost, Drizzle is a library and a collection of complementary opt-in tools.
**ORM** stands for _object relational mapping_, and developers tend to call Django-like or Spring-like tools an ORM.
We truly believe it's a misconception based on legacy nomenclature, and we call them **data frameworks**.
With data frameworks you have to build projects **around them** and not **with them**.
**Drizzle** lets you build your project the way you want, without interfering with your project or structure.
Using Drizzle you can define and manage database schemas in TypeScript, access your data in a SQL-like
or relational way, and take advantage of opt-in tools
to push your developer experience _through the roof_. đ¤¯
## Why SQL-like?
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate/abstract you away from SQL, which
leads to a double learning curve: needing to know 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 can have zero to no
learning curve and access to the full power of SQL.
We bring all the familiar **[SQL schema](/docs/mysql/sql-schema-declaration)**, **[queries](/docs/mysql/select)**,
**[automatic migrations](/docs/mysql/migrations)** and **[one more thing](/docs/mysql/rqb)**. â¨
```typescript copy
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
```
```typescript copy
// manage your schema
export const countries = mysqlTable('countries', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 256 }),
});
export const cities = mysqlTable('cities', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 256 }),
countryId: int('country_id').references(() => countries.id),
});
```
```sql
CREATE TABLE `countries` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(256)
);
CREATE TABLE `cities` (
`id` int AUTO_INCREMENT PRIMARY KEY,
`name` varchar(256),
`country_id` int
);
ALTER TABLE `cities` ADD CONSTRAINT `cities_country_id_countries_id_fkey` FOREIGN KEY (`country_id`) REFERENCES `countries`(`id`);
```
## Why not SQL-like?
We're always striving for a perfectly balanced solution, and while SQL-like does cover 100% of the needs,
there are certain common scenarios where you can query data in a better way.
We've built the **[Queries API](/docs/mysql/rqb)** for you, so you can fetch relational nested data from the database
in the most convenient and performant way, and never think about joins and data mapping.
**Drizzle always outputs exactly 1 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
},
});
```
## Serverless?
The best part is no part. **Drizzle has exactly 0 dependencies!**

Drizzle ORM is dialect-specific, slim, performant and serverless-ready **by design**.
We've spent a lot of time to make sure you have best-in-class SQL dialect support, including Postgres, MySQL, and others.
Drizzle operates natively through industry-standard database drivers. We support all major **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[SingleStore](/docs/singlestore/get-started-singlestore)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** drivers out there, and we're adding new ones **[really fast](https://twitter.com/DrizzleORM/status/1653082492742647811?s=20)**.
## Welcome on board!
More and more companies are adopting Drizzle in production, experiencing immense benefits in both DX and performance.
**We're always there to help, so don't hesitate to reach out. We'll gladly assist you in your Drizzle journey!**
We have an outstanding **[Discord community](https://driz.link/discord)** and welcome all builders to our **[Twitter](https://twitter.com/drizzleorm)**.
Now go build something awesome with Drizzle and your **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** database. đ
### Video Showcase
{/* tRPC + NextJS App Router = Simple Typesafe APIs
Jack Herrington 19:17
https://www.youtube.com/watch?v=qCLV0Iaq9zU */}
{/* https://www.youtube.com/watch?v=qDunJ0wVIec */}
{/* https://www.youtube.com/watch?v=NZpPMlSAez0 */}
{/* https://www.youtube.com/watch?v=-A0kMiJqQRY */}
Source: https://orm.drizzle.team/docs/mysql/perf-queries
# Query performance
When it comes to **Drizzle** â we're a thin TypeScript layer on top of SQL with
almost 0 overhead and to make it actual 0, you can utilise our prepared statements API.
**When you run a query on the database, there are several things that happen:**
- all the configurations of the query builder got concatenated to the SQL string
- that string and params are sent to the database driver
- driver compiles SQL query to the binary SQL executable format and sends it to the database
With prepared statements you do SQL concatenation once on the Drizzle ORM side and then database
driver is able to reuse precompiled binary SQL instead of parsing query all the time.
It has extreme performance benefits on large SQL queries.
Different database drivers support prepared statements in different ways and sometimes
Drizzle ORM you can go [**faster than better-sqlite3 driver.**](https://twitter.com/_alexblokh/status/1593593415907909634)
## Prepared statement
```typescript copy {3}
const db = drizzle(...);
const prepared = db.select().from(customers).prepare();
const res1 = await prepared.execute();
const res2 = await prepared.execute();
const res3 = await prepared.execute();
```
## Placeholder
Whenever you need to embed a dynamic runtime value - you can use the `sql.placeholder(...)` api
```ts copy {6,9-10,15,18}
import { sql, eq } from "drizzle-orm";
const p1 = db
.select()
.from(customers)
.where(eq(customers.id, sql.placeholder('id')))
.prepare()
await p1.execute({ id: 10 }) // SELECT * FROM customers WHERE id = 10
await p1.execute({ id: 12 }) // SELECT * FROM customers WHERE id = 12
const p2 = db
.select()
.from(customers)
.where(sql`${customers.name} like ${sql.placeholder('name')}`)
.prepare();
await p2.execute({ name: '%an%' }) // SELECT * FROM customers WHERE name like '%an%'
```
Source: https://orm.drizzle.team/docs/mysql/query-utils
import $count from '@mdx/$count-mysql.mdx';
# Drizzle query utils
### $count
<$count/>
Source: https://orm.drizzle.team/docs/mysql/read-replicas
# Read Replicas
When your project involves a set of read replica instances, and you require a convenient method for managing
SELECT queries from read replicas, as well as performing create, delete, and update operations on the primary
instance, you can leverage the `withReplicas()` function within Drizzle
```ts copy
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
import { boolean, int, mysqlTable, text, withReplicas } from 'drizzle-orm/mysql-core';
const usersTable = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
verified: boolean().notNull().default(false),
});
const primaryClient = await mysql.createConnection({
host: "host",
user: "user",
database: "primary_db",
})
const primaryDb = drizzle({ client: primaryClient });
const read1Client = await mysql.createConnection({
host: "host",
user: "user",
database: "read_1",
})
const read1 = drizzle({ client: read1Client });
const read2Client = await mysql.createConnection({
host: "host",
user: "user",
database: "read_2",
})
const read2 = drizzle({ client: read2Client });
const db = withReplicas(primaryDb, [read1, read2]);
```
You can now use the `db` instance the same way you did before. Drizzle will
handle the choice between read replica and the primary instance automatically
```ts
// Read from either the read1 connection or the read2 connection
await db.select().from(usersTable)
// Use the primary database for the delete operation
await db.delete(usersTable).where(eq(usersTable.id, 1))
```
You can use the `$primary` key to force using primary instances even for read operations
```ts
// read from primary
await db.$primary.select().from(usersTable);
```
With Drizzle, you can also specify custom logic for choosing read replicas.
You can make a weighted decision or any other custom selection method for random read replica choice.
Here is an implementation example of custom logic for selecting read replicas,
where the first replica has a 70% chance of being chosen, and the second replica has a 30%
chance of being selected.
Keep in mind that you can implement any type of random selection method for read replicas
```ts
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!
});
await db.select().from(usersTable)
```
Source: https://orm.drizzle.team/docs/mysql/relations-schema-declaration
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle Relations Fundamentals
In the world of databases, especially relational databases, the concept of relations is absolutely fundamental.
Think of "relations" as the connections and links between different pieces of data. Just like in real life,
where people have relationships with each other, or objects are related to categories, databases use relations to model how different
types of information are connected and work together.
### Normalization
Normalization is the process of organizing data in your database to reduce redundancy (duplication) and improve data integrity
(accuracy and consistency). Think of it like tidying up a messy filing cabinet. Instead of having all sorts of papers
crammed into one folder, you organize them into logical folders and categories to make everything easier to find and manage.
- **Reduces Data Redundancy**: Imagine storing a customer's address every time they place an order. If the address changes, you'd have to update it in multiple places! Normalization helps you store information in one place and refer to it from other places, minimizing repetition.
- **Improves Data Integrity**: Less redundancy means less chance of inconsistencies. If you update an address in one place, it's updated everywhere it's needed.
- **Prevents Anomalies**: Normalization helps prevent issues like:
1. **Insertion Anomalies**: Difficulty adding new data because you're missing related information.
2. **Update Anomalies**: Having to update the same information in multiple rows.
3. **Deletion Anomalies**: Accidentally losing valuable information when you delete something seemingly unrelated.
- **Easier to Understand and Maintain**: A normalized database is generally more logically structured and easier to understand, query, and modify.
Normalization is often described in terms of "normal forms" (1NF, 2NF, 3NF, and beyond). While the details can get quite technical, the core ideas are straightforward:
#### 1NF (First Normal Form): `Atomic Values`
**Goal**: Each column should hold a single, indivisible value. No repeating groups of data within a single cell
**Example**: Instead of having a single `address` column that stores `123 Main St, City, USA`, you'd
break it down into separate columns: `street_address`, `city`, `state`, `zip_code`.
```sql
-- Unnormalized (violates 1NF)
CREATE TABLE `Customers_Unnormalized` (
`customer_id` INT PRIMARY KEY,
`name` VARCHAR(255),
`address` VARCHAR(255) -- Problem: Multiple pieces of info in one column
);
-- Normalized to 1NF
CREATE TABLE `Customers_1NF` (
`customer_id` INT PRIMARY KEY,
`name` VARCHAR(255),
`street_address` VARCHAR(255),
`city` VARCHAR(255),
`state` VARCHAR(255),
`zip_code` VARCHAR(10)
);
```
#### 2NF (Second Normal Form): `Eliminate Redundant Data Dependent on Part of the Key`
**Goal**: Applies when you have a table with a composite primary key (a primary key made up of two or more columns).
2NF ensures that all non-key attributes are fully dependent on the entire composite primary key, not just part of it.
Imagine we have a table called `order_items`. This table tracks items within orders, and we use a composite primary key (`order_id`, `product_id`)
because a single order can have multiple of the same product (though in this simplified example, let's assume each product appears only
once per order for clarity, but the composite key logic still applies).
```sql
CREATE TABLE `OrderItems_Unnormalized` (
`order_id` INT,
`product_id` VARCHAR(10),
`product_name` VARCHAR(100),
`product_price` DECIMAL(10, 2),
`quantity` INT,
`order_date` DATE,
PRIMARY KEY (`order_id`, `product_id`) -- Composite Primary Key
);
INSERT INTO `OrderItems_Unnormalized` (`order_id`, `product_id`, `product_name`, `product_price`, `quantity`, `order_date`) VALUES
(101, 'A123', 'Laptop', 1200.00, 1, '2023-10-27'),
(101, 'B456', 'Mouse', 25.00, 2, '2023-10-27'),
(102, 'A123', 'Laptop', 1200.00, 1, '2023-10-28'),
(103, 'C789', 'Keyboard', 75.00, 1, '2023-10-29');
```
```
+------------------------------------------------------------------------------------+
| OrderItems_Unnormalized |
+------------------------------------------------------------------------------------+
| PK (order_id, product_id) | product_name | product_price | quantity | order_date |
+------------------------------------------------------------------------------------+
| 101, A123 | Laptop | 1200.00 | 1 | 2023-10-27 |
| 101, B456 | Mouse | 25.00 | 2 | 2023-10-27 |
| 102, A123 | Laptop | 1200.00 | 1 | 2023-10-28 |
| 103, C789 | Keyboard | 75.00 | 1 | 2023-10-29 |
+------------------------------------------------------------------------------------+
```
**Problem**: Notice that `product_name` and `product_price` are repeated whenever the same `product_id` appears in different orders.
These attributes are only dependent on `product_id`, which is part of the composite primary key (`order_id`, `product_id`), but not the entire key.
This is a partial dependency.
To achieve 2NF, we need to remove the partially dependent attributes (`product_name`, `product_price`) and place them in a separate table where they are
fully dependent on the primary key of that new table.
```
+-------------------+ 1:M +---------------------------+
| Products | <---------- | OrderItems_2NF |
+-------------------+ +---------------------------+
| PK product_id | | PK (order_id, product_id) |
| product_name | | quantity |
| product_price | | order_date |
+-------------------+ | FK product_id |
+---------------------------+
```
```sql
CREATE TABLE `Products` (
`product_id` VARCHAR(10) PRIMARY KEY,
`product_name` VARCHAR(100),
`product_price` DECIMAL(10, 2)
);
CREATE TABLE `OrderItems_2NF` (
`order_id` INT,
`product_id` VARCHAR(10),
`quantity` INT,
`order_date` DATE,
PRIMARY KEY (`order_id`, `product_id`), -- Composite Primary Key remains
FOREIGN KEY (`product_id`) REFERENCES `Products`(`product_id`) -- Foreign Key to Products
);
-- Insert data into Products
INSERT INTO `Products` (`product_id`, `product_name`, `product_price`) VALUES
('A123', 'Laptop', 1200.00),
('B456', 'Mouse', 25.00),
('C789', 'Keyboard', 75.00);
-- Insert data into OrderItems_2NF (referencing Products)
INSERT INTO `OrderItems_2NF` (`order_id`, `product_id`, `quantity`, `order_date`) VALUES
(101, 'A123', 1, '2023-10-27'),
(101, 'B456', 2, '2023-10-27'),
(102, 'A123', 1, '2023-10-28'),
(103, 'C789', 1, '2023-10-29');
```
#### 3NF (Third Normal Form): `Eliminate Redundant Data Dependent on Non-Key Attributes`
**Goal**: Remove data that is dependent on other non-key attributes. This is about eliminating transitive dependencies.
**Problem**: Let's say we have a `suppliers` table. We store supplier information, including their `zip_code`, `city`, and `state`. `supplier_id` is the primary key.
```sql
CREATE TABLE `suppliers` (
`supplier_id` VARCHAR(10) PRIMARY KEY,
`supplier_name` VARCHAR(255),
`zip_code` VARCHAR(10),
`city` VARCHAR(100),
`state` VARCHAR(50)
);
INSERT INTO `suppliers` (`supplier_id`, `supplier_name`, `zip_code`, `city`, `state`) VALUES
('S1', 'Acme Corp', '12345', 'Anytown', 'NY'),
('S2', 'Beta Inc', '67890', 'Otherville', 'CA'),
('S3', 'Gamma Ltd', '12345', 'Anytown', 'NY');
```
```
+---------------------------------------------------------------+
| suppliers |
+---------------------------------------------------------------+
| PK supplier_id | supplier_name | zip_code | city | state |
+---------------------------------------------------------------+
| S1 | Acme Corp | 12345 | Anytown | NY |
| S2 | Beta Inc | 67890 | Otherville | CA |
| S3 | Gamma Ltd | 12345 | Anytown | NY |
+---------------------------------------------------------------+
```
**Solution**: To achieve 3NF, we remove the attributes dependent on the non-key attribute (`city`, `state` dependent on `zip_code`) and put
them into a separate table keyed by the non-key attribute itself (`zip_code`).
```
+-------------------+ 1:M +--------------------+
| zip_codes | <---------- | suppliers |
+-------------------+ +--------------------+
| PK zip_code | | PK supplier_id |
| city | | supplier_name |
| state | | FK zip_code |
+-------------------+ +--------------------+
```
```sql
CREATE TABLE `zip_codes` (
`zip_code` VARCHAR(10) PRIMARY KEY,
`city` VARCHAR(100),
`state` VARCHAR(50)
);
CREATE TABLE `suppliers` (
`supplier_id` VARCHAR(10) PRIMARY KEY,
`supplier_name` VARCHAR(255),
`zip_code` VARCHAR(10), -- Foreign Key to zip_codes
FOREIGN KEY (`zip_code`) REFERENCES `zip_codes`(`zip_code`)
);
-- Insert data into zip_codes
INSERT INTO `zip_codes` (`zip_code`, `city`, `state`) VALUES
('12345', 'Anytown', 'NY'),
('67890', 'Otherville', 'CA');
-- Insert data into suppliers (referencing zip_codes)
INSERT INTO `suppliers` (`supplier_id`, `supplier_name`, `zip_code`) VALUES
('S1', 'Acme Corp', '12345'),
('S2', 'Beta Inc', '67890'),
('S3', 'Gamma Ltd', '12345');
```
There are additional normal forms, such as `4NF`, `5NF`, `6NF`, `EKNF`, `ETNF`, and `DKNF`. We won't cover these here, but we will create a
dedicated set of tutorials for them in our guides and tutorials section.
### Database Relationships
#### One-to-One
In a one-to-one relationship, each record in `table A` is related to at most one record in `table B`, and each record in `table B` is
related to at most one record in `table A`. It's a very direct, exclusive pairing.
1. **User Profiles and User Account Details**: Think of a website. Each user account (in a Users table) might have exactly one user profile (in a UserProfiles table) containing more detailed information.
2. **Employees and Parking Spaces**: An Employees table and a ParkingSpaces table. Each employee might be assigned at most one parking space, and each parking space is assigned to at most one employee.
3. **Splitting Tables for Organization**: Sometimes, you might split a very wide table into two for better organization or security reasons, maintaining a 1-1 relationship between them.
```
Table A (One Side) Table B (One Side)
+---------+ +---------+
| PK (A) | <---------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
```
#### One-to-Many
In a one-to-many relationship, one record in `table A` can be related to many records in `table B`, but each
record in `table B` is related to at most one record in `table A`. Think of it as a "parent-child" relationship.
1. **Customers and Orders**: One customer can place many orders, but each order belongs to only one customer.
2. **Authors and Books**: One author can write many books, but (let's simplify for now and say) each book is written by one primary author.
3. **Departments and Employees**: One department can have many employees, but each employee belongs to only one department.
```
Table A (One Side) Table B (Many Side)
+---------+ +---------+
| PK (A) | ----------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
(One) (Many)
```
#### Many-to-Many
In a many-to-many relationship, one record in `table A` can be related to many records in `table B`, and one
record in `table B` can be related to many records in `table A`. It's a more complex, bidirectional relationship.
1. **Students and Courses**: One student can enroll in many courses, and one course can have many students enrolled.
2. **Products and Categories**: One product can belong to multiple categories (e.g., a "T-shirt" can be
in "Clothing" and "Summer Wear" categories), and one category can contain many products.
3. **Authors and Books**: A book can be written by multiple authors, and an author can write multiple books.
```
Table A (Many Side) Junction Table Table B (Many Side)
+---------+ +-------------+ +---------+
| PK (A) | -------->| FK (A) | <----| FK (B) |
| ... | | FK (B) | | ... |
+---------+ +-------------+ +---------+
(Many) (Junction) (Many)
```
Many-to-many relationships are not directly implemented with foreign keys between the two main tables.
Instead, you need a `junction` table (also called an associative table or bridging table).
This table acts as an intermediary to link records from both tables.
```sql
-- Table for Students (Many side)
CREATE TABLE `students` (
`id` INT PRIMARY KEY,
`name` VARCHAR(255)
);
-- Table for Courses (Many side)
CREATE TABLE `courses` (
`id` INT PRIMARY KEY,
`name` VARCHAR(255),
`credits` INT
);
-- Junction Table: Enrollments (Connects Students and Courses - M-M relationship)
CREATE TABLE `enrollments` (
`id` INT PRIMARY KEY AUTO_INCREMENT, -- Optional, but good practice for junction tables
`student_id` INT,
`course_id` INT,
`enrollment_date` DATE,
-- Composite Foreign Keys (often part of a composite primary key or unique constraint)
FOREIGN KEY (`student_id`) REFERENCES `students`(`id`),
FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`),
UNIQUE KEY (`student_id`, `course_id`) -- Prevent duplicate enrollments for the same student and course
);
```
### Why Foreign Keys?
You might think of foreign key constraints as simply a way to validate data - ensuring
that when you enter a value in a foreign key column, that value actually exists in the primary key
column of another table. And you'd be partially right! This value checking is the mechanism foreign keys use.
But it's crucial to understand that this validation is not the end goal, it's the means
to a much larger purpose. Foreign key constraints are fundamentally about:
We've discussed relationships like `One-to-Many` between Customers and Orders.
A foreign key is the SQL language's way of telling the database:
> Hey database, I want to enforce a 1-M relationship here. Every value in the customer_id column of the
Orders table must correspond to a valid customer_id in the Customers table.
It's not just a suggestion; it's a constraint the database actively enforces.
The database becomes relationship-aware because of the foreign key.
- This is the core of "data integrity" in the context of relationships. Referential integrity means
that relationships between tables remain consistent and valid over time.
- Foreign keys prevent orphaned records. What's an orphaned record? In our Customer-Order example,
an order that exists in the Orders table but doesn't have a corresponding customer in the Customers
table would be an orphan. Foreign keys prevent this from happening (or control what happens
if you try to delete a customer with orders - via CASCADE, SET NULL, etc.).
- Why is preventing orphans important? Orphaned records break the logical structure of your data.
If you have an order without a customer, you lose crucial context. Queries become unreliable, reports
become inaccurate, and your application's logic can break down.
**Example**:
```
Without a foreign key, you could accidentally delete a customer from the Customers
table while their orders still exist in the Orders table. Suddenly, you have orders that point to
a customer that no longer exists! A foreign key constraint prevents this data inconsistency.
```
- Foreign keys are not just about technical enforcement; they are also a crucial part of database design documentation.
- When you see a foreign key in a database schema, it immediately tells you:
`Table 'X' is related to Table 'Y' in this way.` It's a clear visual and structural indicator of relationships.
- This makes databases easier to understand, maintain, and evolve over time. New developers can quickly
grasp how different parts of the database are connected.
In essence, foreign key constraints are not just about checking values; they are about:
1. Defining the rules of your data relationships
2. Actively enforcing those rules at the database level
3. Guaranteeing data integrity and consistency within those relationships
4. Making your database more robust, reliable, and understandable
### Why NOT Foreign Keys?
While highly beneficial, there are some scenarios where you might reconsider or use Foreign Keys with caution.
These are typically edge cases and often involve trade-offs.
- **Scenario**: Extremely high-volume transactional systems (e.g., real-time logging, very high-frequency trading
platforms, massive IoT data ingestion).
- **Explanation**: Every time you insert or update data in a table with a foreign key, the database system
needs to perform checks to ensure referential integrity. In extremely high-write scenarios, these
checks can introduce a small but potentially noticeable performance overhead.
- **Scenario**: Systems where data is distributed across multiple database nodes or clusters (common in sharded databases, cloud environments, and microservices).
- **Explanation**: Cross-node foreign keys can introduce significant complexity and performance overhead. Validating referential integrity requires communication between nodes, leading to increased latency. Distributed transactions needed to maintain consistency are also more complex and can be less performant than local transactions. In such architectures, application-level data integrity checks or eventual consistency models might be considered alternatives.
- **Scenario**: Integrating a relational database with older legacy systems or non-relational data stores (e.g., NoSQL, flat files, external APIs).
- **Explanation**: Legacy systems or non-relational data might not consistently adhere to the referential integrity rules enforced by foreign keys. Imposing foreign keys in such scenarios can lead to data import issues, data inconsistencies, and might necessitate complex data transformation or application-level integrity management instead. You might need to carefully evaluate the data quality and consistency of the external sources and potentially rely on application logic or ETL processes to ensure data integrity instead of strictly enforcing foreign keys at the database level.
You can also check out some great explanations from the PlanetScale team in their [article](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints#why-does-planetscale-not-recommend-constraints)
### Polymorphic Relations
Polymorphic relationships are a more advanced concept that allows a single relationship to point to
different types of entities or tables. It's about creating more flexible and adaptable relationships when
you have different kinds of data that share some commonality.
Imagine you have an `activities` log. An activity could be a `comment` a `like` or a `share`.
Each of these `activity` types has different details. Instead of creating separate tables and
relationships for each activity type and the things they relate to, you might use a polymorphic approach.
- **Comments/Reviews**: A "Comment" might be related to different types of content: articles, products, videos, etc.
Instead of having separate article_id, product_id, video_id columns in a Comments table, you can use a
polymorphic relationship.
```
+---------------------+
| **Comments** |
+---------------------+
| PK comment_id |
| commentable_type | ------> [Polymorphic Relationship]
| commentable_id | -------->
| user_id |
| comment_text |
| ... |
+---------------------+
^
|
+---------------------+ +---------------------+ +---------------------+
| **Articles** | | **Products** | | **Videos** |
+---------------------+ +---------------------+ +---------------------+
| PK article_id | | PK product_id | | PK video_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +---------------------+
```
- **Notifications:** A notification could be related to a user, an order, a system event, etc.
```
+----------------------+
| **Notifications** |
+----------------------+
| PK notification_id |
| notifiable_type | ------> [Polymorphic Relationship]
| notifiable_id | -------->
| user_id |
| message |
| ... |
+----------------------+
^
|
+---------------------+ +---------------------+ +-----------------------+
| **Users** | | **Orders** | | **System Events** |
+---------------------+ +---------------------+ +-----------------------+
| PK user_id | | PK order_id | | PK event_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +-----------------------+
```
Polymorphic relationships are more complex and are often handled at the application level or
using more advanced database features (depending on the specific database system). Standard SQL doesn't
have direct, built-in support for enforcing polymorphic foreign key constraints in the same way as regular foreign keys.
Source: https://orm.drizzle.team/docs/mysql/relations-v1-v2
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import Section from '@mdx/Section.astro';
import Npx from "@mdx/Npx.astro";
import Npm from "@mdx/Npm.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
# Migrating to Relational Queries version 2
drizzle-orm@rc
drizzle-kit@rc -D
- **Relations Fundamentals** - [read here](/docs/mysql/relations-schema-declaration)
- **drizzle-kit pull** - [read here](/docs/mysql/drizzle-kit-pull)
Below is the table of contents. Click an item to jump to that section:
- [What is working differently from v1](#what-is-working-differently-from-v1)
- [New features in v2](#what-is-new)
- [How to migrate relations definition from v1 to v2](#how-to-migrate-relations-schema-definition-from-v1-to-v2)
- [How to migrate queries from v1 to v2](#how-to-migrate-queries-from-v1-to-v2)
- [Internal changes(imports, internal types, etc.)](#internal-changes)
### API changes
#### What is working differently from v1
{/* ##### Drizzle Relations schema definition */}
One of the biggest updates were in **Relations Schema definition**
The first difference is that you no longer need to specify `relations` for each table separately in different objects and
then pass them all to `drizzle()` along with your schema. In Relational Queries v2, you now have one dedicated place to
specify all the relations for all the tables you need.
The `r` parameter in the callback provides comprehensive autocomplete
functionality - including all tables from your schema and functions such as `one`, `many`, and `through` - essentially
offering everything you need to specify your relations.
```ts
// relations.ts
import * as schema from "./schema"
import { defineRelations } from "drizzle-orm"
export const relations = defineRelations(schema, (r) => ({
...
}));
```
```ts
// index.ts
import { relations } from "./relations"
import { drizzle } from "drizzle-orm/mysql2"
const db = drizzle(process.env.DATABASE_URL, { relations })
```
##### What is different?
```ts copy
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().primaryKey(),
name: p.text(),
invitedBy: p.int('invited_by'),
});
export const posts = p.mysqlTable('posts', {
id: p.int().primaryKey(),
content: p.text(),
authorId: p.int('author_id'),
});
```
**One place for all your relations**
```ts
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
invitee: one(users, {
fields: [users.invitedBy],
references: [users.id],
}),
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
}));
```
You can still separate it into different `parts`, and you can make the parts any size you want
```ts
import { defineRelations, defineRelationsPart } from 'drizzle-orm';
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
and then you can provide it to the db instance
```ts
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
```
**Define `many` without `one`**
In v1, if you wanted only the `many` side of a relationship, you had to specify the `one` side on the other end,
which made for a poor developer experience.
In v2, you can simply use the `many` side without any additional steps
```ts
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.many.posts({
from: r.users.id,
to: r.posts.authorId,
}),
},
}));
```
**New `optional` option**
`optional: false` at the type level makes the `author` key in the `posts` object required.
This should be used when you are certain that this specific entity will always exist.
Was not supported in v1
```ts {9}
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.one.posts({
from: r.users.id,
to: r.posts.authorId,
optional: false,
}),
},
}));
```
**No modes in `drizzle()`**
We found a way to use the same strategy for all MySQL dialects, so there's no need to specify them
```ts
import * as schema from './schema'
const db = drizzle(process.env.DATABASE_URL, { mode: "planetscale", schema });
// or
const db = drizzle(process.env.DATABASE_URL, { mode: "default", schema });
```
```ts
import { relations } from './relations'
const db = drizzle(process.env.DATABASE_URL, { relations });
```
**`from` and `to` upgrades**
We've renamed `fields` to `from` and `references` to `to`, and we made both accept either a single value or an array
```ts
...
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
...
```
```ts
...
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
...
```
```ts
...
author: r.one.users({
from: [r.posts.authorId],
to: [r.users.id],
}),
...
```
**`relationName` -> `alias`**
```ts {8}
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const postsRelation = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
relationName: "author_post",
}),
}));
```
```ts {9}
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "author_post",
}),
},
}));
```
**`custom types` new functions**
There are a few new function were added to custom types, so you can control how data is mapped on Relational Queries v2:
Optional mapping function, that is used for transforming data returned by transformed to JSON in database data to desired format
For example, when querying bigint column via [RQB](/docs/rqb) or 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:
```ts
fromJson(value: string): bigint {
return BigInt(value);
},
```
It'll cause the returned data to change from:
```ts
{
customField: "5044565289845416380";
}
```
to:
```ts
{
customField: 5044565289845416380n;
}
```
Optional selection modifier function, that is used for modifying selection of column inside JSON functions
Additional mapping that could be required for such scenarios can be handled using fromJson function
Used by [relational queries](/docs/rqb)
For example, when using bigint we need to cast field to text to preserve data integrity
```ts
forJsonSelect(identifier: SQL, sql: SQLGenerator, arrayDimensions?: number): SQL {
return sql`${identifier}::text`
},
```
This will change query from:
```sql
SELECT
row_to_json("t".*)
FROM
(
SELECT
"table"."custom_bigint" AS "bigint"
FROM
"table"
) AS "t"
```
to:
```sql
SELECT
row_to_json("t".*)
FROM
(
SELECT
"table"."custom_bigint"::text AS "bigint"
FROM
"table"
) AS "t"
```
Returned by query object will change from:
```ts
{
bigint: 5044565289845416000; // Partial data loss due to direct conversion to JSON format
}
```
to:
```ts
{
bigint: "5044565289845416380"; // Data is preserved due to conversion of field to text before JSON-ification
}
```
```ts
const customBytes = customType<{
data: Buffer;
driverData: Buffer;
jsonData: string;
}>({
dataType: () => 'bytea',
fromJson: (value) => {
return Buffer.from(value.slice(2, value.length), 'hex');
},
forJsonSelect: (identifier, sql, arrayDimensions) =>
sql`${identifier}::text${sql.raw('[]'.repeat(arrayDimensions ?? 0))}`,
});
```
##### What is new?
**`through` for many-to-many relations**
Previously, you would need to query through a junction table and then map it out for every response
You don't need to do it now!
```ts
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().primaryKey(),
name: p.text(),
verified: p.boolean().notNull(),
});
export const groups = p.mysqlTable('groups', {
id: p.int().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.mysqlTable(
'users_to_groups',
{
userId: p
.int('user_id')
.notNull()
.references(() => users.id),
groupId: p
.int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [p.primaryKey({ columns: [t.userId, t.groupId] })],
);
```
```ts
export const usersRelations = relations(users, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, {
fields: [usersToGroups.groupId],
references: [groups.id],
}),
user: one(users, {
fields: [usersToGroups.userId],
references: [users.id],
}),
}));
```
```ts
// Query example
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});
```
```ts
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));
```
```ts
// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});
```
**Predefined filters**
Was not supported in v1
```ts {10-12}
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema,
(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
```
```ts {4}
// Query example: get groups with all verified users
const response = await db.query.groups.findMany({
with: {
verifiedUsers: true,
},
});
```
##### `where` is now object
```ts
const response = db._query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
});
```
```ts
const response = db.query.users.findMany({
where: {
id: 1,
},
});
```
For a complete API Reference please check our [Select Filters docs](/docs/mysql/rqb#select-filters)
```ts
// schema.ts
import { int, json, mysqlTable, text, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: text(),
email: text().notNull(),
age: int(),
createdAt: timestamp('created_at').defaultNow(),
lastLogin: timestamp('last_login'),
subscriptionEnd: timestamp('subscription_end'),
lastActivity: timestamp('last_activity'),
preferences: json(), // JSON column for user settings/preferences
interests: json().$type(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [{ RAW: (table) => sql`${table.name} LIKE 'john%'` }, { name: { like: "jane%" } }],
},
{
RAW: (table) => sql`${table.age} BETWEEN 25 AND 35`
},
],
},
}),
```
##### `orderBy` is now object
```ts
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});
```
```ts
const response = db.query.users.findMany({
orderBy: { id: "asc" },
});
```
##### Filtering by relations
Was not supported in v1
Example: Get all users whose ID>10 and who have at least one post with content starting with âMâ
```ts
const usersWithPosts = await db.query.usersTable.findMany({
where: {
id: {
gt: 10
},
posts: {
content: {
like: 'M%'
}
}
},
});
```
##### Using offset on related objects
Was not supported in v1
```ts
await db.query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // correct â
limit: 3,
},
},
});
```
#### How to migrate relations schema definition from v1 to v2
##### **Option 1**: Using `drizzle-kit pull`
In new version `drizzle-kit pull` supports pulling `relations.ts` file in a new syntax:
##### Step 1
drizzle-kit pull
#### Step 2
Transfer generated relations code from `drizzle/relations.ts` to the file you are using to specify your relations
```plaintext {4-5}
â đ drizzle
â â đ meta
â â đ migration.sql
â â đ relations.ts âââââââââ
â â đ schema.ts |
â đ src â
â â đ db â
â â â đ relations.ts <ââââââ
â â â đ schema.ts
â â đ index.ts
â âĻ
```
`drizzle/relations.ts` includes an import of all tables from `drizzle/schema.ts`, which looks like this:
```ts
import * as schema from './schema'
```
You may need to change this import to a file where ALL your schema tables are located.
If there are multiple schema files, you can do the following:
```ts
import * as schema1 from './schema1'
import * as schema2 from './schema2'
...
```
#### Step 3
Change drizzle database instance creation and provide `relations` object instead of `schema`
```ts
import * as schema from './schema'
import { drizzle } from 'drizzle-orm/mysql2'
const db = drizzle('', { schema })
```
```ts
// should be imported from a file in Step 2
import { relations } from './relations'
import { drizzle } from 'drizzle-orm/mysql2'
const db = drizzle('', { relations })
```
If you had MySQL dialect, you can remove `mode` from `drizzle()` as long as it's not needed in version 2
##### **Option 2**: Manual migration
If you want to migrate manually, you can check our [Drizzle Relations section](/docs/mysql/relations) for the complete API reference and examples of one-to-one, one-to-many, and many-to-many relations.
#### How to migrate queries from v1 to v2
##### Migrate `where` statements
You can check our [Select Filters docs](/docs/mysql/rqb#select-filters) to see examples and a complete API reference.
With the new syntax, you can use `AND`, `OR`, `NOT`, and `RAW`, plus all the filtering operators that
were previously available in Relations v1.
**Examples**
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
},
});
```
```sql {8}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
`d0`.`age` = 15;
```
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
name: 'John'
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(`d0`.`age` = 15)
AND (`d0`.`name` = 'John')
);
```
```ts
const response = await db.query.users.findMany({
where: {
OR: [
{
id: {
gt: 10,
},
},
{
name: {
like: "John%",
},
},
],
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(`d0`.`id` > 10)
OR (`d0`.`name` LIKE 'John%')
);
```
```ts
const response = await db.query.users.findMany({
where: {
NOT: {
id: {
gt: 10,
},
},
name: {
like: "John%",
},
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(not(`d0`.`id` > 10))
AND (`d0`.`name` LIKE 'John%')
);
```
```ts
// schema.ts
import { int, json, mysqlTable, text, timestamp } from "drizzle-orm/mysql-core";
import { sql } from "drizzle-orm";
export const users = mysqlTable("users", {
id: int().primaryKey(),
name: text(),
email: text().notNull(),
age: int(),
createdAt: timestamp("created_at").defaultNow(),
lastLogin: timestamp("last_login"),
subscriptionEnd: timestamp("subscription_end"),
lastActivity: timestamp("last_activity"),
preferences: json(), // JSON column for user settings/preferences
interests: json().$type(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [{ RAW: (table) => sql`${table.name} LIKE 'john%'` }, { name: { like: "jane%" } }],
},
{
RAW: (table) => sql`${table.age} BETWEEN 25 AND 35`
},
],
},
}),
```
```sql
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`email` AS `email`,
`d0`.`age` AS `age`,
`d0`.`created_at` AS `createdAt`,
`d0`.`last_login` AS `lastLogin`,
`d0`.`subscription_end` AS `subscriptionEnd`,
`d0`.`last_activity` AS `lastActivity`,
`d0`.`preferences` AS `preferences`,
`d0`.`interests` AS `interests`
FROM
`users` AS `d0`
WHERE
(
(
(
(`d0`.`name` LIKE 'john%')
OR (`d0`.`name` LIKE 'jane%')
)
)
AND (`d0`.`age` BETWEEN 25 AND 35)
);
```
##### Migrate `orderBy` statements
Order by was simplified to a single object, where you specify the column and the sort direction (`asc` or `desc`)
```ts
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});
```
```ts
const response = db.query.users.findMany({
orderBy: { id: "asc" },
});
```
##### Migrate `many-to-many` queries
Relational Queries v1 had a very complex way of managing many-to-many queries.
You had to use junction tables to query through them explicitly, and then map those tables out, like this:
```ts
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});
```
After upgrading to Relational Queries v2, your many-to-many relation will look like this:
```ts
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));
```
And when you migrate your query, it will become this:
```ts
// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});
```
### Internal changes
1. Every `drizzle` database, `session`, `migrator` and `transaction` instance updated with new generic arguments for RQB v2 queries
**migrator**
```ts
export async function migrate<
TSchema extends Record
>(
db: MySql2Database,
config: MigrationConfig,
) {
...
}
```
```ts {1,2}
export async function migrate(
db: MySql2Database,
config: MigrationConfig,
) {
...
}
```
**session**
```ts
export class MySql2Session<
TFullSchema extends Record,
TSchema extends V1.TablesRelationalConfig,
> extends MySqlSession
```
```ts {2,3}
export class MySql2Session<
TRelations extends AnyRelations,
> extends MySqlSession
```
**transaction**
```ts
export class MySql2Transaction<
TFullSchema extends Record,
TSchema extends V1.TablesRelationalConfig,
> extends MySqlTransaction
```
```ts {2,3}
export class MySql2Transaction<
TRelations extends AnyRelations,
> extends MySqlTransaction
```
**driver**
```ts
export class MySql2Database<
TSchema extends Record = Record,
> extends MySqlDatabase
```
```ts {2,3}
export class MySql2Database<
TRelations extends AnyRelations = EmptyRelations,
> extends MySqlDatabase
```
2. Updated `DrizzleConfig` generic with `TRelations` argument and `relations: TRelations` field
```ts
export interface DrizzleConfig<
TSchema extends Record = Record
> {
logger?: boolean | Logger;
schema?: TSchema;
casing?: Casing;
}
```
```ts {2,5}
export interface DrizzleConfig<
TRelationConfigs extends AnyRelations = EmptyRelations,
> {
logger?: boolean | Logger | undefined;
relations?: TRelationConfigs | undefined;
cache?: Cache | undefined;
jit?: boolean | undefined;
}
```
3. The following entities have been removed from `drizzle-orm` and `drizzle-orm/relations`. The original imports now
include new types used by Relational Queries v2, so make sure to update your imports if you intend to use the older types:
- `Relations`
- `TableRelationsKeysOnly`
- `ExtractTableRelationsFromSchema`
- `ExtractRelationsFromTableExtraConfigSchema`
- `getOperators`
- `FindTableByDBName`
- `RelationalSchemaConfig`
- `RelationConfig`
- `extractTablesRelationalConfig`
- `relations`
- `createOne`
- `createMany`
- `NormalizedRelation`
- `normalizeRelation`
- `createTableRelationsHelpers`
- `TableRelationsHelpers`
4. In the same manner, `${dialect}-core/query-builders/query` files were updated with RQB v2's alternatives
```ts
import { RelationalQueryBuilder, MySqlRelationalQuery } from 'drizzle-orm/mysql-core/query-builders/query';
```
Source: https://orm.drizzle.team/docs/mysql/relations
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import IsSupportedChipGroup from '@mdx/IsSupportedChipGroup.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTab from '@mdx/CodeTab.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import Npm from '@mdx/Npm.astro';
# Drizzle relations
drizzle-orm@rc
drizzle-kit@rc -D
- **Relations Fundamentals** - get familiar with the concepts of foreign key constraints, soft relations, database normalization, etc - [read here](/docs/mysql/relations-schema-declaration)
- **Declare schema** - get familiar with how to define drizzle schemas - [read here](/docs/mysql/sql-schema-declaration)
- **Database connection** - get familiar with how to connect to database using drizzle - [read here](/docs/mysql/get-started-mysql)
The sole purpose of Drizzle relations is to let you query your relational data in the most simple and concise way:
```ts
import { drizzle } from 'drizzle-orm/mysql2';
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text().notNull()
});
export const posts = p.mysqlTable('posts', {
id: p.int().autoincrement().primaryKey(),
content: p.text().notNull(),
ownerId: p.int('owner_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.ownerId,
to: r.users.id,
}),
}
}))
const db = drizzle({ client, relations });
const result = await db.query.posts.findMany({
with: {
author: true,
},
});
```
```ts
[{
id: 10,
content: "My first post!",
ownerId: 14,
author: {
id: 1,
name: "Alex"
}
}]
```
```ts
import { drizzle } from 'drizzle-orm/mysql2';
import { eq } from 'drizzle-orm';
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text().notNull()
});
export const posts = p.mysqlTable('posts', {
id: p.int().autoincrement().primaryKey(),
content: p.text().notNull(),
ownerId: p.int('owner_id'),
});
const db = drizzle({ client });
const res = await db.select()
.from(posts)
.leftJoin(users, eq(posts.ownerId, users.id))
.orderBy(posts.id)
const mappedResult = ...
```
### `one()`
Here is a list of all fields available for `.one()` in drizzle relations
```ts {3-11}
const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.ownerId,
to: r.users.id,
optional: false,
alias: 'custom_name',
where: {
verified: true,
}
}),
}
}))
```
- `author` key is a custom key that appears in the `posts` object when using Drizzle relational queries.
- `r.one.users` defines that `author` will be a single object from the `users` table rather than an array of objects.
- `from: r.posts.ownerId` specifies the table from which we are establishing a soft relation.
In this case, the relation starts from the `ownerId` column in the `posts` table.
- `to: r.users.id` specifies the table to which we are establishing a soft relation.
In this case, the relation points to the `id` column in the `users` table.
- `optional: false` at the type level makes the `author` key in the posts object `required`.
This should be used when you are certain that this specific entity will always exist.
- `alias` is used to add a specific alias to relationships between tables. If you have multiple identical relationships between two tables, you should
differentiate them using `alias`
- `where` condition can be used for polymorphic relations. It fetches relations based on a `where` statement.
For example, in the case above, only `verified authors` will be retrieved. Learn more about polymorphic relations [here](/docs/mysql/relations-schema-declaration#polymorphic-relations).
### `many()`
Here is a list of all fields available for `.many()` in drizzle relations
```ts {3-10}
const relations = defineRelations({ users, posts }, (r) => ({
users: {
feed: r.many.posts({
from: r.users.id,
to: r.posts.ownerId,
alias: 'custom_name',
where: {
approved: true,
}
}),
}
}))
```
- `feed` key is a custom key that appears in the `users` object when using Drizzle relational queries.
- `r.many.posts` defines that `feed` will be an array of objects from the `posts` table rather than just an object
- `from: r.users.id` specifies the table from which we are establishing a soft relation.
In this case, the relation starts from the `id` column in the `users` table.
- `to: r.posts.ownerId` specifies the table to which we are establishing a soft relation.
In this case, the relation points to the `ownerId` column in the `posts` table.
- `alias` is used to add a specific alias to relationships between tables. If you have multiple identical relationships between two tables, you should
differentiate them using `alias`
- `where` condition can be used for polymorphic relations. It fetches relations based on a `where` statement.
For example, in the case above, only `approved posts` will be retrieved. Learn more about polymorphic relations [here](/docs/mysql/relations-schema-declaration#polymorphic-relations).
## ---
### One-to-one
Drizzle ORM provides you an API to define `one-to-one` relations between tables with the `defineRelations` function.
An example of a `one-to-one` relation between users and users, where a user can invite another (this example uses a self reference):
```typescript copy {10-17}
import { mysqlTable, text, int } from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = mysqlTable('users', {
id: int().autoincrement().primaryKey(),
name: text(),
invitedBy: int('invited_by'),
});
export const relations = defineRelations({ users }, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
})
}
}));
```
Another example would be a user having a profile information stored in separate table. In this case, because the foreign key is stored in the "profile_info" table, the user relation have neither fields or references. This tells Typescript that `user.profileInfo` is nullable:
```typescript copy {15-22}
import { mysqlTable, text, int, json } from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = mysqlTable('users', {
id: int().autoincrement().primaryKey(),
name: text(),
});
export const profileInfo = mysqlTable('profile_info', {
id: int().autoincrement().primaryKey(),
userId: int('user_id').references(() => users.id),
metadata: json(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
}),
},
}));
const user = await db.query.users.findFirst({ with: { profileInfo: true } });
//____^? type { id: number, name: string | null, profileInfo: { ... } | null }
```
### One-to-many
Drizzle ORM provides you an API to define `one-to-many` relations between tables with `defineRelations` function.
Example of `one-to-many` relation between users and posts they've written:
```typescript copy {15-25}
import { mysqlTable, text, int } from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: text(),
});
export const posts = mysqlTable('posts', {
id: int().primaryKey(),
content: text(),
authorId: int('author_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
Now lets add comments to the posts:
```typescript copy {9-14,22,27-32}
...
export const posts = mysqlTable('posts', {
id: int().primaryKey(),
content: text(),
authorId: int('author_id'),
});
export const comments = mysqlTable('comments', {
id: int().autoincrement().primaryKey(),
text: text(),
authorId: int('author_id'),
postId: int('post_id'),
});
export const relations = defineRelations({ users, posts, comments }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
comments: r.many.comments(),
},
users: {
posts: r.many.posts(),
},
comments: {
post: r.one.posts({
from: r.comments.postId,
to: r.posts.id,
}),
},
}));
```
### Many-to-many
Drizzle ORM provides you an API to define `many-to-many` relations between tables through so called `junction` or `join` tables,
they have to be explicitly defined and store associations between related tables.
Example of `many-to-many` relation between users and groups we are using `through` to bypass junction table selection and directly select many `groups` for each `user`.
```typescript copy {27-39}
import { defineRelations } from 'drizzle-orm';
import { int, mysqlTable, primaryKey, text } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: int().autoincrement().primaryKey(),
name: text(),
});
export const groups = mysqlTable('groups', {
id: int().autoincrement().primaryKey(),
name: text(),
});
export const usersToGroups = mysqlTable(
'users_to_groups',
{
userId: int('user_id')
.notNull()
.references(() => users.id),
groupId: int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [primaryKey({ columns: [t.userId, t.groupId] })],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
**Query example:**
```ts
const res = await db.query.users.findMany({
with: {
groups: true
},
});
// response type
type Response = {
id: number;
name: string | null;
groups: {
id: number;
name: string | null;
}[];
}[];
```
### Predefined filters
Predefined `where` statements in Drizzle's relation definitions are a type of polymorphic relations implementation, but it's not fully it. Essentially, they allow you to
connect tables not only by selecting specific columns but also through custom `where` statements. Let's look at some examples:
We can define a relation between `groups` and `users` so that when querying group's users, we only retrieve those whose `verified` column is set to `true`
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from './schema';
export const relations = defineRelations(schema,(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
...
await db.query.groups.findMany({
with: {
verifiedUsers: true,
},
});
```
```ts
import * as p from "drizzle-orm/mysql-core";
export const users = p.mysqlTable("users", {
id: p.int().autoincrement().primaryKey(),
name: p.text().notNull(),
verified: p.boolean().notNull(),
});
export const groups = p.mysqlTable("groups", {
id: p.int().autoincrement().primaryKey(),
title: p.text().notNull(),
});
export const usersToGroups = p.mysqlTable(
"users_to_groups",
{
userId: p
.int("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
groupId: p
.int("group_id")
.notNull()
.references(() => groups.id, { onDelete: "cascade" }),
},
(t) => [p.primaryKey({ columns: [t.groupId, t.userId] })]
);
```
You can only specify filters on the target (to) table. So in this example, the where clause will only include columns from the `users` table since we are establishing a relation **TO** users
```ts {7}
export const relations = defineRelations(schema,(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
```
## ---
### Relations Parts
In a case you need to separate relations config into several parts you can use `defineRelationsPart` helpers
```ts
import { defineRelations, defineRelationsPart } from 'drizzle-orm';
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
and then you can provide it to the db instance
```ts
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
```
There are a few rules you would need to follow to make sure it `defineRelationsParts` works as expected
**Rule 1**: If you specify reltions with parts, when passing it to drizzle db function you would need to specify it in the right order(main relations goes first)
```ts
// â
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
// â
const db = drizzle(process.env.DB_URL, { relations: { ...part, ...relations } })
```
Even if there will be no type or runtime error, this is how "..." works with objects. As long as main relation
recursively infer all tables names, so it can be available in autocomplete. Here is an example:
```ts
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
Here `relations` and `part` can be represented and this object:
```json
// relations
{
"users": {"invitee": {...}, "posts": {...}},
// added here, so all tables from schema will exist in autocomplete
"posts": {}
}
// part
{
"posts": {"author": {...}}
}
```
Having `{ ...relations, ...part }` will result in
```json
{
"users": {"invitee": {...}, "posts": {...}},
"posts": {"author": {...}}
}
```
and having `{ ...relations, ...part }` will result in
```json
{
"users": {"invitee": {...}, "posts": {...}},
// As you can see in the final object, posts relations information will be lost
"posts": {}
}
```
**Rule 2**: You should have min relations, so drizzle can infer all of the table for autocomplete. If you want to have only parts, then
one of your parts should be empty, like this:
```ts
export const mainPart = defineRelationsPart(schema);
```
In this case, all tables will be inferred correctly, and you'll have complete information about your schema
## ---
### Performance
When working with relations in Drizzle ORM, especially in applications with
significant data or complex queries, optimizing database performance is crucial.
Indexes play a vital role in speeding up data retrieval, particularly when querying
related data. This section outlines recommended indexing strategies for each type
of relationship defined using Drizzle ORM.
##### One-to-one Relationships
In a one-to-one relationship, like the "user invites user" example or the
"user has profile info" example, the key performance consideration is efficient joining
of the related tables.
For optimal performance in one-to-one relationships, you should create an index
on the foreign key column in the table that is being referenced
(the "target" table in the relation).
When you query data with related one-to-one information, Drizzle performs a JOIN operation. An index on the foreign key column allows the database to quickly
locate the related row in the target table, significantly speeding up the join process.
**Example:**
```typescript
import * as p from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const profileInfo = p.mysqlTable('profile_info', {
id: p.int().autoincrement().primaryKey(),
userId: p.int('user_id').references(() => users.id),
metadata: p.json(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
})
}
}));
```
To optimize queries fetching user data along with their profile information,
you should create an index on the `userId` column in the `profile_info` table.
```typescript {13-15}
import * as p from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const profileInfo = p.mysqlTable('profile_info', {
id: p.int().autoincrement().primaryKey(),
userId: p.int('user_id').references(() => users.id),
metadata: p.json(),
}, (table) => [
p.index('profile_info_user_id_idx').on(table.userId)
]);
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
})
}
}));
```
```sql
CREATE INDEX `profile_info_user_id_idx` ON `profile_info` (`user_id`);
```
#### One-to-many Relationships
Similar to one-to-one relationships, one-to-many relations benefit significantly
from indexing to optimize join operations. Consider the "users and posts" example where one user can have many posts.
For one-to-many relationships, create an index on the foreign key column in the table that represents the "many" side of the relationship (the table with the foreign key referencing the "one" side).
When you fetch a user with their posts or posts with their authors, joins are performed.
Indexing the foreign key (`authorId` in `posts` table) allows the database to efficiently
retrieve all posts associated with a given user or quickly find the author of a post.
**Example:**
```typescript
import * as p from "drizzle-orm/mysql-core";
import { defineRelations } from 'drizzle-orm';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const posts = p.mysqlTable('posts', {
id: p.int().autoincrement().primaryKey(),
content: p.text(),
authorId: p.int('author_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
To optimize queries involving users and their posts, create an index on the `authorId` column in the `posts` table.
```typescript {13-15}
import * as p from "drizzle-orm/mysql-core";
import { defineRelations } from 'drizzle-orm';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const posts = p.mysqlTable('posts', {
id: p.int().autoincrement().primaryKey(),
content: p.text(),
authorId: p.int('author_id'),
}, (t) => [
p.index('posts_author_id_idx').on(t.authorId)
]);
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
```sql
CREATE INDEX `posts_author_id_idx` ON `posts` (`author_id`);
```
#### Many-to-many Relationships
Many-to-many relationships, implemented using junction tables, require a slightly
more nuanced indexing strategy to ensure optimal query performance.
Consider the "users and groups" example with the `usersToGroups` junction table.
For many-to-many relationships, it is generally recommended to create the following
indexes on the junction table:
1. **Index on each foreign key column individually:** This optimizes queries that
filter or join based on a single side of the relationship
(e.g., finding all groups for a user OR all users in a group).
2. **Composite index on both foreign key columns together:** This is crucial for
efficiently resolving the many-to-many relationship itself. It speeds up queries that need to find the connections between both entities.
When querying many-to-many relations, especially when using `through` in Drizzle ORM, the database needs to efficiently navigate the junction table.
- Indexes on individual foreign key columns (`userId`, `groupId` in `usersToGroups`) help when you are querying from one side to find the other (e.g., "find groups for a user").
- The composite index on `(userId, groupId)` in `usersToGroups` is particularly important for quickly finding all relationships defined in the junction table. This is used when Drizzle ORM resolves the `many-to-many` relation to fetch related entities.
**Example:**
In the "users and groups" example, the `usersToGroups` junction table connects `users` and `groups`.
```typescript
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const groups = p.mysqlTable('groups', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.mysqlTable(
'users_to_groups',
{
userId: p.int('user_id')
.notNull()
.references(() => users.id),
groupId: p.int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [p.primaryKey({ columns: [t.userId, t.groupId] })],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
To optimize queries for users and groups, create indexes on `usersToGroups` table as follows:
```typescript {26-28}
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const groups = p.mysqlTable('groups', {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.mysqlTable(
'users_to_groups',
{
userId: p.int('user_id')
.notNull()
.references(() => users.id),
groupId: p.int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [
p.primaryKey({ columns: [t.userId, t.groupId] }),
p.index('users_to_groups_user_id_idx').on(t.userId),
p.index('users_to_groups_group_id_idx').on(t.groupId),
p.index('users_to_groups_composite_idx').on(t.userId, t.groupId),
],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
```sql
CREATE INDEX `users_to_groups_user_id_idx` ON `users_to_groups` (`user_id`);
CREATE INDEX `users_to_groups_group_id_idx` ON `users_to_groups` (`group_id`);
CREATE INDEX `users_to_groups_composite_idx` ON `users_to_groups` (`user_id`,`group_id`);
```
By applying these indexing strategies, you can significantly improve the performance of your Drizzle ORM applications when working with relational data, especially as your data volume grows and your queries become more complex. Remember to choose the indexes that best suit your specific query patterns and application needs.
## ---
### Foreign keys
You might've noticed that `relations` look similar to foreign keys â they even have a `references` property. So what's the difference?
While foreign keys serve a similar purpose, defining relations between tables, they work on a different level compared to `relations`.
Foreign keys are a database level constraint, they are checked on every `insert`/`update`/`delete` operation and throw an error if a constraint is violated.
On the other hand, `relations` are a higher level abstraction, they are used to define relations between tables on the application level only.
They do not affect the database schema in any way and do not create foreign keys implicitly.
What this means is `relations` and foreign keys can be used together, but they are not dependent on each other.
You can define `relations` without using foreign keys (and vice versa), which allows them to be used with databases that do not support foreign keys.
The following two examples will work exactly the same in terms of querying the data using Drizzle relational queries.
```ts {15}
export const users = p.mysqlTable("users", {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const profileInfo = p.mysqlTable("profile_info", {
id: p.int().autoincrement().primaryKey(),
userId: p.int("user_id"),
metadata: p.json(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
}),
},
}));
```
```ts {15}
export const users = p.mysqlTable("users", {
id: p.int().autoincrement().primaryKey(),
name: p.text(),
});
export const profileInfo = p.mysqlTable("profile_info", {
id: p.int().autoincrement().primaryKey(),
userId: p.int("user_id").references(() => users.id),
metadata: p.json(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
}),
},
}));
```
### Disambiguating relations
Drizzle also provides the `alias` option as a way to disambiguate
relations when you define multiple of them between the same two tables. For
example, if you define a `posts` table that has the `author` and `reviewer`
relations.
```ts {19,22,29,34}
import { mysqlTable, int, text } from 'drizzle-orm/mysql-core';
import { defineRelations } from 'drizzle-orm';
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: text(),
});
export const posts = mysqlTable('posts', {
id: int().primaryKey(),
content: text(),
authorId: int('author_id'),
reviewerId: int('reviewer_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
users: {
posts: r.many.posts({
alias: "author",
}),
reviewedPosts: r.many.posts({
alias: "reviewer",
}),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "author",
}),
reviewer: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "reviewer",
}),
},
}));
```
Source: https://orm.drizzle.team/docs/mysql/rqb
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Drizzle Queries
Drizzle ORM is designed to be a thin typed layer on top of SQL.
We truly believe we've designed the best way to operate an SQL database from TypeScript and it's time to make it better.
Relational queries are meant to provide you with a great developer experience for querying
nested relational data from an SQL database, avoiding multiple joins and complex data mappings.
It is an extension to the existing schema definition and query builder.
You can opt-in to use it based on your needs.
We've made sure you have both the best-in-class developer experience and performance.
Inside relational queries, references to a table's columns must go through the callback parameter, not through the imported table object.
This applies to every clause that accepts a callback â `orderBy`, `where`.`RAW`, `extras` and `subqueries inside extras`.
The callback exposes the aliased table for the current query scope, which is required for correct SQL generation in nested or self-referential queries.
```ts
await db.query.posts.findMany({
orderBy: sql`${posts.id} asc`, // <- â direct table usage
with: {
comments: {
orderBy: sql`${comments.id} desc`, // <- â direct table usage
},
},
});
```
```ts
await db.query.users.findMany({
where: {
RAW: sql`${users.name} LIKE 'john%'`, // <- â direct table usage
},
});
```
```ts
await db.query.users.findMany({
extras: {
loweredName: sql`lower(${users.name})`, // <- â direct table usage
totalPostsCount: db.$count(posts, eq(posts.authorId, users.id)), // <- â direct table usage
},
});
```
```ts
await db.query.posts.findMany({
orderBy: (t) => sql`${t.id} asc`, // <- â
callback used
with: {
comments: {
orderBy: (t, { desc }) => desc(t.id), // <- â
callback used
},
},
});
```
```ts
await db.query.users.findMany({
where: {
RAW: (t) => sql`${t.name} LIKE 'john%'`, // <- â
callback used
},
});
```
```ts
await db.query.users.findMany({
extras: {
loweredName: (t, { sql }) => sql`lower(${t.name})`, // <- â
callback used
totalPostsCount: (t) => db.$count(posts, eq(posts.authorId, t.id)), // <- â
callback used
},
});
```
```typescript copy
import { drizzle } from "drizzle-orm/mysql2";
import { relations } from "./relations";
const db = drizzle(process.env.DATABASE_URL, { relations });
const result = await db.query.users.findMany({
with: {
posts: true,
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```typescript copy
import * as p from "drizzle-orm/mysql-core";
export const posts = p.mysqlTable("posts", {
id: p.int().primaryKey().autoincrement(),
content: p.text().notNull(),
authorId: p.int("author_id").notNull(),
});
export const users = p.mysqlTable("users", {
id: p.int().primaryKey().autoincrement(),
name: p.text().notNull(),
});
```
```typescript copy
import { defineRelations } from "drizzle-orm";
import { posts, users } from "./schema";
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
Relational queries are an extension to Drizzle's original **[query builder](/docs/mysql/select)**.
You need to provide all `tables` and `relations` from your schema file/files upon `drizzle()`
initialization and then just use the `db.query` API.
```ts
import { relations } from './relations';
import { drizzle } from 'drizzle-orm/mysql2';
const db = drizzle(process.env.DATABASE_URL, { relations });
await db.query.users.findMany(...);
```
```typescript copy
import { type AnyMySqlColumn, int, mysqlTable, primaryKey, text, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
invitedBy: int('invited_by').references((): AnyMySqlColumn => users.id),
});
export const groups = mysqlTable('groups', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
description: text(),
});
export const usersToGroups = mysqlTable('users_to_groups', {
id: int().primaryKey().autoincrement(),
userId: int('user_id').notNull().references(() => users.id),
groupId: int('group_id').notNull().references(() => groups.id),
}, (t) => [
primaryKey({ columns: [t.userId, t.groupId] })
]);
export const posts = mysqlTable('posts', {
id: int().primaryKey().autoincrement(),
content: text().notNull(),
authorId: int('author_id').references(() => users.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const comments = mysqlTable('comments', {
id: int().primaryKey().autoincrement(),
content: text().notNull(),
creator: int().references(() => users.id),
postId: int('post_id').references(() => posts.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const commentLikes = mysqlTable('comment_likes', {
id: int().primaryKey().autoincrement(),
creator: int().references(() => users.id),
commentId: int('comment_id').references(() => comments.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
```
```typescript copy
import { defineRelations } from 'drizzle-orm';
import * as schema from './schema';
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
posts: r.many.posts(),
},
groups: {
users: r.many.users(),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
comments: r.many.comments(),
},
comments: {
post: r.one.posts({
from: r.comments.postId,
to: r.posts.id,
}),
author: r.one.users({
from: r.comments.creator,
to: r.users.id,
}),
likes: r.many.commentLikes(),
},
commentLikes: {
comment: r.one.comments({
from: r.commentLikes.commentId,
to: r.comments.id,
}),
author: r.one.users({
from: r.commentLikes.creator,
to: r.users.id,
}),
},
})
);
```
Drizzle provides `.findMany()` and `.findFirst()` APIs.
### Find many
```typescript copy
const users = await db.query.users.findMany();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
}[];
```
### Find first
`.findFirst()` will add `limit 1` to the query.
```typescript copy
const user = await db.query.users.findFirst();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
};
```
### Include relations
`With` operator lets you combine data from multiple related tables and properly aggregate results.
**Getting all posts with comments:**
```typescript copy
const posts = await db.query.posts.findMany({
with: {
comments: true,
},
});
```
**Getting first post with comments:**
```typescript copy
const post = await db.query.posts.findFirst({
with: {
comments: true,
},
});
```
You can chain nested with statements as much as necessary.
For any nested `with` queries Drizzle will infer types using [Core Type API](/docs/mysql/goodies#type-api).
**Get all users with posts. Each post should contain a list of comments:**
```typescript copy
const users = await db.query.users.findMany({
with: {
posts: {
with: {
comments: true,
},
},
},
});
```
### Partial fields select
`columns` parameter lets you include or omit columns you want to get from the database.
Drizzle performs partial selects on the query level, no additional data is transferred from the database.
Keep in mind that **a single SQL statement is outputted by Drizzle.**
**Get all posts with just `id`, `content` and include `comments`:**
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});
```
**Get all posts without `content`:**
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
content: false,
},
});
```
When both `true` and `false` select options are present, all `false` options are ignored.
If you include the `name` field and exclude the `id` field, `id` exclusion will be redundant,
all fields apart from `name` would be excluded anyways.
**Exclude and Include fields in the same query:**
```typescript copy
const users = await db.query.users.findMany({
columns: {
name: true,
id: false //ignored
},
});
```
```ts
// result type
const users: {
name: string;
};
```
**Only include columns from nested relations:**
```typescript copy
const res = await db.query.users.findMany({
columns: {},
with: {
posts: true
}
});
```
```ts
// result type
const res: {
posts: {
id: number,
text: string
}
}[];
```
### Nested partial fields select
Just like with **[`partial select`](#partial-select)**, you can include or exclude columns of nested relations:
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});
```
### Select filters
Just like in our SQL-like query builder,
relational queries API lets you define filters and conditions with the list of our **[`operators`](/docs/mysql/operators)**.
You can either import them from `drizzle-orm` or use from the callback syntax:
```typescript copy
const users = await db.query.users.findMany({
where: {
id: 1
}
});
```
```sql {6}
select
`d0`.`id` as `id`,
`d0`.`name` as `name`
from `users` as `d0`
where
`d0`.`id` = 1
```
Find post with `id=1` and comments that were created before particular date:
```typescript copy
await db.query.posts.findMany({
where: {
id: 1,
},
with: {
comments: {
where: {
createdAt: { lt: new Date() },
},
},
},
});
```
**List of all filter operators**
```ts
where: {
OR: [],
AND: [],
NOT: {},
RAW: (table) => sql`${table.id} = 1`,
// filter by relations
[relation]: {},
// filter by columns
[column]: {
OR: [],
AND: [],
NOT: {},
eq: 1,
ne: 1,
gt: 1,
gte: 1,
lt: 1,
lte: 1,
in: [1],
notIn: [1],
like: "",
notLike: "",
isNull: true,
isNotNull: true
},
},
```
**Examples**
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
},
});
```
```sql {8}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
`d0`.`age` = 15;
```
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
name: 'John'
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(`d0`.`age` = 15)
AND (`d0`.`name` = 'John')
);
```
```ts
const response = await db.query.users.findMany({
where: {
OR: [
{
id: {
gt: 10,
},
},
{
name: {
like: "John%",
},
},
],
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(`d0`.`id` > 10)
OR (`d0`.`name` LIKE 'John%')
);
```
```ts
const response = await db.query.users.findMany({
where: {
NOT: {
id: {
gt: 10,
},
},
name: {
like: "John%",
},
},
});
```
```sql {9-10}
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(
(not(`d0`.`id` > 10))
AND (`d0`.`name` LIKE 'John%')
);
```
```ts
// schema.ts
import { int, json, mysqlTable, text, timestamp } from "drizzle-orm/mysql-core";
import { sql } from "drizzle-orm";
export const users = mysqlTable("users", {
id: int().primaryKey(),
name: text(),
email: text().notNull(),
age: int(),
createdAt: timestamp("created_at").defaultNow(),
lastLogin: timestamp("last_login"),
subscriptionEnd: timestamp("subscription_end"),
lastActivity: timestamp("last_activity"),
preferences: json(), // JSON column for user settings/preferences
interests: json().$type(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [{ RAW: (table) => sql`${table.name} LIKE 'john%'` }, { name: { like: "jane%" } }],
},
{
RAW: (table) => sql`${table.age} BETWEEN 25 AND 35`
},
],
},
}),
```
```sql
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`email` AS `email`,
`d0`.`age` AS `age`,
`d0`.`created_at` AS `createdAt`,
`d0`.`last_login` AS `lastLogin`,
`d0`.`subscription_end` AS `subscriptionEnd`,
`d0`.`last_activity` AS `lastActivity`,
`d0`.`preferences` AS `preferences`,
`d0`.`interests` AS `interests`
FROM
`users` AS `d0`
WHERE
(
(
(
(`d0`.`name` LIKE 'john%')
OR (`d0`.`name` LIKE 'jane%')
)
)
AND (`d0`.`age` BETWEEN 25 AND 35)
);
```
### Relations Filters
With Drizzle Relations, you can filter not only by the table you're querying but also by any table you include in the query.
**Example:** Get all `users` whose ID>10 and who have at least one post with content starting with "M"
```ts
const usersWithPosts = await db.query.usersTable.findMany({
where: {
id: {
gt: 10
},
posts: {
content: {
like: 'M%'
}
}
},
});
```
**Example:** Get all `users` with posts, only if user has at least 1 post
```ts
const response = await db.query.users.findMany({
with: {
posts: true,
},
where: {
posts: true,
},
});
```
### Limit & Offset
Drizzle ORM provides `limit` & `offset` API for queries and for the nested entities.
**Find 5 posts:**
```typescript copy
await db.query.posts.findMany({
limit: 5,
});
```
**Find posts and get 3 comments at most:**
```typescript copy
await db.query.posts.findMany({
with: {
comments: {
limit: 3,
},
},
});
```
`offset` now can be used in with tables as well!
```typescript
await db.query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // correct â
limit: 3,
},
},
});
```
Find posts with comments from the 6th to the 10th post:
```typescript copy
await db.query.posts.findMany({
with: {
comments: true,
},
limit: 5,
offset: 5,
});
```
### Order By
Drizzle provides API for ordering in the relational query builder.
You can use same ordering **[core API](/docs/mysql/select#order-by)** or use
`order by` operator from the callback with no imports.
When you use multiple `orderBy` statements in the same table, they will be included in the query in the same order in which you added them
```typescript copy
await db.query.posts.findMany({
orderBy: {
id: "asc",
},
});
```
**Order by `asc` + `desc`:**
```typescript copy
await db.query.posts.findMany({
orderBy: { id: "asc" },
with: {
comments: {
orderBy: { id: "desc" },
},
},
});
```
You can also use custom `sql` in order by statement:
```typescript copy
await db.query.posts.findMany({
orderBy: (t) => sql`${t.id} asc`,
with: {
comments: {
orderBy: (t, { desc }) => desc(t.id),
},
},
});
```
### Include custom fields
Relational query API lets you add custom additional fields.
It's useful when you need to retrieve data and apply additional functions to it.
As of now aggregations are not supported in `extras`, please use **[`core queries`](/docs/mysql/select)** for that.
```typescript copy {5}
import { sql } from 'drizzle-orm';
await db.query.users.findMany({
extras: {
loweredName: (table) => sql`lower(${table.name})`,
},
});
```
```typescript copy {3}
await db.query.users.findMany({
extras: {
loweredName: (users, { sql }) => sql`lower(${users.name})`,
},
})
```
`lowerName` as a key will be included to all fields in returned object.
If you will specify `.as("")` for any extras field - drizzle will ignore it
To retrieve all users with groups, but with the fullName field included (which is a concatenation of firstName and lastName),
you can use the following query with the Drizzle relational query builder.
```typescript copy
const res = await db.query.users.findMany({
extras: {
fullName: (users, { sql }) => sql`concat(${users.name}, " ", ${users.name})`,
},
with: {
groups: true,
},
});
```
```ts
// result type
const res: {
id: number;
name: string;
age: number | null;
invitedBy: number | null;
fullName: string;
groups: {
id: number;
name: string;
description: string | null;
}[];
}[]
```
To retrieve all posts with comments and add an additional field to calculate the size of the post content and the size of each comment content:
```typescript copy
const res = await db.query.posts.findMany({
extras: {
contentLength: (table, { sql }) => sql`char_length(${table.content})`,
},
with: {
comments: {
extras: {
commentSize: (table, { sql }) => sql`char_length(${table.content})`,
},
},
},
});
```
```ts
// result type
const res: {
id: number;
createdAt: Date;
content: string;
authorId: number | null;
contentLength: number;
comments: {
id: number;
createdAt: Date;
content: string;
creator: number | null;
postId: number | null;
commentSize: number;
}[];
}[]
```
### Include subqueries
You can also use subqueries within Relational Queries to leverage the power of custom SQL syntax
**Get users with posts and total posts count for each user**
```ts
import { posts } from './schema';
import { eq } from 'drizzle-orm';
await db.query.users.findMany({
with: {
posts: true
},
extras: {
totalPostsCount: (table) => db.$count(posts, eq(posts.authorId, table.id)),
}
});
```
```sql
SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`,
(
(
SELECT
count(*)
FROM
`posts`
WHERE
`posts`.`author_id` = `d0`.`id`
)
) AS `totalPostsCount`,
`posts`.`r` AS `posts`
FROM
`users` AS `d0`
LEFT JOIN LATERAL (
SELECT
coalesce(json_arrayagg(json_object('id', `id`, 'name', `name`, 'authorId', `authorId`)), json_array()) AS `r`
FROM
(
SELECT
`d1`.`id` AS `id`,
`d1`.`name` AS `name`,
`d1`.`author_id` AS `authorId`
FROM
`posts` AS `d1`
WHERE
`d0`.`id` = `d1`.`author_id`
) AS `t`
) AS `posts` ON TRUE;
```
### Prepared statements
Prepared statements are designed to massively improve query performance â [see here.](/docs/mysql/perf-queries)
In this section, you can learn how to define placeholders and execute prepared statements
using the Drizzle relational query builder.
##### **Placeholder in `where`**
```ts copy
const prepared = db.query.users.findMany({
where: { id: { eq: sql.placeholder("id") } },
with: {
posts: {
where: { id: 1 },
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ id: 1 });
```
##### **Placeholder in `limit`**
```ts copy
const prepared = db.query.users.findMany({
with: {
posts: {
limit: sql.placeholder("limit"),
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ limit: 1 });
```
##### **Placeholder in `offset`**
```ts copy
const prepared = db.query.users.findMany({
offset: sql.placeholder('offset'),
with: {
posts: true,
},
}).prepare();
const usersWithPosts = await prepared.execute({ offset: 1 });
```
##### **Multiple placeholders**
```ts copy
const prepared = db.query.users.findMany({
limit: sql.placeholder("uLimit"),
offset: sql.placeholder("uOffset"),
where: {
OR: [{ id: { eq: sql.placeholder("id") } }, { id: 3 }],
},
with: {
posts: {
where: { id: { eq: sql.placeholder("pid") } },
limit: sql.placeholder("pLimit"),
},
},
}).prepare();
const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 });
```
Source: https://orm.drizzle.team/docs/mysql/schemas
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# Table schemas
If you declare an entity within a schema, query builder will prepend schema names in queries:
`select * from schema.users`
```ts {3,5}
import { int, text, mysqlSchema } from "drizzle-orm/mysql-core";
export const mySchema = mysqlSchema("my_schema")
export const mySchemaUsers = mySchema.table("users", {
id: int("id").primaryKey().autoincrement(),
name: text("name"),
});
```
{/* TODO: ??? example > **Warning**
> If you will have tables with same names in different schemas then drizzle will respond with `never[]` error in result types and error from database
>
> In this case you may use [alias syntax](./joins#join-aliases-and-self-joins) */}
Source: https://orm.drizzle.team/docs/mysql/seed-functions
import Callout from '@mdx/Callout.astro';
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
# Generators
For now, specifying `arraySize` along with `isUnique` in generators that support it will result in unique values being generated (not unique arrays), which will then be packed into arrays.
## ---
### `default`
Generates the same given value each time the generator is called.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`defaultValue` |-- |`any`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.default({
// value you want to generate
defaultValue: "post content",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `valuesFromArray`
Generates values from given array
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`values` |-- |`any[]` \| `{ weight: number; values: any[] }[]`
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
title: funcs.valuesFromArray({
// Array of values you want to generate (can be an array of weighted values)
values: ["Title1", "Title2", "Title3", "Title4", "Title5"],
// Property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `intPrimaryKey`
Generates sequential integers starting from 1.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |-- |-- |--
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
id: funcs.intPrimaryKey(),
},
},
}));
```
### `number`
Generates numbers with a floating point within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`precision` |`100` |`number`
| |`maxValue` |``` `precision * 1000` if isUnique equals false``` ``` `precision * count` if isUnique equals true``` |`number`
| |`minValue` |`-maxValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitPrice: funcs.number({
// lower border of range.
minValue: 10,
// upper border of range.
maxValue: 120,
// precision of generated number:
// precision equals 10 means that values will be accurate to one tenth (1.2, 34.6);
// precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67).
precision: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `int`
Generates integers within the given range
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxValue` |``` `1000` if isUnique equals false``` ``` `count * 10` if isUnique equals true``` |`number \| bigint`
| |`minValue` |`-maxValue` |`number \| bigint`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
unitsInStock: funcs.int({
// lower border of range.
minValue: 0,
// lower border of range.
maxValue: 100,
// property that controls if generated values gonna be unique or not.
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `boolean`
Generates boolean values (true or false)
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
isAvailable: funcs.boolean({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `date`
Generates a date within the given range
| | param | default | type
|:-| :-------- | :-------------- | :--------
| |`minDate` |``` `'2024-05-08' - 4 years` - if maxDate is not set``` ``` `maxDate - 8 years` - if maxDate is set ``` |`string \| Date`
| |`maxDate` |``` `'2024-05-08' + 4 years` - if minDate is not set``` ``` `minDate + 8 years` - if minDate is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
If only one of the parameters (`minDate` or `maxDate`) is provided, the unspecified parameter will be calculated by adding or subtracting 8 years to/from the specified one
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthDate: funcs.date({
// lower border of range.
minDate: "1990-01-01",
// upper border of range.
maxDate: "2010-12-31",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `time`
Generates time in 24-hour format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |`'00:00:00.000Z'` |`string \| Date`
| |`max` |`'23:59:59.999Z'` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthTime: funcs.time({
// lower border of range.
min: "11:12:13.141",
// upper border of range.
max: "15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `timestamp`
Generates timestamps
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.timestamp({
// lower border of range.
min: "2025-03-07T11:12:13.141",
// upper border of range.
max: "2025-03-08T15:16:17.181",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `datetime`
Generates datetime objects
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`min` |``` `'2024-05-08' - 2 years` - if max is not set``` ``` `max - 4 years` - if max is set ``` |`string \| Date`
| |`max` |``` `'2024-05-08' + 2 years` - if min is not set``` ``` `min + 4 years` - if min is set ``` |`string \| Date`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
orders: {
columns: {
shippedDate: funcs.datetime({
// lower border of range.
min: "2025-03-07 13:12:13Z",
// upper border of range.
max: "2025-03-09 15:12:13Z",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `year`
Generates years in `YYYY` format
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
birthYear: funcs.year({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `json`
Generates JSON objects with a fixed structure
```ts
{ email, name, isGraduated, hasJob, salary, startedWorking, visitedCountries}
// or
{ email, name, isGraduated, hasJob, visitedCountries }
```
> The JSON structure will be picked randomly
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
metadata: funcs.json({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `interval`
Generates time intervals.
Example of a generated value: `1 year 12 days 5 minutes`
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
timeSpentOnWebsite: funcs.interval({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `string`
Generates random strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
hashedPassword: funcs.string({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `uuid`
Generates v4 UUID strings
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
products: {
columns: {
id: funcs.uuid({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `firstName`
Generates a person's first name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
firstName: funcs.firstName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `lastName`
Generates a person's last name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
lastName: funcs.lastName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `fullName`
Generates a person's full name
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
fullName: funcs.fullName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `email`
Generates unique email addresses
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
email: funcs.email({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `phoneNumber`
Generates unique phone numbers
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`template` |-- |`string`
| |`prefixes` |[Used dataset for prefixes](https://github.com/OleksiiKH0240/drizzle-orm/blob/main/drizzle-seed/src/datasets/phonesInfo.ts) |`string[]`
| |`generatedDigitsNumbers` | `7` - `if prefixes was defined` |`number \| number[]`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
//generate phone number using template property
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `template` - phone number template, where all '#' symbols will be substituted with generated digits.
template: "+(380) ###-####",
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
//generate phone number using prefixes and generatedDigitsNumbers properties
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: 7,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
```ts
import { seed } from "drizzle-seed";
// generate phone number using prefixes and generatedDigitsNumbers properties but with different generatedDigitsNumbers for prefixes
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
phoneNumber: funcs.phoneNumber({
// `prefixes` - array of any string you want to be your phone number prefixes.(not compatible with `template` property)
prefixes: ["+380 99", "+380 67", "+1"],
// `generatedDigitsNumbers` - number of digits that will be added at the end of prefixes.(not compatible with `template` property)
generatedDigitsNumbers: [7, 7, 10],
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `country`
Generates country's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
country: funcs.country({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `city`
Generates city's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
city: funcs.city({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `streetAddress`
Generates street address
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
streetAddress: funcs.streetAddress({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: false,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `jobTitle`
Generates job titles
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
jobTitle: funcs.jobTitle({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `postcode`
Generates postal codes
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
postcode: funcs.postcode({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `state`
Generates US states
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
state: funcs.state({
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `companyName`
Generates random company's names
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
users: {
columns: {
company: funcs.companyName({
// `isUnique` - property that controls whether the generated values will be unique or not
isUnique: true,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `loremIpsum`
Generates `lorem ipsum` text sentences.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`sentencesCount` | 1 |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
posts: {
columns: {
content: funcs.loremIpsum({
// `sentencesCount` - number of sentences you want to generate as one generated value(string).
sentencesCount: 2,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `point`
Generates 2D points within specified ranges for x and y coordinates.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxXValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minXValue` |`-maxXValue` |`number`
| |`maxYValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minYValue` |`-maxYValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
triangles: {
columns: {
pointCoords: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minXValue` - lower bound of range for x coordinate.
minXValue: -5,
// `maxXValue` - upper bound of range for x coordinate.
maxXValue: 20,
// `minYValue` - lower bound of range for y coordinate.
minYValue: 0,
// `maxYValue` - upper bound of range for y coordinate.
maxYValue: 30,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `line`
Generates 2D lines within specified ranges for a, b and c parameters of line.
```
line equation: a*x + b*y + c = 0
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`maxAValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minAValue` |`-maxAValue` |`number`
| |`maxBValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minBValue` |`-maxBValue` |`number`
| |`maxCValue` |``` `10 * 1000` if isUnique equals false``` ``` `10 * count` if isUnique equals true``` |`number`
| |`minCValue` |`-maxCValue` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
lines: {
columns: {
lineParams: funcs.point({
// `isUnique` - property that controls if generated values gonna be unique or not.
isUnique: true,
// `minAValue` - lower bound of range for a parameter.
minAValue: -5,
// `maxAValue` - upper bound of range for x parameter.
maxAValue: 20,
// `minBValue` - lower bound of range for y parameter.
minBValue: 0,
// `maxBValue` - upper bound of range for y parameter.
maxBValue: 30,
// `minCValue` - lower bound of range for y parameter.
minCValue: 0,
// `maxCValue` - upper bound of range for y parameter.
maxCValue: 10,
// number of elements in each one-dimensional array.
// (If specified, arrays will be generated.)
arraySize: 3
}),
},
},
}));
```
### `bitString`
Generates bit strings based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`dimensions` |`database column bit-length` |`number`
| |`arraySize` |-- |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
bitStringTable: {
columns: {
bit: funcs.bitString({
// desired length of each bit string (e.g., `dimensions = 3` produces values like `'010'`).
dimensions: 12,
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
}),
},
},
}));
```
### `inet`
Generates ip addresses based on specified parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`ipAddress` |`'ipv4'` |`'ipv4' \| 'ipv6'`
| |`includeCidr`|`true` |`boolean`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
inetTable: {
columns: {
inet: funcs.inet({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// type of IP address to generate â either "ipv4" or "ipv6";
ipAddress: "ipv4",
// determines whether generated IPs include a CIDR suffix.
includeCidr: true,
}),
},
},
}));
```
### `geometry`
Generates geometry objects based on the given parameters.
Currently, if you set arraySize to a value greater than 1
or try to insert more than one `geometry point` element into a `geometry(point, 0)[]` column in MySQL via drizzle-orm,
youâll encounter an error.
This bug is already in the backlog.
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, mysqlTable } from 'drizzle-orm/mysql-core';
const geometryTable = mysqlTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(3),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// currently arraySize with values > 1 are not supported
arraySize: 3,
}),
},
},
}));
```
```ts {13}
import { seed } from "drizzle-seed";
import { geometry, mysqlTable } from 'drizzle-orm/mysql-core';
const geometryTable = mysqlTable('geometry_table', {
geometryArray: geometry('geometry_array', { type: 'point', srid: 0 }).array(1),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryArray: funcs.geometry({
// will work as expected
arraySize: 1,
}),
},
},
}));
```
Currently, if you set the SRID of a `geometry(point)` column to anything other than 0 (for example, 4326) in your drizzle-orm table declaration,
youâll encounter an error during the seeding process.
This bug is already in the backlog.
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, mysqlTable } from 'drizzle-orm/mysql-core';
const geometryTable = mysqlTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 4326 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
```ts {5}
import { seed } from "drizzle-seed";
import { geometry, mysqlTable } from 'drizzle-orm/mysql-core';
const geometryTable = mysqlTable('geometry_table', {
geometryColumn: geometry('geometry_column', { type: 'point', srid: 0 }),
});
await seed(db, { geometryTable }, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryColumn: funcs.geometry({
srid: 4326,
}),
},
},
}));
```
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`type` |`'point'` |`'point'`
| |`srid` |`4326` |`4326 \| 3857`
| |`decimalPlaces` |`6` |`1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
geometryTable: {
columns: {
geometryPointTuple: funcs.geometry({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 1,
// geometry type to generate; currently only `'point'` is supported;
type: "point",
// Spatial Reference System Identifier: determines what type of point will be generated - either `4326` or `3857`;
srid: 4326,
// number of decimal places for points when `srid` is `4326` (e.g., `decimalPlaces = 3` produces values like `'point(30.723 46.482)'`).
decimalPlaces: 5,
}),
},
},
}));
```
### `vector`
Generates vectors based on the provided parameters.
| | param | default | type
|:-| :-------- | :-------- | :--------
| |`isUnique` |`database column uniqueness` |`boolean`
| |`arraySize` |-- |`number`
| |`decimalPlaces` |`2` |`number`
| |`dimensions` |`database columnâs dimensions` |`number`
| |`minValue` |`-1000` |`number`
| |`maxValue` |`1000` |`number`
```ts
import { seed } from "drizzle-seed";
await seed(db, schema, { count: 1000 }).refine((funcs) => ({
vectorTable: {
columns: {
vector: funcs.vector({
// property that controls if generated values gonna be unique or not;
isUnique: true,
// number of elements in each one-dimensional array (If specified, arrays will be generated);
arraySize: 3,
// number of decimal places for each vector element (e.g., `decimalPlaces = 3` produces values like `1.123`);
decimalPlaces: 5,
// number of elements in each generated vector (e.g., `dimensions = 3` produces values like `[1,2,3]`);
dimensions: 12,
// minimum allowed value for each vector element;
minValue: -100,
// maximum allowed value for each vector element.
maxValue: 100,
}),
},
},
}));
```
Source: https://orm.drizzle.team/docs/mysql/seed-overview
import Npm from "@mdx/Npm.astro";
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
import Callout from '@mdx/Callout.astro';
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
# Drizzle Seed
`drizzle-seed` is a TypeScript library that helps you generate deterministic, yet realistic,
fake data to populate your database. By leveraging a seedable pseudorandom number generator (pRNG),
it ensures that the data you generate is consistent and reproducible across different runs.
This is especially useful for testing, development, and debugging purposes.
#### What is Deterministic Data Generation?
Deterministic data generation means that the same input will always produce the same output.
In the context of `drizzle-seed`, when you initialize the library with the same seed number,
it will generate the same sequence of fake data every time. This allows for predictable and repeatable data sets.
#### Pseudorandom Number Generator (pRNG)
A pseudorandom number generator is an algorithm that produces a sequence of numbers
that approximates the properties of random numbers. However, because it's based on an initial value
called a seed, you can control its randomness. By using the same seed, the pRNG will produce the
same sequence of numbers, making your data generation process reproducible.
#### Benefits of Using a pRNG:
- Consistency: Ensures that your tests run on the same data every time.
- Debugging: Makes it easier to reproduce and fix bugs by providing a consistent data set.
- Collaboration: Team members can share seed numbers to work with the same data sets.
With drizzle-seed, you get the best of both worlds: the ability to generate realistic fake data and the control to reproduce it whenever needed.
## Installation
drizzle-seed
## Basic Usage
In this example we will create 10 users with random names and ids
```ts {12}
import { mysqlTable, int, varchar } from "drizzle-orm/mysql-core";
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
const users = mysqlTable("users", {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users });
}
main();
```
## Options
**`count`**
By default, the `seed` function will create 10 entities.
However, if you need more for your tests, you can specify this in the seed options object
```ts
await seed(db, schema, { count: 1000 });
```
**`seed`**
If you need a seed to generate a different set of values for all subsequent runs, you can define a different number
in the `seed` option. Any new number will generate a unique set of values
```ts
await seed(db, schema, { seed: 12345 });
```
## Reset database
With `drizzle-seed`, you can easily reset your database and seed it with new values, for example, in your test suites
```ts
// path to a file with schema you want to reset
import * as schema from "./schema.ts";
import { reset } from "drizzle-seed";
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await reset(db, schema);
}
main();
```
Different dialects will have different strategies for database resetting. For MySQL:
For MySQL, the `drizzle-seed` package will first disable `FOREIGN_KEY_CHECKS` to ensure the next step won't fail, and then generate `TRUNCATE` statements to empty the content of all tables
```sql
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE tableName1;
TRUNCATE tableName2;
...
SET FOREIGN_KEY_CHECKS = 1;
```
## Refinements
In case you need to change the behavior of the seed generator functions that `drizzle-seed` uses by default, you can specify your own implementation and even use your own list of values for the seeding process
`.refine` is a callback that receives a list of all available generator functions from `drizzle-seed`. It should return an object with keys representing the tables you want to refine, defining their behavior as needed.
Each table can specify several properties to simplify seeding your database:
- `columns`: Refine the default behavior of each column by specifying the required generator function, or exclude the column from seeding by specifying `false`.
- `count`: Specify the number of rows to insert into the database. By default, it's 10. If a global count is defined in the `seed()` options, the count defined here will override it for this specific table.
- `with`: Define how many referenced entities to create for each parent table if you want to generate associated entities.
You can also specify a weighted random distribution for the number of referenced values you want to create. For details on this API, you can refer to [Weighted Random docs](#weighted-random) docs section
**API**
```ts
await seed(db, schema).refine((f) => ({
users: {
columns: {},
count: 10,
with: {
posts: 10
}
},
}));
```
Let's check a few examples with an explanation of what will happen:
```ts filename='schema.ts'
import { mysqlTable, int, varchar } from "drizzle-orm/mysql-core";
export const users = mysqlTable("users", {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
});
export const posts = mysqlTable("posts", {
id: int().primaryKey().autoincrement(),
description: varchar({ length: 255 }),
userId: int().references(() => users.id),
});
```
**Example 1**: Seed only the `users` table with 20 entities and with refined seed logic for the `name` column
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: f.fullName(),
},
count: 20
}
}));
}
main();
```
**Example 2**: Seed only the `users` table, excluding the `name` column from seeding
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: false, // the name column will not be seeded, allowing the database to use its default value.
}
}
}));
}
main();
```
**Example 3**: Seed the `users` table with 20 entities and add 10 `posts` for each `user` by seeding the `posts` table and creating a reference from `posts` to `users`
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 20,
with: {
posts: 10
}
}
}));
}
main();
```
**Example 4**: Seed the `users` table with 5 entities and populate the database with 100 `posts` without connecting them to the `users` entities. Refine `id` generation for `users` so
that it will give any int from `10000` to `20000` and remains unique, and refine `posts` to retrieve values from a self-defined array
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 5,
columns: {
id: f.int({
minValue: 10000,
maxValue: 20000,
isUnique: true,
}),
}
},
posts: {
count: 100,
columns: {
description: f.valuesFromArray({
values: [
"The sun set behind the mountains, painting the sky in hues of orange and purple",
"I can't believe how good this homemade pizza turned out!",
"Sometimes, all you need is a good book and a quiet corner.",
"Who else thinks rainy days are perfect for binge-watching old movies?",
"Tried a new hiking trail today and found the most amazing waterfall!",
// ...
],
})
}
}
}));
}
main();
```
There are many more possibilities that we will define in these docs, but for now, you can explore a few sections in this documentation. Check the [Generators](/docs/mysql/seed-functions) section to get familiar with all the available generator functions you can use.
A particularly great feature is the ability to use weighted randomization, both for generator values created for a column and for determining the number of related entities that can be generated by `drizzle-seed`.
Please check [Weighted Random docs](#weighted-random) for more info.
## Weighted Random
There may be cases where you need to use multiple datasets with a different priority that should be inserted into your database during the seed stage. For such cases, drizzle-seed provides an API called weighted random
The Drizzle Seed package has a few places where weighted random can be used:
- Columns inside each table refinements
- The `with` property, determining the amount of related entities to be created
Let's check an example for both:
```ts filename="schema.ts"
import { mysqlTable, int, varchar, double } from "drizzle-orm/mysql-core";
export const orders = mysqlTable(
"orders",
{
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
quantityPerUnit: varchar().notNull(),
unitPrice: double().notNull(),
unitsInStock: int().notNull(),
unitsOnOrder: int().notNull(),
reorderLevel: int().notNull(),
discontinued: int().notNull(),
}
);
export const details = mysqlTable(
"details",
{
unitPrice: double().notNull(),
quantity: int().notNull(),
discount: double().notNull(),
orderId: int()
.notNull()
.references(() => orders.id, { onDelete: "cascade" }),
}
);
```
**Example 1**: Refine the `unitPrice` generation logic to generate `5000` random prices, with a 30% chance of prices between 10-100 and a 70% chance of prices between 100-300
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
count: 5000,
columns: {
unitPrice: f.weightedRandom(
[
{
weight: 0.3,
value: funcs.int({ minValue: 10, maxValue: 100 })
},
{
weight: 0.7,
value: funcs.number({ minValue: 100, maxValue: 300, precision: 100 })
}
]
),
}
}
}));
}
main();
```
**Example 2**: For each order, generate 1 to 3 details with a 60% chance, 5 to 7 details with a 30% chance, and 8 to 10 details with a 10% chance
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/mysql2";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3] },
{ weight: 0.3, count: [5, 6, 7] },
{ weight: 0.1, count: [8, 9, 10] },
]
}
}
}));
}
main();
```
## Complex example
```ts
import { seed } from "drizzle-seed";
import * as schema from "./schema.ts";
const main = async () => {
const titlesOfCourtesy = ["Ms.", "Mrs.", "Dr."];
const unitsOnOrders = [0, 10, 20, 30, 50, 60, 70, 80, 100];
const reorderLevels = [0, 5, 10, 15, 20, 25, 30];
const quantityPerUnit = [
"100 - 100 g pieces",
"100 - 250 g bags",
"10 - 200 g glasses",
"10 - 4 oz boxes",
"10 - 500 g pkgs.",
"10 - 500 g pkgs."
];
const discounts = [0.05, 0.15, 0.2, 0.25];
await seed(db, schema).refine((funcs) => ({
customers: {
count: 10000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" }),
fax: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
employees: {
count: 200,
columns: {
firstName: funcs.firstName(),
lastName: funcs.lastName(),
title: funcs.jobTitle(),
titleOfCourtesy: funcs.valuesFromArray({ values: titlesOfCourtesy }),
birthDate: funcs.date({ minDate: "2010-12-31", maxDate: "2010-12-31" }),
hireDate: funcs.date({ minDate: "2010-12-31", maxDate: "2024-08-26" }),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
country: funcs.country(),
homePhone: funcs.phoneNumber({ template: "(###) ###-####" }),
extension: funcs.int({ minValue: 428, maxValue: 5467 }),
notes: funcs.loremIpsum()
}
},
orders: {
count: 50000,
columns: {
shipVia: funcs.int({ minValue: 1, maxValue: 3 }),
freight: funcs.number({ minValue: 0, maxValue: 1000, precision: 100 }),
shipName: funcs.streetAddress(),
shipCity: funcs.city(),
shipRegion: funcs.state(),
shipPostalCode: funcs.postcode(),
shipCountry: funcs.country()
},
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3, 4] },
{ weight: 0.2, count: [5, 6, 7, 8, 9, 10] },
{ weight: 0.15, count: [11, 12, 13, 14, 15, 16, 17] },
{ weight: 0.05, count: [18, 19, 20, 21, 22, 23, 24, 25] },
]
}
},
suppliers: {
count: 1000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
products: {
count: 5000,
columns: {
name: funcs.companyName(),
quantityPerUnit: funcs.valuesFromArray({ values: quantityPerUnit }),
unitPrice: funcs.weightedRandom(
[
{
weight: 0.5,
value: funcs.int({ minValue: 3, maxValue: 300 })
},
{
weight: 0.5,
value: funcs.number({ minValue: 3, maxValue: 300, precision: 100 })
}
]
),
unitsInStock: funcs.int({ minValue: 0, maxValue: 125 }),
unitsOnOrder: funcs.valuesFromArray({ values: unitsOnOrders }),
reorderLevel: funcs.valuesFromArray({ values: reorderLevels }),
discontinued: funcs.int({ minValue: 0, maxValue: 1 })
}
},
details: {
columns: {
unitPrice: funcs.number({ minValue: 10, maxValue: 130 }),
quantity: funcs.int({ minValue: 1, maxValue: 130 }),
discount: funcs.weightedRandom(
[
{ weight: 0.5, value: funcs.valuesFromArray({ values: discounts }) },
{ weight: 0.5, value: funcs.default({ defaultValue: 0 }) }
]
)
}
}
}));
}
main();
```
```ts
import type { AnyColumn } from "drizzle-orm";
import { int, numeric, mysqlTable, text, timestamp, varchar } from "drizzle-orm/mysql-core";
export const customers = mysqlTable('customer', {
id: varchar({ length: 256 }).primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text(),
region: text(),
country: text().notNull(),
phone: text().notNull(),
fax: text(),
});
export const employees = mysqlTable(
'employee',
{
id: int().primaryKey().autoincrement(),
lastName: text().notNull(),
firstName: text(),
title: text().notNull(),
titleOfCourtesy: text().notNull(),
birthDate: timestamp().notNull(),
hireDate: timestamp().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text().notNull(),
country: text().notNull(),
homePhone: text().notNull(),
extension: int().notNull(),
notes: text().notNull(),
reportsTo: int().references((): AnyColumn => employees.id),
photoPath: text(),
},
);
export const orders = mysqlTable('order', {
id: int().primaryKey().autoincrement(),
orderDate: timestamp().notNull(),
requiredDate: timestamp().notNull(),
shippedDate: timestamp(),
shipVia: int().notNull(),
freight: numeric().notNull(),
shipName: text().notNull(),
shipCity: text().notNull(),
shipRegion: text(),
shipPostalCode: text(),
shipCountry: text().notNull(),
customerId: text().notNull().references(() => customers.id, { onDelete: 'cascade' }),
employeeId: int().notNull().references(() => employees.id, { onDelete: 'cascade' }),
});
export const suppliers = mysqlTable('supplier', {
id: int().primaryKey().autoincrement(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
region: text(),
postalCode: text().notNull(),
country: text().notNull(),
phone: text().notNull(),
});
export const products = mysqlTable('product', {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 255 }).notNull(),
quantityPerUnit: text().notNull(),
unitPrice: numeric().notNull(),
unitsInStock: int().notNull(),
unitsOnOrder: int().notNull(),
reorderLevel: int().notNull(),
discontinued: int().notNull(),
supplierId: int().notNull().references(() => suppliers.id, { onDelete: 'cascade' }),
});
export const details = mysqlTable('order_detail', {
unitPrice: numeric().notNull(),
quantity: int().notNull(),
discount: numeric().notNull(),
orderId: int().notNull().references(() => orders.id, { onDelete: 'cascade' }),
productId: int().notNull().references(() => products.id, { onDelete: 'cascade' }),
});
```
## Limitations
#### Types limitations for `with`
Due to certain TypeScript limitations and the current API in Drizzle, it is not possible to properly infer references between tables, especially when circular dependencies between tables exist.
This means the `with` option will display all tables in the schema, and you will need to manually select the one that has a one-to-many relationship
The `with` option works for one-to-many relationships. For example, if you have one `user` and many `posts`, you can use users `with` posts, but you cannot use posts `with` users
#### Type limitations for the third parameter in Drizzle tables:
Currently, we do not have type support for the third parameter in Drizzle tables. While it will work at runtime, it will not function correctly at the type level
Source: https://orm.drizzle.team/docs/mysql/seed-versioning
import Tab from "@mdx/Tab.astro";
import Tabs from "@mdx/Tabs.astro";
import Callout from "@mdx/Callout.astro";
import TableWrapper from "@mdx/TableWrapper.astro";
# Versioning
`drizzle-seed` uses versioning to manage outputs for static and dynamic data. To ensure true
determinism, ensure that values remain unchanged when using the same `seed` number. If changes are made to
static data sources or dynamic data generation logic, the version will be updated, allowing
you to choose between sticking with the previous version or using the latest.
You can upgrade to the latest `drizzle-seed` version for new features, such as additional
generators, while maintaining deterministic outputs with a previous version if needed. This
is particularly useful when you need to rely on existing deterministic data while accessing new functionality.
```ts
await seed(db, schema, { version: '2' });
```
## History
| api version | npm version | Changed generators |
| :-------------- | :-------------- | :------------- |
| `v1` | `0.1.1` | |
| `v2` | `0.2.1` |`string()`, `interval({ isUnique: true })` |
| `v3` | `0.4.0` |`hash generating function` |
| `v4 (LTS) ` | `1.0.0-beta.8` |`uuid` |
> This is not an actual API change; it is just an example of how we will proceed with `drizzle-seed` versioning.
For example, `lastName` generator was changed, and new version, `V2`, of this generator became available.
Later, `firstName` generator was changed, making `V3` version of this generator available.
| | `V1` | `V2` | `V3(latest)` |
| :--------------: | :--------------: | :-------------: | :--------------: |
| **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | |
| **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` |
##### Use the `firstName` generator of version 3 and the `lastName` generator of version 2
```ts
await seed(db, schema);
```
If you are not ready to use latest generator version right away, you can specify max version to use
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 2
```ts
await seed(db, schema, { version: '2' });
```
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 1.
```ts
await seed(db, schema, { version: '1' });
```
## Version 2
#### Unique `interval` generator was changed
An older version of the generator could produce intervals like `1 minute 60 seconds` and `2 minutes 0 seconds`, treating them as distinct intervals.
However, when the `1 minute 60 seconds` interval is inserted into a MySQL database, it is automatically converted to `2 minutes 0 seconds`. As a result, attempting to insert the `2 minutes 0 seconds` interval into a unique column afterwards will cause an error
You will be affected, if you use the unique `interval` generator in your seeding script, as shown in the script below:
```ts
import { binary, char, mysqlTable, text, varbinary, varchar } from 'drizzle-orm/mysql-core';
import { drizzle } from 'drizzle-orm/mysql2';
import { seed } from "drizzle-seed";
const intervals = mysqlTable('intervals', {
interval1: char({ length: 255 }).unique(),
interval2: char({ length: 255 }),
interval3: varchar({ length: 255 }).unique(),
interval4: varchar({ length: 255 }),
interval5: binary({ length: 255 }).unique(),
interval6: binary({ length: 255 }),
interval7: varbinary({ length: 255 }).unique(),
interval8: varbinary({ length: 255 }),
interval9: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals }, { version: '2' }).refine((f) => ({
intervals: {
columns: {
interval: f.interval({ isUnique: true }),
interval1: f.interval({ isUnique: true }),
interval2: f.interval({ isUnique: true }),
interval3: f.interval({ isUnique: true }),
interval4: f.interval({ isUnique: true }),
interval5: f.interval({ isUnique: true }),
interval6: f.interval({ isUnique: true }),
interval7: f.interval({ isUnique: true }),
interval8: f.interval({ isUnique: true }),
interval9: f.interval({ isUnique: true }),
},
},
}));
}
main();
```
#### `string` generators were changed: both non-unique and unique
Ability to generate a unique string based on the length of the text column (e.g., `varchar(20)`)
You will be affected, if your table includes a column of a text-like type with a maximum length parameter or a unique column of a text-like type:
```ts
import { binary, char, mysqlTable, varbinary, varchar } from 'drizzle-orm/mysql-core';
import { drizzle } from 'drizzle-orm/mysql2';
import { seed } from "drizzle-seed";
const strings = mysqlTable('strings', {
string1: char({ length: 255 }).unique(),
string2: char({ length: 255 }),
string3: varchar({ length: 255 }).unique(),
string4: varchar({ length: 255 }),
string5: binary({ length: 255 }).unique(),
string6: binary({ length: 255 }),
string7: varbinary({ length: 255 }).unique(),
string8: varbinary({ length: 255 }),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings });
}
main();
```
You will be affected, if you use the `string` generator in your seeding script, as shown in the script below:
```ts
import { binary, char, mysqlTable, text, varbinary, varchar } from 'drizzle-orm/mysql-core';
import { drizzle } from 'drizzle-orm/mysql2';
import { seed } from "drizzle-seed";
const strings = mysqlTable('strings', {
string1: char({ length: 255 }).unique(),
string2: char({ length: 255 }),
string3: char({ length: 255 }),
string4: varchar({ length: 255 }).unique(),
string5: varchar({ length: 255 }),
string6: varchar({ length: 255 }),
string7: binary({ length: 255 }).unique(),
string8: binary({ length: 255 }),
string9: binary({ length: 255 }),
string10: varbinary({ length: 255 }).unique(),
string11: varbinary({ length: 255 }),
string12: varbinary({ length: 255 }),
string13: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings }).refine((f) => ({
strings: {
columns: {
string1: f.string({ isUnique: true }),
string2: f.string({ isUnique: true }),
string3: f.string(),
string4: f.string({ isUnique: true }),
string5: f.string({ isUnique: true }),
string6: f.string(),
string7: f.string({ isUnique: true }),
string8: f.string({ isUnique: true }),
string9: f.string(),
string10: f.string({ isUnique: true }),
string11: f.string({ isUnique: true }),
string12: f.string(),
string13: f.string({ isUnique: true }),
},
},
}));
}
main();
```
## Version 3
#### `hash generating function was changed`
The previous version of the hash generating function generated different hashes depending on whether Bun or Node.js was used, and hashes also varied across versions of Node.js.
The new hash generating function will generate the same hash regardless of the version of Node.js or Bun, resulting in deterministic data generation across all versions.
All generators will output different values compared to the previous version, even with the same seed number.
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '3' });
```
**Switch to the old version**
The previous version of hash generating function is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 hash generating function:
```ts
await seed(db, schema, { version: '2' });
```
## Version 4
#### `uuid` generator was changed
UUID values generated by the old version of the `uuid` generator fail Zodâs v4 UUID validation.
example
```ts
import { createSelectSchema } from 'drizzle-zod';
import { seed } from 'drizzle-seed';
await seed(db, { uuidTest: schema.uuidTest }, { count: 1 }).refine((funcs) => ({
uuidTest: {
columns: {
col1: funcs.uuid()
}
}
})
);
const uuidSelectSchema = createSelectSchema(schema.uuidTest);
const res = await db.select().from(schema.uuidTest);
// the line below will throw an error when using old version of uuid generator
uuidSelectSchema.parse(res[0]);
```
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '4' });
```
**Switch to the old version**
The previous version of `uuid` generator is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '2' });
```
To use the v3 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '3' });
```
Source: https://orm.drizzle.team/docs/mysql/select
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import $count from '@mdx/$count-mysql.mdx';
# SQL Select
Drizzle provides you the most SQL-like way to fetch data from your database, while remaining type-safe and composable.
It natively supports mostly every query feature and capability of every dialect,
and whatever it doesn't support yet, can be added by the user with the powerful [`sql`](/docs/mysql/sql) operator.
For the following examples, let's assume you have a `users` table defined like this:
```typescript
import { mysqlTable, text, int } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int(),
});
```
### Basic select
Select all rows from a table including all columns:
```typescript
const result = await db.select().from(users);
/*
{
id: number;
name: string;
age: number | null;
}[]
*/
```
```sql
select `id`, `name`, `age` from `users`;
```
Notice that the result type is inferred automatically based on the table definition, including columns nullability.
Drizzle always explicitly lists columns in the `select` clause instead of using `select *`.
This is required internally to guarantee the fields order in the query result, and is also generally considered a good practice.
### Partial select
In some cases, you might want to select only a subset of columns from a table.
You can do that by providing a selection object to the `.select()` method:
```typescript copy
const result = await db.select({
field1: users.id,
field2: users.name,
}).from(users);
const { field1, field2 } = result[0];
```
```sql
select `id`, `name` from `users`;
```
Like in SQL, you can use arbitrary expressions as selection fields, not just table columns:
```typescript
const result = await db.select({
id: users.id,
lowerName: sql`lower(${users.name})`,
}).from(users);
```
```sql
select `id`, lower(`name`) from `users`;
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `string`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a string), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/mysql/sql#sqlmapwith) method.
You can also specify column alias
```ts
const result = await db.select({
id: users.id,
lowerName: users.name.as("lower"),
}).from(users);
```
```sql
select `id`, `name` as `lower` from `users`
```
### Conditional select
You can have a dynamic selection object based on some condition:
```typescript
async function selectUsers(withName: boolean) {
return db
.select({
id: users.id,
...(withName ? { name: users.name } : {}),
})
.from(users);
}
const result = await selectUsers(true);
```
### Distinct select
You can use `.selectDistinct()` instead of `.select()` to retrieve only unique rows from a dataset:
```ts
await db.selectDistinct().from(users).orderBy(users.id, users.name);
await db.selectDistinct({ id: users.id }).from(users).orderBy(users.id);
```
```sql
select distinct `id`, `name` from `users` order by `users`.`id`, `users`.`name`;
select distinct `id` from `users` order by `users`.`id`;
```
### Advanced select
Powered by TypeScript, Drizzle APIs let you build your select queries in a variety of flexible ways.
Sneak peek of advanced partial select, for more detailed advanced usage examples - see our [dedicated guide](/docs/mysql/guides/include-or-exclude-columns).
```ts
import { getColumns, sql } from 'drizzle-orm';
await db.select({
...getColumns(posts),
titleLength: sql`length(${posts.title})`,
}).from(posts);
```
```ts
import { getColumns } from 'drizzle-orm';
const { content, ...rest } = getColumns(posts); // exclude "content" column
await db.select({ ...rest }).from(posts); // select all other columns
```
```ts
await db.query.posts.findMany({
columns: {
title: true,
},
});
```
```ts
await db.query.posts.findMany({
columns: {
content: false,
},
});
```
## ---
### Filters
You can filter the query results using the [filter operators](/docs/mysql/operators) in the `.where()` method:
```typescript copy
import { eq, lt, gte, ne } from 'drizzle-orm';
await db.select().from(users).where(eq(users.id, 42));
await db.select().from(users).where(lt(users.id, 42));
await db.select().from(users).where(gte(users.id, 42));
await db.select().from(users).where(ne(users.id, 42));
...
```
```sql
select `id`, `name`, `age` from `users` where `users`.`id` = 42;
select `id`, `name`, `age` from `users` where `users`.`id` < 42;
select `id`, `name`, `age` from `users` where `users`.`id` >= 42;
select `id`, `name`, `age` from `users` where `users`.`id` <> 42;
```
All filter operators are implemented using the [`sql`](/docs/mysql/sql) function.
You can use it yourself to write arbitrary SQL filters, or build your own operators.
For inspiration, you can check how the operators provided by Drizzle are [implemented](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/sql/expressions/conditions.ts).
```typescript copy
import { sql } from 'drizzle-orm';
function equals42(col: Column) {
return sql`${col} = 42`;
}
await db.select().from(users).where(sql`${users.id} < 42`);
await db.select().from(users).where(sql`${users.id} = 42`);
await db.select().from(users).where(equals42(users.id));
await db.select().from(users).where(sql`${users.id} >= 42`);
await db.select().from(users).where(sql`${users.id} <> 42`);
await db.select().from(users).where(sql`lower(${users.name}) = 'aaron'`);
```
```sql
select `id`, `name`, `age` from `users` where `users`.`id` < 42;
select `id`, `name`, `age` from `users` where `users`.`id` = 42;
select `id`, `name`, `age` from `users` where `users`.`id` = 42;
select `id`, `name`, `age` from `users` where `users`.`id` >= 42;
select `id`, `name`, `age` from `users` where `users`.`id` <> 42;
select `id`, `name`, `age` from `users` where lower(`users`.`name`) = 'aaron';
```
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select `id`, `name`, `age` from `users` where `id` = ?; -- params: [42]
```
Inverting condition with a `not` operator:
```typescript copy
import { eq, not, sql } from 'drizzle-orm';
await db.select().from(users).where(not(eq(users.id, 42)));
await db.select().from(users).where(sql`not ${users.id} = 42`);
```
```sql
select `id`, `name`, `age` from `users` where not (`users`.`id` = 42);
select `id`, `name`, `age` from `users` where not `users`.`id` = 42;
```
You can safely alter schema, rename tables and columns
and it will be automatically reflected in your queries because of template interpolation,
as opposed to hardcoding column or table names when writing raw SQL.
### Combining filters
You can logically combine filter operators with `and()` and `or()` operators:
```typescript copy
import { eq, and, sql } from 'drizzle-orm';
await db.select().from(users).where(
and(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 and ${users.name} = 'Dan'`);
```
```sql
select `id`, `name`, `age` from `users` where ((`users`.`id` = 42) and (`users`.`name` = 'Dan'));
select `id`, `name`, `age` from `users` where `users`.`id` = 42 and `users`.`name` = 'Dan';
```
```typescript copy
import { eq, or, sql } from 'drizzle-orm';
await db.select().from(users).where(
or(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 or ${users.name} = 'Dan'`);
```
```sql
select `id`, `name`, `age` from `users` where ((`users`.`id` = 42) or (`users`.`name` = 'Dan'));
select `id`, `name`, `age` from `users` where `users`.`id` = 42 or `users`.`name` = 'Dan';
```
### Advanced filters
In combination with TypeScript, Drizzle APIs provide you powerful and flexible ways to combine filters in queries.
Sneak peek of conditional filtering, for more detailed advanced usage examples - see our [dedicated guide](/docs/mysql/guides/conditional-filters-in-query).
```ts
const searchPosts = async (term?: string) => {
await db
.select()
.from(posts)
.where(term ? like(posts.title, term) : undefined);
};
await searchPosts();
await searchPosts('AI');
```
```ts
const searchPosts = async (filters: SQL[]) => {
await db
.select()
.from(posts)
.where(and(...filters));
};
const filters: SQL[] = [];
filters.push(like(posts.title, 'AI'));
filters.push(inArray(posts.category, ['Tech', 'Art', 'Science']));
filters.push(gt(posts.views, 200));
await searchPosts(filters);
```
## ---
### Limit & offset
Use `.limit()` and `.offset()` to add limit and offset clauses to the query - for example, to implement pagination:
```ts
await db.select().from(users).limit(10);
await db.select().from(users).limit(10).offset(10);
```
```sql
select `id`, `name`, `age` from `users` limit 10;
select `id`, `name`, `age` from `users` limit 10 offset 10;
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.select().from(users).orderBy(users.name);
await db.select().from(users).orderBy(desc(users.name));
// order by multiple fields
await db.select().from(users).orderBy(users.name, users.name2);
await db.select().from(users).orderBy(asc(users.name), desc(users.name2));
```
```sql
select `id`, `name`, `age`, `name2` from `users` order by `users`.`name`;
select `id`, `name`, `age`, `name2` from `users` order by `users`.`name` desc;
select `id`, `name`, `age`, `name2` from `users` order by `users`.`name`, `users`.`name2`;
select `id`, `name`, `age`, `name2` from `users` order by `users`.`name` asc, `users`.`name2` desc;
```
### Advanced pagination
Powered by TypeScript, Drizzle APIs let you implement all possible SQL pagination and sorting approaches.
Sneak peek of advanced pagination, for more detailed advanced
usage examples - see our dedicated [limit offset pagination](/docs/mysql/guides/limit-offset-pagination)
and [cursor pagination](/docs/mysql/guides/cursor-based-pagination) guides.
```ts
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.limit(4) // the number of rows to return
.offset(4); // the number of rows to skip
```
```ts
const getUsers = async (page = 1, pageSize = 3) => {
return await db.query.users.findMany({
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
offset: (page - 1) * pageSize,
});
};
await getUsers();
```
```ts
const getUsers = async (page = 1, pageSize = 10) => {
const sq = db
.select({ id: users.id })
.from(users)
.orderBy(users.id)
.limit(pageSize)
.offset((page - 1) * pageSize)
.as('subquery');
return await db.select().from(users).innerJoin(sq, eq(users.id, sq.id)).orderBy(users.id);
};
```
```ts
const nextUserPage = async (cursor?: number, pageSize = 3) => {
return await db
.select()
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined) // if cursor is provided, get rows after it
.limit(pageSize) // the number of rows to return
.orderBy(asc(users.id)); // ordering
};
// pass the cursor of the last row of the previous page (id)
await nextUserPage(3);
```
## ---
### WITH 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 sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
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`
```
MySQL doesn't support native `RETURNING`, so `insert`, `update`, and `delete` statements that depend on returning rows are not available inside `with` for this dialect.
To select arbitrary SQL values as fields in a CTE and reference them in other CTEs or in the main query,
you need to add aliases to them:
```typescript copy
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);
```
If you don't provide an alias, the field type will become `DrizzleTypeError` and you won't be able to reference it in other queries.
If you ignore the type error and still try to use the field,
you will get a runtime error, since there's no way to reference that field without an alias.
### Select from subquery
Just like in SQL, you can embed queries into other queries by using the subquery API:
```typescript copy
const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');
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 be used in any place where a table can be used, for example in joins:
```typescript copy
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));
```
```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`;
```
## ---
### Aggregations
With Drizzle, you can do aggregations using functions like `sum`, `count`, `avg`, etc. by
grouping and filtering with `.groupBy()` and `.having()` respectfully, same as you would do in raw SQL:
```typescript
import { gt } from 'drizzle-orm';
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as signed)`,
})
.from(users)
.groupBy(users.age);
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as signed)`,
})
.from(users)
.groupBy(users.age)
.having(({ count }) => gt(count, 1));
```
```sql
select `age`, cast(count(`id`) as signed)
from `users`
group by `users`.`age`;
select `age`, cast(count(`id`) as signed)
from `users`
group by `users`.`age`
having cast(count(`users`.`id`) as signed) > 1;
```
Alternatively, you can use [`.mapWith(Number)`](/docs/mysql/sql#sqlmapwith) to cast the value to a number at runtime.
If you need count aggregation - we recommend using our [`$count`](/docs/mysql/select#count) API
### Aggregations helpers
Drizzle has a set of wrapped `sql` functions, so you don't need to write
`sql` templates for common cases in your app
Remember, aggregation functions are often used with the GROUP BY clause of the SELECT statement.
So if you are selecting using aggregating functions and other columns in one query,
be sure to use the `.groupBy` clause
**count**
Returns the number of values in `expression`.
```ts
import { count } from 'drizzle-orm'
await db.select({ value: count() }).from(users);
await db.select({ value: count(users.id) }).from(users);
```
```sql
select count(*) from `users`;
select count(`id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(*)`.mapWith(Number)
}).from(users);
await db.select({
value: sql`count(${users.id})`.mapWith(Number)
}).from(users);
```
**countDistinct**
Returns the number of non-duplicate values in `expression`.
```ts
import { countDistinct } from 'drizzle-orm'
await db.select({ value: countDistinct(users.id) }).from(users);
```
```sql
select count(distinct `id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`count(distinct ${users.id})`.mapWith(Number)
}).from(users);
```
**avg**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avg } from 'drizzle-orm'
await db.select({ value: avg(users.id) }).from(users);
```
```sql
select avg(`id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(${users.id})`.mapWith(String)
}).from(users);
```
**avgDistinct**
Returns the average (arithmetic mean) of all non-null values in `expression`.
```ts
import { avgDistinct } from 'drizzle-orm'
await db.select({ value: avgDistinct(users.id) }).from(users);
```
```sql
select avg(distinct `id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`avg(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**sum**
Returns the sum of all non-null values in `expression`.
```ts
import { sum } from 'drizzle-orm'
await db.select({ value: sum(users.id) }).from(users);
```
```sql
select sum(`id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(${users.id})`.mapWith(String)
}).from(users);
```
**sumDistinct**
Returns the sum of all non-null and non-duplicate values in `expression`.
```ts
import { sumDistinct } from 'drizzle-orm'
await db.select({ value: sumDistinct(users.id) }).from(users);
```
```sql
select sum(distinct `id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`sum(distinct ${users.id})`.mapWith(String)
}).from(users);
```
**max**
Returns the maximum value in `expression`.
```ts
import { max } from 'drizzle-orm'
await db.select({ value: max(users.id) }).from(users);
```
```sql
select max(`id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`max(${expression})`.mapWith(users.id)
}).from(users);
```
**min**
Returns the minimum value in `expression`.
```ts
import { min } from 'drizzle-orm'
await db.select({ value: min(users.id) }).from(users);
```
```sql
select min(`id`) from `users`;
```
```ts
// It's equivalent to writing
await db.select({
value: sql`min(${users.id})`.mapWith(users.id)
}).from(users);
```
A more advanced example:
```typescript copy
const orders = mysqlTable("order", {
id: int().primaryKey(),
orderDate: int("order_date").notNull(),
requiredDate: int("required_date").notNull(),
shippedDate: int("shipped_date"),
shipVia: int("ship_via").notNull(),
freight: decimal().notNull(),
shipName: text("ship_name").notNull(),
shipCity: text("ship_city").notNull(),
shipRegion: text("ship_region"),
shipPostalCode: text("ship_postal_code"),
shipCountry: text("ship_country").notNull(),
customerId: text("customer_id").notNull(),
employeeId: int("employee_id").notNull(),
});
const details = mysqlTable("order_detail", {
unitPrice: decimal("unit_price").notNull(),
quantity: int("quantity").notNull(),
discount: decimal().notNull(),
orderId: int("order_id").notNull(),
productId: int("product_id").notNull(),
});
await db.select({
id: orders.id,
shippedDate: orders.shippedDate,
shipName: orders.shipName,
shipCity: orders.shipCity,
shipCountry: orders.shipCountry,
productsCount: sql`cast(count(${details.productId}) as signed)`,
quantitySum: sql`sum(${details.quantity})`,
totalPrice: sql`sum(${details.quantity} * ${details.unitPrice})`,
})
.from(orders)
.leftJoin(details, eq(orders.id, details.orderId))
.groupBy(orders.id)
.orderBy(asc(orders.id));
```
### $count
<$count/>
## ---
### Iterator
If you need to return a very large amount of rows from a query and you don't want to load them all into memory, you can use `.iterator()` to convert the query into an async iterator:
```ts copy
const iterator = db.select().from(users).iterator();
for await (const row of iterator) {
console.log(row);
}
```
It also works with prepared statements:
```ts copy
const query = db.select().from(users).prepare();
const iterator = query.iterator();
for await (const row of iterator) {
console.log(row);
}
```
## ---
### Use Index
The `USE INDEX` hint suggests to the optimizer which indexes to consider when processing the query. The optimizer is not forced to use these indexes but will prioritize them if they are suitable.
```ts copy
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: varchar({ length: 100 }).notNull(),
}, () => [usersTableNameIndex]);
const usersTableNameIndex = index('users_name_index').on(users.name);
await db.select()
.from(users, { useIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
You can also use this option on any join you want
```ts
await db.select()
.from(users)
.leftJoin(posts, eq(posts.userId, users.id), { useIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
### Ignore Index
The `IGNORE INDEX` hint tells the optimizer to avoid using specific indexes for the query. MySQL will consider all other indexes (if any) or perform a full table scan if necessary.
```ts copy
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: varchar({ length: 100 }).notNull(),
}, () => [usersTableNameIndex]);
const usersTableNameIndex = index('users_name_index').on(users.name);
await db.select()
.from(users, { ignoreIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
You can also use this option on any join you want
```ts
await db.select()
.from(users)
.leftJoin(posts, eq(posts.userId, users.id), { ignoreIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
### Force Index
The `FORCE INDEX` hint forces the optimizer to use the specified index(es) for the query. If the specified index cannot be used, MySQL will not fall back to other indexes; it might resort to a full table scan instead.
```ts copy
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: varchar({ length: 100 }).notNull(),
}, () => [usersTableNameIndex]);
const usersTableNameIndex = index('users_name_index').on(users.name);
await db.select()
.from(users, { forceIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
You can also use this option on any join you want
```ts
await db.select()
.from(users)
.leftJoin(posts, eq(posts.userId, users.id), { forceIndex: usersTableNameIndex })
.where(eq(users.name, 'David'));
```
Source: https://orm.drizzle.team/docs/mysql/set-operations
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Set Operations
SQL set operations combine the results of multiple query blocks into a single result.
The SQL standard defines the following three set operations: `UNION`, `INTERSECT`, `EXCEPT`, `UNION ALL`, `INTERSECT ALL`, `EXCEPT ALL`.
### Union
Combine all results from two query blocks into a single result, omitting any duplicates.
Get all names from customers and users tables without duplicates.
```typescript copy
import { union } from 'drizzle-orm/mysql-core'
import { users, customers } from './schema'
const allNamesForUserQuery = db.select({ name: users.name }).from(users);
const result = await union(
allNamesForUserQuery,
db.select({ name: customers.name }).from(customers)
).limit(10);
```
```sql
(select `name` from `sellers`)
union
(select `name` from `customers`)
limit 10
```
```ts copy
import { users, customers } from './schema'
const result = await db
.select({ name: users.name })
.from(users)
.union(db.select({ name: customers.name }).from(customers))
.limit(10);
```
```sql
(select `name` from `sellers`)
union
(select `name` from `customers`)
limit 10
```
```typescript copy
import { int, mysqlTable, text, varchar } from "drizzle-orm/mysql-core";
export const users = mysqlTable('sellers', {
id: int().primaryKey(),
name: varchar({ length: 256 }).notNull(),
address: text(),
});
export const customers = mysqlTable('customers', {
id: int().primaryKey(),
name: varchar(, { length: 256 }).notNull(),
city: text(),
email: varchar({ length: 256 }).notNull()
});
```
### Union All
Combine all results from two query blocks into a single result, with duplicates.
Let's consider a scenario where you have two tables, one representing online sales and the other
representing in-store sales. In this case, you want to combine the data from both tables into a
single result set. Since there might be duplicate transactions,
you want to keep all the records and not eliminate duplicates.
```typescript copy
import { unionAll } from 'drizzle-orm/mysql-core'
import { onlineSales, inStoreSales } from './schema'
const onlineTransactions = db.select({ transaction: onlineSales.transactionId }).from(onlineSales);
const inStoreTransactions = db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales);
const result = await unionAll(onlineTransactions, inStoreTransactions);
```
```sql
(select `transaction_id` from `online_sales`)
union all
(select `transaction_id` from `in_store_sales`)
```
```ts copy
import { onlineSales, inStoreSales } from './schema'
const result = await db
.select({ transaction: onlineSales.transactionId })
.from(onlineSales)
.unionAll(
db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales)
);
```
```sql
(select `transaction_id` from `online_sales`)
union all
(select `transaction_id` from `in_store_sales`)
```
```typescript copy
import { int, mysqlTable, timestamp } from "drizzle-orm/mysql-core";
export const onlineSales = mysqlTable('online_sales', {
transactionId: int('transaction_id').primaryKey(),
productId: int('product_id').unique(),
quantitySold: int('quantity_sold'),
saleDate: timestamp('sale_date', { mode: 'date' }),
});
export const inStoreSales = mysqlTable('in_store_sales', {
transactionId: int('transaction_id').primaryKey(),
productId: int('product_id').unique(),
quantitySold: int('quantity_sold'),
saleDate: timestamp('sale_date', { mode: 'date' }),
});
```
### Intersect
Combine only those rows which the results of two query blocks have in common, omitting any duplicates.
Suppose you have two tables that store information about students' course enrollments.
You want to find the courses that are common between two different departments,
but you want distinct course names, and you're not interested in counting multiple
enrollments of the same course by the same student.
In this scenario, you want to find courses that are common between the two departments but don't want
to count the same course multiple times even if multiple students from the same department are enrolled in it.
```typescript copy
import { intersect } from 'drizzle-orm/mysql-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.courseName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.courseName }).from(depB);
const result = await intersect(departmentACourses, departmentBCourses);
```
```sql
(select `course_name` from `department_a_courses`)
intersect
(select `course_name` from `department_b_courses`)
```
```typescript copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.courseName })
.from(depA)
.intersect(db.select({ courseName: depB.courseName }).from(depB));
```
```sql
(select `course_name` from `department_a_courses`)
intersect
(select `course_name` from `department_b_courses`)
```
```typescript copy
import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const depA = mysqlTable('department_a_courses', {
studentId: int('student_id'),
courseName: varchar('course_name', { length: 256 }).notNull(),
});
export const depB = mysqlTable('department_b_courses', {
studentId: int('student_id'),
courseName: varchar('course_name', { length: 256 }).notNull(),
});
```
### Intersect All
Combine only those rows which the results of two query blocks have in common, with duplicates.
Let's consider a scenario where you have two tables containing data about customer orders, and you want
to identify products that are ordered by both regular customers and VIP customers. In this case,
you want to keep track of the quantity of each product, even if it's ordered multiple times by
different customers.
In this scenario, you want to find products that are ordered by both regular customers and VIP customers,
but you want to retain the quantity information, even if the same product is ordered multiple
times by different customers.
```typescript copy
import { intersectAll } from 'drizzle-orm/mysql-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const regularOrders = db.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);
const vipOrders = db.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);
const result = await intersectAll(regularOrders, vipOrders);
```
```sql
(select `product_id`, `quantity_ordered` from `regular_customer_orders`)
intersect all
(select `product_id`, `quantity_ordered` from `vip_customer_orders`)
```
```ts copy
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const result = await db
.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered,
})
.from(regularCustomerOrders)
.intersectAll(
db
.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered,
})
.from(vipCustomerOrders)
);
```
```sql
(select `product_id`, `quantity_ordered` from `regular_customer_orders`)
intersect all
(select `product_id`, `quantity_ordered` from `vip_customer_orders`)
```
```typescript copy
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const regularCustomerOrders = mysqlTable('regular_customer_orders', {
customerId: int('customer_id').primaryKey(),
productId: int('product_id').notNull(),
quantityOrdered: int('quantity_ordered').notNull(),
});
export const vipCustomerOrders = mysqlTable('vip_customer_orders', {
customerId: int('customer_id').primaryKey(),
productId: int('product_id').notNull(),
quantityOrdered: int('quantity_ordered').notNull(),
});
```
### Except
For two query blocks A and B, return all results from A which are not also present in B, omitting any duplicates.
Suppose you have two tables that store information about employees' project assignments.
You want to find the projects that are unique to one department
and not shared with another department, excluding duplicates.
In this scenario, you want to identify the projects that are exclusive to one department and
not shared with the other department. You don't want to count the same project
multiple times, even if multiple employees from the same department are assigned to it.
```typescript copy
import { except } from 'drizzle-orm/mysql-core'
import { depA, depB } from './schema'
const departmentACourses = db.select({ courseName: depA.projectsName }).from(depA);
const departmentBCourses = db.select({ courseName: depB.projectsName }).from(depB);
const result = await except(departmentACourses, departmentBCourses);
```
```sql
(select `projects_name` from `department_a_projects`)
except
(select `projects_name` from `department_b_projects`)
```
```ts copy
import { depA, depB } from './schema'
const result = await db
.select({ courseName: depA.projectsName })
.from(depA)
.except(db.select({ courseName: depB.projectsName }).from(depB));
```
```sql
(select `projects_name` from `department_a_projects`)
except
(select `projects_name` from `department_b_projects`)
```
```typescript
import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const depA = mysqlTable('department_a_projects', {
employeeId: int('employee_id'),
projectsName: varchar('projects_name', { length: 256 }).notNull(),
});
export const depB = mysqlTable('department_b_projects', {
employeeId: int('employee_id'),
projectsName: varchar('projects_name', { length: 256 }).notNull(),
});
```
### Except All
For two query blocks A and B, return all results from A which are not also present in B, with duplicates.
Let's consider a scenario where you have two tables containing data about customer orders, and you want to
identify products that are exclusively ordered by regular customers (without VIP customers).
In this case, you want to keep track of the quantity of each product, even if it's ordered multiple times by different regular customers.
In this scenario, you want to find products that are exclusively ordered by regular customers and not
ordered by VIP customers. You want to retain the quantity information, even if the same product is ordered multiple times by different regular customers.
```typescript copy
import { exceptAll } from 'drizzle-orm/mysql-core'
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const regularOrders = db.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered }
).from(regularCustomerOrders);
const vipOrders = db.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered }
).from(vipCustomerOrders);
const result = await exceptAll(regularOrders, vipOrders);
```
```sql
(select `product_id`, `quantity_ordered` from `regular_customer_orders`)
except all
(select `product_id`, `quantity_ordered` from `vip_customer_orders`)
```
```ts copy
import { regularCustomerOrders, vipCustomerOrders } from './schema'
const result = await db
.select({
productId: regularCustomerOrders.productId,
quantityOrdered: regularCustomerOrders.quantityOrdered,
})
.from(regularCustomerOrders)
.exceptAll(
db
.select({
productId: vipCustomerOrders.productId,
quantityOrdered: vipCustomerOrders.quantityOrdered,
})
.from(vipCustomerOrders)
);
```
```sql
(select `product_id`, `quantity_ordered` from `regular_customer_orders`)
except all
(select `product_id`, `quantity_ordered` from `vip_customer_orders`)
```
```typescript copy
import { int, mysqlTable } from "drizzle-orm/mysql-core";
export const regularCustomerOrders = mysqlTable('regular_customer_orders', {
customerId: int('customer_id').primaryKey(),
productId: int('product_id').notNull(),
quantityOrdered: int('quantity_ordered').notNull(),
});
export const vipCustomerOrders = mysqlTable('vip_customer_orders', {
customerId: int('customer_id').primaryKey(),
productId: int('product_id').notNull(),
quantityOrdered: int('quantity_ordered').notNull(),
});
```
Source: https://orm.drizzle.team/docs/mysql/sql-comments
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# SQL Comments
You can add custom tags to any query using the `.comment()` method. These tags are appended as [sqlcommenter](https://google.github.io/sqlcommenter)-formatted comments at the end of each query,
helping you attach metadata for query tracking, debugging and database traffic control.
The `.comment()` method is available on `select`, `insert`, `update` and `delete` queries.
### String comments
You can pass a raw string as a comment:
```ts
db.select().from(users).comment("my_first_tag");
```
```sql
select `id`, `name` from `users` /*my_first_tag*/
```
```ts
db.select().from(users).comment("key='val'");
```
```sql
select `id`, `name` from `users` /*key='val'*/
```
### Object comments
You can pass an object with key-value pairs for structured tags:
```ts
db.select().from(users).comment({ priority: 'high', category: 'analytics' });
```
```sql
select `id`, `name` from `users` /*priority='high',category='analytics'*/
```
```ts
db.select().from(users).comment({ trace: true, route: '/api/users', version: 2 });
```
```sql
select `id`, `name` from `users` /*route='%2Fapi%2Fusers',trace='true',version='2'*/
```
### Usage with insert, update, delete
```ts
db.insert(users).values({ name: 'Dan' }).comment({ operation: 'seed' });
```
```sql
insert into `users` (`name`) values ('Dan') /*operation='seed'*/
```
```ts
db.update(users).set({ name: 'Dan' }).where(eq(users.id, 1)).comment({ operation: 'update' });
```
```sql
update `users` set `name` = 'Dan' where `users`.`id` = 1 /*operation='update'*/
```
```ts
db.delete(users).where(eq(users.id, 1)).comment({ operation: 'cleanup' });
```
```sql
delete from `users` where `users`.`id` = 1 /*operation='cleanup'*/
```
### Limitations
You can't use `.comment()` after the statement has been prepared. Prepared statements compile the SQL query once and reuse it across executions,
so the query string is fixed at preparation time and can't be modified afterwards â including appending comments:
```ts
// can't be used
const p = db.select().from(users).prepare();
// â
p.comment({ key: 'val' }).execute();
```
Instead, add the comment before preparing the statement:
```ts
// â
db.select().from(users).comment({ key: "val" }).prepare();
```
Source: https://orm.drizzle.team/docs/mysql/sql-schema-declaration
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle schema
Drizzle lets you define a schema in TypeScript with various models and properties supported by the underlying database.
When you define your schema, it serves as the source of truth for future modifications in queries (using Drizzle-ORM)
and migrations (using Drizzle-Kit).
If you are using Drizzle-Kit for the migration process, make sure to export all the models defined in your schema files so that Drizzle-Kit can import them and use them in the migration diff process.
```ts
import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const usersTable = mysqlTable("users", {
id: int().primaryKey().autoincrement(),
name: varchar({ length: 256 }).notNull(),
age: int().notNull(),
email: varchar({ length: 256 }).notNull().unique(),
});
```
```ts
import { mysqlTable } from "drizzle-orm/mysql-core";
export const usersTable = mysqlTable("users", (t) => ({
id: t.int().primaryKey().autoincrement(),
name: t.varchar({ length: 256 }).notNull(),
age: t.int().notNull(),
email: t.varchar({ length: 256 }).notNull().unique(),
}));
```
```ts
import * as t from "drizzle-orm/mysql-core";
export const usersTable = t.mysqlTable("users", {
id: t.int().primaryKey().autoincrement(),
name: t.varchar({ length: 256 }).notNull(),
age: t.int().notNull(),
email: t.varchar({ length: 256 }).notNull().unique(),
});
```
## Organize your schema files
You can declare your SQL schema directly in TypeScript either in a single `schema.ts` file,
or you can spread them around â whichever you prefer, all the freedom!
#### Schema in 1 file
The most common way to declare your schema with Drizzle is to put all your tables into one `schema.ts` file.
> Note: You can name your schema file whatever you like. For example, it could be `models.ts`, or something else.
This approach works well if you don't have too many table models defined, or if you're okay with keeping them all in one file
Example:
```plaintext
đĻ
â đ src
â đ db
â đ schema.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema file. With this configuration, Drizzle will
read from the `schema.ts` file and use this information during the migration generation process. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/mysql/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'mysql',
schema: './src/db/schema.ts'
})
```
#### Schema in multiple files
You can place your Drizzle models â such as tables, enums, sequences, etc. â not only in one file but in any file you prefer.
The only thing you must ensure is that you export all the models from those files so that the Drizzle kit can import
them and use them in migrations.
One use case would be to separate each table into its own file.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ countries.ts
â đ cities.ts
â đ products.ts
â đ clients.ts
â đ etc.ts
```
In the `drizzle.config.ts` file, you need to specify the path to your schema folder. With this configuration, Drizzle will
read from the `schema` folder and find all the files recursively and get all the drizzle tables from there. For more information
about the `drizzle.config.ts` file and migrations with Drizzle, please check: [link](/docs/mysql/drizzle-config-file)
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'mysql',
schema: './src/db/schema'
})
```
You can also group them in any way you like, such as creating groups for user-related tables, messaging-related tables, product-related tables, etc.
```plaintext
đĻ
â đ src
â đ db
â đ schema
â đ users.ts
â đ messaging.ts
â đ products.ts
```
## Shape your data schema
Drizzle schema consists of several model types from database you are using. With drizzle you can specify:
- Tables with columns, constraints, etc.
- Schemas, which are equivalent to databases in MySQL
- Enums
- Views
- etc.
Let's go one by one and check how the schema should be defined with drizzle
#### **Tables and columns declaration**
A table in Drizzle should be defined with at least 1 column, the same as it should be done in database. There is one important thing to know,
there is no such thing as a common table object in drizzle. You need to choose a dialect you are using, PostgreSQL, MySQL, SQLite, etc.

```ts copy
import { mysqlTable, int } from "drizzle-orm/mysql-core"
export const users = mysqlTable('users', {
id: int()
});
```
By default, Drizzle will use the TypeScript key names for columns in database queries.
Therefore, the schema and query from the example will generate the SQL query shown below
This example uses a db object, whose initialization is not covered in this part of the documentation. To learn how to connect to the database, please refer to the [Connections Docs](/docs/mysql/get-started-mysql)
\
**TypeScript key = database key**
```ts
// schema.ts
import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const users = mysqlTable('users', {
id: int(),
first_name: varchar({ length: 256 })
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT `id`, `first_name` FROM `users`;
```
If you want to use different names in your TypeScript code and in the database, you can use column aliases
```ts
// schema.ts
import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const users = mysqlTable('users', {
id: int(),
firstName: varchar('first_name', { length: 256 })
})
```
```ts
// query.ts
await db.select().from(users);
```
```sql
SELECT `id`, `first_name` FROM `users`;
```
### Camel and Snake casing
Database model names often use `snake_case` conventions, while in TypeScript, it is common to use `camelCase` for naming models.
This can lead to a lot of alias definitions in the schema. To address this, Drizzle provides a way to automatically
map `camelCase` from TypeScript to `snake_case` in the database via a dedicated builder.
Use the `snakeCase` or `camelCase` builder from `drizzle-orm/pg-core` to declare a table whose column keys are
automatically mapped to the chosen database naming convention
```ts {2,4,11,12}
// schema.ts
import { snakeCase, camelCase } from 'drizzle-orm/mysql-core';
export const users = snakeCase.table('users', {
id: int().primaryKey().autoincrement(),
fullName: text(), // â full_name in DB
createdAt: timestamp(), // â created_at in DB
});
// Available on all entity types:
snakeCase.table / snakeCase.view / snakeCase.schema
camelCase.table / camelCase.view / camelCase.schema
```
```ts
// db.ts
import { drizzle } from "drizzle-orm/mysql2";
const db = drizzle({ connection: process.env.DATABASE_URL })
```
```ts
// query.ts
await db.select().from(users);
```
```sql
select `id`, `full_name`, `created_at` from `users`;
```
### Advanced
There are a few tricks you can use with Drizzle ORM. As long as Drizzle is entirely in TypeScript files,
you can essentially do anything you would in a simple TypeScript project with your code.
One common feature is to separate columns into different places and then reuse them.
For example, consider the `updated_at`, `created_at`, and `deleted_at` columns. Many tables/models may need these
three fields to track and analyze the creation, deletion, and updates of entities in a system
We can define those columns in a separate file and then import and spread them across all the table objects you have
```ts
// columns.helpers.ts
export const timestamps = {
updated_at: timestamp(),
created_at: timestamp().defaultNow().notNull(),
deleted_at: timestamp(),
}
```
```ts
// users.sql.ts
export const users = mysqlTable('users', {
id: int(),
...timestamps
})
```
```ts
// posts.sql.ts
export const posts = mysqlTable('posts', {
id: int(),
...timestamps
})
```
#### **Schemas**
In MySQL, there is an entity called `Schema`, but in MySQL terms, this is equivalent to a `Database`.
You can define them with `drizzle-orm` and use them in queries, but they won't be detected by `drizzle-kit` or included in the migration flow

Define the schema you want to manage using Drizzle
```ts
import { mysqlSchema } from "drizzle-orm/mysql-core"
export const customSchema = mysqlSchema('custom');
```
Then place the table inside the schema object
```ts {5-7}
import { int, mysqlSchema } from "drizzle-orm/mysql-core";
export const customSchema = mysqlSchema('custom');
export const users = customSchema.table('users', {
id: int()
})
```
### Example
Once you know the basics, let's define a schema example for a real project to get a better view and understanding
> All examples will use `generateUniqueString`. The implementation for it will be provided after all the schema examples
```ts copy
import { mysqlTable as table } from "drizzle-orm/mysql-core";
import * as t from "drizzle-orm/mysql-core";
import type { AnyMySqlColumn } from "drizzle-orm/mysql-core";
export const users = table(
"users",
{
id: t.int().primaryKey().autoincrement(),
firstName: t.varchar("first_name", { length: 256 }),
lastName: t.varchar("last_name", { length: 256 }),
email: t.varchar({ length: 256 }).notNull(),
invitee: t.int().references((): AnyMySqlColumn => users.id),
role: t.mysqlEnum(["guest", "user", "admin"]).default("guest"),
},
(table) => [
t.uniqueIndex("email_idx").on(table.email)
]
);
export const posts = table(
"posts",
{
id: t.int().primaryKey().autoincrement(),
slug: t.varchar({ length: 256 }).$default(() => generateUniqueString(16)),
title: t.varchar({ length: 256 }),
ownerId: t.int("owner_id").references(() => users.id),
},
(table) => [
t.uniqueIndex("slug_idx").on(table.slug),
t.index("title_idx").on(table.title),
]
);
export const comments = table("comments", {
id: t.int().primaryKey().autoincrement(),
text: t.varchar({ length: 256 }),
postId: t.int("post_id").references(() => posts.id),
ownerId: t.int("owner_id").references(() => users.id),
});
```
**`generateUniqueString` implementation:**
```ts
function generateUniqueString(length: number = 12): string {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let uniqueString = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
uniqueString += characters[randomIndex];
}
return uniqueString;
}
```
#### What's next?
Source: https://orm.drizzle.team/docs/mysql/sql
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Magical `sql` operator đĒ
When working with an ORM library, there may be cases where you find it challenging to write a
specific query using the provided ORM syntax. In such situations, you can resort to using
raw queries, which involve constructing a query as a raw string. However, raw queries often
lack the benefits of type safety and query parameterization.
To address this, many libraries have introduced the concept of an `sql` template. This template
allows you to write more type-safe and parameterized queries, enhancing the overall safety and
flexibility of your code. Drizzle, being a powerful ORM library, also supports the sql template.
With Drizzle's `sql` template, you can go even further in crafting queries. If you encounter
difficulties in writing an entire query using the library's query builder, you can selectively
use the `sql` template within specific sections of the Drizzle query. This flexibility enables you
to employ the sql template in partial SELECT statements, WHERE clauses, ORDER BY clauses, HAVING
clauses, GROUP BY clauses, and even in relational query builders.
By leveraging the capabilities of the sql template in Drizzle, you can maintain the advantages
of type safety and query parameterization while achieving the desired query structure and complexity.
This empowers you to create more robust and maintainable code within your application.
## sql`` template
One of the most common usages you may encounter in other ORMs as well
is the ability to use `sql` queries as-is for raw queries.
```typescript copy
import { sql } from 'drizzle-orm'
const id = 69;
await db.execute(sql`select * from ${usersTable} where ${usersTable.id} = ${id}`)
```
It will generate the current query
```sql
select * from `users` where `users`.`id` = ?; --> [69]
```
Any tables and columns provided to the sql parameter are automatically mapped to their corresponding SQL
syntax with escaped names for tables, and the escaped table names are appended to column names.
Additionally, any dynamic parameters such as `${id}` will be mapped to the `?` placeholder,
and the corresponding values will be moved to an array of values that are passed separately to the database.
This approach effectively prevents any potential SQL Injection vulnerabilities.
## `sql`
Please note that `sql` does not perform any runtime mapping. The type you define using `sql` is
purely a helper for Drizzle. It is important to understand that there is no feasible way to
determine the exact type dynamically, as SQL queries can be highly versatile and customizable.
You can define a custom type in Drizzle to be used in places where fields require a specific type other than `unknown`.
This feature is particularly useful in partial select queries, ensuring consistent typing for selected fields:
```typescript
// without sql type defined
const response: { lowerName: unknown }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
// with sql type defined
const response: { lowerName: string }[] = await db.select({
lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);
```
## `sql``.mapWith()`
For the cases you need to make a runtime mapping for values passed from database driver to drizzle you can use `.mapWith()`
This function accepts different values, that will map response in runtime.
You can replicate a specific column mapping strategy as long as
the interface inside mapWith is the same interface that is implemented by Column.
```typescript
import { mysqlTable, int, text } from 'drizzle-orm/mysql-core';
const usersTable = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
});
// at runtime this values will be mapped same as `text` column is mapped in drizzle
sql`...`.mapWith(usersTable.name);
```
You can also pass your own implementation for the `DriverValueDecoder` interface:
```ts
sql``.mapWith({
mapFromDriverValue: (value: any) => {
const mappedValue = value;
// mapping you want to apply
return mappedValue;
},
});
// or
sql``.mapWith(Number);
```
## `sql``.as()`
In different cases, it can sometimes be challenging to determine how to name a custom field that you want to use.
You may encounter situations where you need to explicitly specify an alias for a
field that will be selected. This can be particularly useful when dealing with complex queries.
To address these scenarios, we have introduced a helpful `.as('alias_name')` helper, which allows
you to define an alias explicitly. By utilizing this feature, you can provide a clear and meaningful
name for the field, making your queries more intuitive and readable.
```typescript
sql`lower(${usersTable.name})`.as('lower_name')
```
```sql
... `users`.`name` as lower_name ...
```
## `sql.raw()`
There are cases where you may not need to create parameterized values from input or map tables/columns to escaped ones.
Instead, you might simply want to generate queries as they are. For such situations, we provide the `sql.raw()` function.
The `sql.raw()` function allows you to include raw SQL statements within your queries without any additional processing or escaping.
This can be useful when you have pre-constructed SQL statements or when you need to incorporate complex or dynamic
SQL code directly into your queries.
```typescript
sql.raw(`select * from users where id = ${12}`);
// vs
sql`select * from users where id = ${12}`;
```
```sql
select * from users where id = 12;
--> vs
select * from users where id = ?; --> [12]
```
You can also utilize `sql.raw()` within the sql function, enabling you to include any raw string
without escaping it through the main `sql` template function.
By using `sql.raw()` inside the `sql` function, you can incorporate unescaped raw strings
directly into your queries. This can be particularly useful when you have specific
SQL code or expressions that should remain untouched by the template function's automatic escaping or modification.
```typescript
sql`select * from ${usersTable} where id = ${12}`;
// vs
sql`select * from ${usersTable} where id = ${sql.raw(12)}`;
```
```sql
select * from `users` where id = ?; --> [12]
--> vs
select * from `users` where id = 12;
```
## sql.fromList()
The `sql` template generates sql chunks, which are arrays of SQL parts that will be concatenated
into the query and params after applying the SQL to the database or query in Drizzle.
In certain scenarios, you may need to aggregate these chunks into an array using custom business
logic and then concatenate them into a single SQL statement that can be passed to the database or query.
For such cases, the fromList function can be quite useful.
The fromList function allows you to combine multiple SQL chunks into a single SQL statement.
You can use it to aggregate and concatenate the individual SQL parts according to your specific
requirements and then obtain a unified SQL query that can be executed.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql` or `);
}
const finalSql: SQL = sql.fromList(sqlChunks)
```
```sql
select * from users where id = ? or id = ? or id = ? or id = ? or id = ?; --> [0, 1, 2, 3, 4]
```
## sql.join()
Indeed, the `sql.join` function serves a similar purpose to the fromList helper.
However, it provides additional flexibility when it comes to handling spaces between
SQL chunks or specifying custom separators for concatenating the SQL chunks.
With `sql.join`, you can concatenate SQL chunks together using a specified separator.
This separator can be any string or character that you want to insert between the chunks.
This is particularly useful when you have specific requirements for formatting or delimiting
the SQL chunks. By specifying a custom separator, you can achieve the desired structure and formatting
in the final SQL query.
```typescript
const sqlChunks: SQL[] = [];
sqlChunks.push(sql`select * from users`);
// some logic
sqlChunks.push(sql`where`);
// some logic
for (let i = 0; i < 5; i++) {
sqlChunks.push(sql`id = ${i}`);
if (i === 4) continue;
sqlChunks.push(sql`or`);
}
const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
```
```sql
select * from users where id = ? or id = ? or id = ? or id = ? or id = ?; --> [0, 1, 2, 3, 4]
```
## sql.append()
If you have already generated SQL using the `sql` template, you can achieve the same behavior as `fromList`
by using the append function to directly add a new chunk to the generated SQL.
By using the append function, you can dynamically add additional SQL chunks to the existing SQL string,
effectively concatenating them together. This allows you to incorporate custom logic or business
rules for aggregating the chunks into the final SQL query.
```typescript
const finalSql = sql`select * from users`;
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = ? or id = ? or id = ? or id = ? or id = ?; --> [0, 1, 2, 3, 4]
```
## sql.empty()
By using sql.empty(), you can start with a blank SQL object and then dynamically append SQL chunks to it as needed. This allows you to construct the SQL query incrementally, applying custom logic or conditions to determine the contents of each chunk.
Once you have initialized the SQL object using sql.empty(), you can take advantage of the full range
of sql template features such as parameterization, composition, and escaping.
This empowers you to construct the SQL query in a flexible and controlled manner,
adapting it to your specific requirements.
```typescript
const finalSql = sql.empty();
// some logic
finalSql.append(sql`select * from users`);
// some logic
finalSql.append(sql` where `);
// some logic
for (let i = 0; i < 5; i++) {
finalSql.append(sql`id = ${i}`);
if (i === 4) continue;
finalSql.append(sql` or `);
}
```
```sql
select * from users where id = ? or id = ? or id = ? or id = ? or id = ?; --> [0, 1, 2, 3, 4]
```
## Convert `sql` to string and params
In all the previous examples, you observed the usage of SQL template syntax in TypeScript along with
the generated SQL output.
If you need to obtain the query string and corresponding parameters generated from the SQL template,
you must specify the database dialect you intend to generate the query for. Different databases have
varying syntax for parameterization and escaping, so selecting the appropriate dialect is crucial.
Once you have chosen the dialect, you can utilize the corresponding implementation's functionality
to convert the SQL template into the desired query string and parameter format. This ensures
compatibility with the specific database system you are working with.
```typescript copy
import { MySqlDialect } from 'drizzle-orm/mysql-core';
const mysqlDialect = new MySqlDialect();
mysqlDialect.sqlToQuery(sql`select * from ${usersTable} where ${usersTable.id} = ${12}`);
```
```sql
select * from `users` where `users`.`id` = ?; --> [ 12 ]
```
## `sql` select
You can use the sql functionality in partial select queries as well. Partial select queries allow you to
retrieve specific fields or columns from a table rather than fetching the entire row.
For more detailed information about partial select queries, you can refer to the Core API
documentation available at **[Core API docs](/docs/mysql/select#basic-and-partial-select)**.
**Select different custom fields from table**
Here you can see a usage for **[`sql`](/docs/mysql/sql#sqlt)**, **[`sql``.mapWith()`](/docs/mysql/sql#sqlmapwith)**, **[`sql``.as()`](/docs/mysql/sql#sqlast)**.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'
await db.select({
id: usersTable.id,
lowerName: sql`lower(${usersTable.name})`,
aliasedName: sql`lower(${usersTable.name})`.as('aliased_column'),
count: sql`count(*)`.mapWith(Number)
}).from(usersTable)
```
```sql
select `id`, lower(`name`), lower(`name`) as `aliased_column`, count(*) from `users`;
```
## `sql` in where
Indeed, Drizzle provides a set of available expressions that you can use within the sql template.
However, it is true that databases often have a wider range of expressions available,
including those provided through extensions or other means.
To ensure flexibility and enable you to utilize any expressions that are not natively
supported by Drizzle, you have the freedom to write the SQL template
directly using the sql function. This allows you to leverage the full power of
SQL and incorporate any expressions or functionalities specific to your target database.
By using the sql template, you are not restricted to only the predefined expressions in Drizzle.
Instead, you can express complex queries and incorporate any supported expressions that
the underlying database system provides.
**Filtering by `id` but with sql**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'
const id = 77
await db.select()
.from(usersTable)
.where(sql`${usersTable.id} = ${id}`)
```
```sql
select * from `users` where `users`.`id` = ?; --> [ 77 ]
```
**Advanced fulltext search where statement**
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'
const searchPattern = "%Ale%"
await db.select()
.from(usersTable)
.where(sql`lower(${usersTable.name}) like lower(${searchPattern})`)
```
```sql
select * from `users` where lower(`users`.`name`) like lower(?); --> [ "%Ale%" ]
```
## `sql` in orderBy
The `sql` template can indeed be used in the ORDER BY clause when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'
await db.select().from(usersTable).orderBy(sql`${usersTable.id} desc nulls first`)
```
```sql
select * from `users` order by `users`.`id` desc nulls first;
```
## `sql` in having and groupBy
The `sql` template can indeed be used in the HAVING and GROUP BY clauses when you need specific functionality for ordering that is not
available in Drizzle, but you prefer not to resort to raw SQL.
```typescript copy
import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'
await db.select({
projectId: usersTable.projectId,
count: sql`count(${usersTable.id})`.mapWith(Number)
}).from(usersTable)
.groupBy(sql`${usersTable.projectId}`)
.having(sql`count(${usersTable.id}) > 300`)
```
```sql
select `project_id`, count(`users`.`id`) from users group by `users`.`project_id` having count(`users`.`id`) > 300;
```
Source: https://orm.drizzle.team/docs/mysql/transactions
# Transactions
SQL transaction is a grouping of one or more SQL statements that interact with a database.
A transaction in its entirety can commit to a database as a single logical unit
or rollback (become undone) as a single logical unit.
Drizzle ORM provides APIs to run SQL statements in transactions:
```ts copy
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
Drizzle ORM supports `savepoints` with nested transactions API:
```ts copy {7-9}
const db = drizzle(...)
await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
await tx.transaction(async (tx2) => {
await tx2.update(users).set({ name: "Mr. Dan" }).where(eq(users.name, "Dan"));
});
});
```
You can embed business logic to the transaction and rollback whenever needed:
```ts copy {7}
const db = drizzle(...)
await db.transaction(async (tx) => {
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
if (account.balance < 100) {
// This throws an exception that rollbacks the transaction.
tx.rollback()
}
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});
```
You can return values from the transaction:
```ts copy {8}
const db = drizzle(...)
const newBalance: number = await db.transaction(async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
return account.balance;
});
```
You can use transactions with **[relational queries](/docs/mysql/rqb)**:
```ts
const db = drizzle({ schema })
await db.transaction(async (tx) => {
await tx.query.users.findMany({
with: {
accounts: true
}
});
});
```
We provide dialect-specific transaction configuration APIs:
```ts {6-8}
await db.transaction(
async (tx) => {
await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, "Dan"));
await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, "Andrew"));
}, {
isolationLevel: "read committed",
accessMode: "read write",
withConsistentSnapshot: true,
}
);
interface MySqlTransactionConfig {
isolationLevel?:
| "read uncommitted"
| "read committed"
| "repeatable read"
| "serializable";
accessMode?: "read only" | "read write";
withConsistentSnapshot?: boolean;
}
```
Source: https://orm.drizzle.team/docs/mysql/typebox-legacy
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox-legacy
### Install the dependencies
drizzle-orm@rc @sinclair/typebox
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = Value.Parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { mysqlView } from 'drizzle-orm/mysql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const usersView = mysqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = Value.Parse(usersViewSchema, ...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createInsertSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { id?: number | undefined, name: string, age: number } = Value.Parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { id?: number | undefined, name: string, age: number } = Value.Parse(userInsertSchema, user); // Will parse successfully
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 { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createUpdateSchema } from 'drizzle-orm/typebox-legacy';
import { Value } from '@sinclair/typebox/value';
import { eq } from "drizzle-orm";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: { id?: number | undefined, name?: string | undefined, age?: number | undefined } = Value.Parse(userUpdateSchema, user); // Will parse successfully
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 Typebox schema will overwrite it.
```ts copy
import { int, json, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/typebox-legacy';
import { Type } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
bio: text(),
preferences: json()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => Type.String({ ...schema, maxLength: 20 }), // Extends schema
bio: (schema) => Type.String({ ...schema, maxLength: 1000 }), // Extends schema before becoming nullable/optional
preferences: Type.Object({ theme: Type.String() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = Value.Parse(userSelectSchema, ...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Typebox instance**
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSchemaFactory } from 'drizzle-orm/typebox';
import { t } from 'elysia'; // Extended Typebox instance
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const { createInsertSchema } = createSchemaFactory({ typeboxInstance: t });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => t.Number({ ...schema, error: "`name` must be a string" })
});
```
### Data type reference
```ts
mysql.boolean();
// Schema
Type.Boolean();
```
```ts
mysql.mysqlEnum('name', ['val1', 'val2']);
// Schema
Type.Enum({ 'val1': 'val1', 'val2': 'val2' });
```
```ts
mysql.date({ mode: 'date' });
mysql.datetime({ mode: 'date' });
mysql.timestamp({ mode: 'date' });
// Schema
Type.Date();
```
```ts
mysql.binary();
mysql.date({ mode: 'string' });
mysql.datetime({ mode: 'string' });
mysql.decimal();
mysql.time();
mysql.timestamp({ mode: 'string' });
mysql.varbinary();
// Schema
Type.String();
```
```ts
mysql.tinyblob({ mode: 'string' });
mysql.tinyblob();
Type.String({ maxLength: 255 })
```
```ts
mysql.blob({ mode: 'string' });
mysql.blob();
Type.String({ maxLength: 65_535 })
```
```ts
mysql.mediumblob({ mode: 'string' });
mysql.mediumblob();
Type.String({ maxLength: 16_777_215 })
```
```ts
mysql.longblob({ mode: 'string' });
mysql.longblob();
Type.String({ maxLength: 4_294_967_295 })
```
```ts
mysql.char({ length: ... });
// Schema
Type.String({ minLength: length, maxLength: length });
```
```ts
mysql.varchar({ length: ... });
// Schema
Type.String({ maxLength: length });
```
```ts
binary({ length: ... });
// Schema
Type.RegExp(/^[01]*$/, { maxLength: length })
```
```ts
mysql.varbinary({ length: ... });
// Schema
Type.RegExp(/^[01]*$/, { maxLength: length })
```
```ts
mysql.tinytext();
// Schema
Type.String({ maxLength: 255 }); // unsigned 8-bit integer limit
```
```ts
mysql.text();
// Schema
Type.String({ maxLength: 65_535 }); // unsigned 16-bit integer limit
```
```ts
mysql.mediumtext();
// Schema
Type.String({ maxLength: 16_777_215 }); // unsigned 24-bit integer limit
```
```ts
mysql.longtext();
// Schema
Type.String({ maxLength: 4_294_967_295 }); // unsigned 32-bit integer limit
```
```ts
mysql.tinytext({ enum: ... });
mysql.mediumtext({ enum: ... });
mysql.text({ enum: ... });
mysql.longtext({ enum: ... });
mysql.char({ enum: ... });
mysql.varchar({ enum: ... });
// Schema
Type.Enum(enum);
```
```ts
mysql.tinyint();
// Schema
Type.Integer({ minimum: -128, maximum: 127 }); // 8-bit integer lower and upper limit
```
```ts
mysql.tinyint({ unsigned: true });
// Schema
Type.Integer({ minimum: 0, maximum: 255 }); // unsigned 8-bit integer lower and upper limit
```
```ts
mysql.smallint();
// Schema
Type.Integer({ minimum: -32_768, maximum: 32_767 }); // 16-bit integer lower and upper limit
```
```ts
mysql.smallint({ unsigned: true });
// Schema
Type.Integer({ minimum: 0, maximum: 65_535 }); // unsigned 16-bit integer lower and upper limit
```
```ts
mysql.float();
// Schema
Type.Number().min(-8_388_608).max(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.mediumint();
// Schema
Type.Integer({ minimum: -8_388_608, maximum: 8_388_607 }); // 24-bit integer lower and upper limit
```
```ts
mysql.float({ unsigned: true });
// Schema
Type.Number({ minimum: 0, maximum: 16_777_215 }); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.mediumint({ unsigned: true });
// Schema
Type.Integer({ minimum: 0, maximum: 16_777_215 }); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.int();
// Schema
Type.Integer({ minimum: -2_147_483_648, maximum: 2_147_483_647 }); // 32-bit integer lower and upper limit
```
```ts
mysql.int({ unsigned: true });
// Schema
Type.Integer({ minimum: 0, maximum: 4_294_967_295 }); // unsgined 32-bit integer lower and upper limit
```
```ts
mysql.double();
mysql.real();
// Schema
Type.Number({ minimum: -140_737_488_355_328, maximum: 140_737_488_355_327 }); // 48-bit integer lower and upper limit
```
```ts
mysql.double({ unsigned: true });
// Schema
Type.Numer({ minimum: 0, maximum: 281_474_976_710_655 }); // unsigned 48-bit integer lower and upper limit
```
```ts
mysql.decimal({ mode: 'number' });
// Schema
Type.Number({ minimum: -9_007_199_254_740_991, maximum: 9_007_199_254_740_991 });
```
```ts
mysql.decimal({ mode: 'bigint' });
// Schema
Type.BigInt({ minimum: -9_223_372_036_854_775_808n, maximum: 9_223_372_036_854_775_807n });
```
```ts
mysql.decimal({ mode: 'number', unsigned: true });
// Schema
Type.Number({ minimum: 0, maximum: 9_007_199_254_740_991 });
```
```ts
mysql.decimal({ mode: 'bigint', unsigned: true });
// Schema
Type.BigInt({ minimum: 0, maximum: 18_446_744_073_709_551_615n });
```
```ts
mysql.bigint({ mode: 'number' });
// Schema
Type.Integer({ minimum: -9_007_199_254_740_991, maximum: 9_007_199_254_740_991 }); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'number', unsigned: true });
// Schema
Type.Integer({ minimum: 0, maximum: 9_007_199_254_740_991 }); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'string' });
// Schema
TypeRegistry.Set('BigIntStringMode', (_, value) => {
if (typeof value !== 'string' || !(/^-?\d+$/.test(value))) {
return false;
}
const bigint = BigInt(value);
if (bigint < -9_223_372_036_854_775_808n || bigint > 9_223_372_036_854_775_807n) {
return false;
}
return true;
});
{ [Kind]: 'BigIntStringMode', type: 'string' }
```
```ts
mysql.bigint({ mode: 'string', unsigned: true });
// Schema
TypeRegistry.Set('UnsignedBigIntStringMode', (_, value) => {
if (typeof value !== 'string' || !(/^\d+$/.test(value))) {
return false;
}
const bigint = BigInt(value);
if (bigint < 0 || bigint > 9_223_372_036_854_775_807n) {
return false;
}
return true;
});
{ [Kind]: 'UnsignedBigIntStringMode', type: 'string' }
```
```ts
mysql.bigint({ mode: 'bigint' });
// Schema
Type.BigInt({ minimum: -9_223_372_036_854_775_808n, maximum: 9_223_372_036_854_775_807n }); // 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'bigint', unsigned: true });
// Schema
Type.BigInt({ minimum: 0, maximum: 18_446_744_073_709_551_615n }); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.serial();
Type.Integer({ minimum: 0, maximum: 9_007_199_254_740_991 })
```
```ts
mysql.year();
// Schema
Type.Integer({ minimum: 1_901, maximum: 2_155 });
```
```ts
mysql.json();
// Schema
Type.Recursive((self) => Type.Union([Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]), Type.Array(self), Type.Record(Type.String(), self)]));
```
Source: https://orm.drizzle.team/docs/mysql/typebox
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# typebox
#### Install the dependencies
drizzle-orm@rc typebox
Allows you to generate [typebox](https://sinclairzx81.github.io/typebox/#/) schemas from Drizzle ORM schemas
**Features**
- Create a select schema for tables and views.
- Create insert and update schemas for tables.
#### Usage
```ts
import { int, mysqlTable, text, timestamp } from 'drizzle-orm/mysql-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/typebox';
import { Type } from 'typebox';
import { Value } from 'typebox/value';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
email: text().notNull(),
role: text({ 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) => Type.Number({ ...schema, minimum: 0 }),
role: Type.String(),
});
// Usage
const isUserValid: boolean = Value.Check(insertUserSchema, {
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
```
Source: https://orm.drizzle.team/docs/mysql/update
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# SQL Update
```typescript copy
await db.update(users)
.set({ name: 'Mr. Dan' })
.where(eq(users.name, 'Dan'));
```
The object that you pass to `update` should have keys that match column names in your database schema.
Values of `undefined` are ignored in the object: to set a column to `null`, pass `null`.
You can pass SQL as a value to be used in the update object, like this:
```typescript copy
await db.update(users)
.set({ updatedAt: sql`NOW()` })
.where(eq(users.name, 'Dan'));
```
### Limit
Use `.limit()` to add `limit` clause to the query - for example:
```typescript
await db.update(usersTable).set({ verified: true }).limit(2);
```
```sql
update `users` set `verified` = true limit 2
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.update(usersTable).set({ verified: true }).orderBy(usersTable.name);
await db.update(usersTable).set({ verified: true }).orderBy(desc(usersTable.name));
// order by multiple fields
await db.update(usersTable).set({ verified: true }).orderBy(usersTable.name, usersTable.name2);
await db.update(usersTable).set({ verified: true }).orderBy(asc(usersTable.name), desc(usersTable.name2));
```
```sql
update `users` set `verified` = true order by `users`.`name`;
update `users` set `verified` = true order by `users`.`name` desc;
update `users` set `verified` = true order by `users`.`name`, `users`.`name2`;
update `users` set `verified` = true order by `users`.`name` asc, `users`.`name2` desc;
```
Source: https://orm.drizzle.team/docs/mysql/upgrade-v1
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
# Upgrading to Drizzle v1
drizzle-orm@rc
-D drizzle-kit@rc
#### Step 1 - Run `drizzle-kit up`
> v3 migration folder discussion: https://github.com/drizzle-team/drizzle-orm/discussions/2832
We've updated the migrations folder structure by:
- removing `journal.json`
- grouping SQL files and snapshots into separate migration folders
- removing the `drizzle-kit drop` command
These changes eliminate potential Git conflicts with the journal file and simplify the process of dropping or fixing conflicted migrations
The drizzle-kit has been architecturally redesigned:
- Migrated from database snapshots to DDL snapshots
Commutativity checks were added:
- Detecting non-commutative migrations across branches
> Commutativity discussion: https://github.com/drizzle-team/drizzle-orm/discussions/5005
To migrate from old structure to a new one you would need to run
drizzle-kit up
#### Step 2 - Review breaking changes
You can find out the full list of breaking changes [here](/docs/mysql/v0-v1-changes#breaking-changes) and make sure to update your code accordingly
If you were using Relational Queries, you need to upgrade to v2:
- [How to migrate relations definition from v1 to v2](/docs/mysql/relations-v1-v2#how-to-migrate-relations-schema-definition-from-v1-to-v2)
- [How to migrate queries from v1 to v2](/docs/mysql/relations-v1-v2#how-to-migrate-queries-from-v1-to-v2)
#### Step 3 - Done â
Source: https://orm.drizzle.team/docs/mysql/v0-v1-changes
import Callout from '@mdx/Callout.astro';
# Changes in v1
This page shows everything that changed from `drizzle-orm@0.x`, `drizzle-kit@0.x`, `drizzle-seed@0.x` to the latest `drizzle-orm@1.0`, `drizzle-kit@1.0`, `drizzle-seed@1.0`
Read more on how to upgrade [here](/docs/mysql/upgrade-v1)
## Breaking changes
#### Relational Queries v1 removed
RQBv1 has been removed. Use [Relational Queries v2](/docs/mysql/relations) with the new `defineRelations()` API instead and new features. You can find more details about migrating from rqb v1 to v2 [here](/docs/mysql/relations-v1-v2)
A new relations system built around `defineRelations()`:
```ts
import { defineRelations } from 'drizzle-orm';
const relations = defineRelations(schema, (r) => ({
users: {
posts: r.many.posts(),
profile: r.one.profiles(),
},
posts: {
author: r.one.users({
from: posts.authorId,
to: users.id,
}),
},
}));
```
#### Validator packages consolidated into `drizzle-orm`
You can still use drizzle-zod/valibot/typebox/arktype validation packages but all new update will be added to Drizzle ORM directly
- drizzle-zod -> drizzle-orm/zod
- drizzle-valibot -> drizzle-orm/valibot
- drizzle-typebox -> drizzle-orm/typebox-legacy (using @sinclair/typebox)
- drizzle-typebox -> drizzle-orm/typebox (using typebox)
- drizzle-arktype -> drizzle-orm/arktype
- new `drizzle-orm/effect-schema` package added
#### New Casing API
The legacy `drizzle({ casing: 'camelCase' })` has been replaced with a table/view/schema-level API:
```ts
import { snakeCase, camelCase } from 'drizzle-orm/mysql-core';
export const users = snakeCase.table('users', {
id: int().primaryKey().autoincrement(),
fullName: text(), // â full_name in DB
createdAt: timestamp(), // â created_at in DB
});
// Available on all entity types:
snakeCase.table / snakeCase.view / snakeCase.materializedView / snakeCase.schema
camelCase.table / camelCase.view / camelCase.materializedView / camelCase.schema
```
#### `.generatedAlwaysAs()` only accepts SQL
```ts
// â Before â accepted raw values
.generatedAlwaysAs('some expression')
// â
After â only sql`` or () => sql``
.generatedAlwaysAs(sql`some expression`)
.generatedAlwaysAs(() => sql`some expression`)
```
#### `getTableColumns` deprecation
Use `getColumns` instead
```ts
// â Before
import { getTableColumns } from 'drizzle-orm';
const columns = getTableColumns(users);
// â
After
import { getColumns } from 'drizzle-orm';
const columns = getColumns(users);
```
#### New migration folder structure (v3)
**Linked discussion**: [#2832](https://github.com/drizzle-team/drizzle-orm/discussions/2832)
- No more `journal.json`
- SQL files and snapshots grouped into separate migration folders
- `drizzle-kit drop` command removed
These changes eliminate potential Git conflicts with the journal file and simplify the process of dropping or fixing conflicted migrations
Read more on how to upgrade [here](/docs/mysql/upgrade-v1)
#### `drizzle-kit push --strict` deprecation
The `--strict` flag has been removed â its behavior is now the default. `drizzle-kit push` always prompts for confirmation for data-loss statements unless the `--force` flag is passed. Use [drizzle-kit push --explain](/docs/mysql/drizzle-kit-push) to preview the SQL that would be executed before running it.
#### Drizzle seed: UUID generator upgraded to v4
The UUID generator now produces RFC 4122 v4 compliant UUIDs, ensuring compatibility with Zod v4 validation. Use `{ version: '1' }` to keep old behavior, or `{ version: '2' | '3' }` for newer generators.
## Other features
#### New data types
New column types have been added to the MySQL dialect: `blob`, `tinyblob`, `mediumblob`, and `longblob`. You can read more [here](/docs/mysql/column-types#blob).
```ts
import { mysqlTable, blob, tinyblob, mediumblob, longblob } from 'drizzle-orm/mysql-core';
const files = mysqlTable('files', {
tiny: tinyblob(),
regular: blob(),
medium: mediumblob(),
large: longblob(),
});
```
#### JIT Mappers (opt-in)
Just-in-time compiled row mappers that make mapping as fast as the raw driver.
You can find out more [here](/docs/mysql/jit-mappers)
```ts {1}
const db = drizzle({ ..., jit: true });
```
#### New MSSQL dialect
New MSSQL dialect support added. You can read more [here](/docs/mssql/get-started-mssql)
#### New CockroachDB dialect
New CockroachDB dialect support added. You can read more [here](/docs/cockroach/get-started-cockroach)
#### New NetlifyDB driver
New NetlifyDB driver support added. You can read more [here](/docs/connect-netlify-db)
Note: The Netlify Database driver is developed and maintained by the Netlify team.
#### SQLcommenter support
Add custom tags to queries. Tags are appended as SQL comments at the end of each query. You can read more [here](/docs/sql-comments)
```ts
db.select().from(users).comment("my_first_tag");
// select "id", "name" from "users" /*my_first_tag*/
```
#### Column aliases via `.as()`
You can find out more aliasing info [here](/docs/aliases)
```ts
const query = db
.select({ age: users.age.as('ageOfUser'), id: users.id.as('userId') })
.from(users)
.orderBy(asc(users.id.as('userId')));
```
#### Full `drizzle-kit` rewrite
The kit has been architecturally redesigned:
- Migrated from database snapshots to DDL snapshots
- Reworked diff detection and application
- Schema introspection reduced from ~10s to < 1s
- Added query hints and explain support for push
#### `drizzle-kit pull --init`
Creates the drizzle migration table and marks the first pulled migration as applied. You can read more [here](/docs/drizzle-kit-pull)
#### `drizzle-kit push --explain`
Shows the SQL that would be executed without actually running it. You can read more [here](/docs/drizzle-kit-push)
#### `drizzle-kit check` â commutativity check
Detects non-commutative migrations across branches â e.g., two branches altering the same column, or one renaming a table that another is altering.
How it works:
- Builds a DAG from snapshot `prevIds` to understand branch structure
- Finds fork points where branches diverged
- Computes the DDL diff from parent snapshot to each branch leaf
- Checks conflicts using a footprint map of which DDL statement types can interfere
If conflicts are found, the report tells you exactly which migrations on which branches are incompatible and which statements collide.
#### `drizzle-kit --ignore-conflicts` flag
Bypasses commutativity checks when you know the conflicts are expected.
#### `drizzle-kit generate`: migration conflict detection
`drizzle-kit generate` now identifies [incompatible changes](/docs/mysql/v0-v1-changes#drizzle-kit-check--commutativity-check) across migration branches. Use `--ignore-conflicts` to bypass.
#### Updates to the migration table
The migration table is automatically upgraded when you run `drizzle-kit up`. Two new columns are added:
- **`name`** â the full migration folder name (e.g. `20250220153045_brave_wolverine`)
- **`applied_at`** â timestamp of when the migration was executed (pre-existing rows are backfilled with `NULL`)
| Column | Type | v0 | v1 |
| --- | --- | --- |:---:|
| `id` | serial | yes | yes |
| `hash` | text | yes | yes |
| `created_at` | bigint | yes | yes (legacy) |
| `name` | text | â | new |
| `applied_at` | timestamp | â | new |
Migrations are now matched by their full folder name instead of timestamps. Even if two migrations are generated within the same second, the name suffix guarantees uniqueness.
During the upgrade, existing rows are backfilled using a multi-step strategy:
1. **Millis match** â truncate stored millis to seconds, match against local folder timestamps
2. **Hash tiebreaker** â if multiple migrations share the same second, disambiguate via the SQL hash
3. **Hash-only fallback** â if millis matching fails entirely, match by hash alone
This means the upgrade handles all edge cases: normal single-developer projects, teams with closely-timed migrations, and even cases where the old journal data doesn't perfectly align with the new folder names.
#### Migrator applies all missing migrations
Previously the migrator only looked for local migrations with a creation date later than the last applied one. Now it detects and applies **every missing migration**, regardless of timestamp ordering. All migrations are matched against the full folder name (14-digit UTC timestamp + name suffix).
#### Top-level `await` in drizzle config files
Top-level `await` is now supported in `drizzle.config.ts` and schema files on Node.js.
#### Drizzle-kit: Build system improvements
- Migrated from `esbuild-register` to `tsx` loader â seamless ESM and CJS support
- Native Bun and Deno launch support
#### TS Schema file processing
Only files with the following extensions are processed: `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mts`, `.cts`, `.tsx`. All other file types are ignored.
#### Drizzle Seed: Ignore columns in refinements
Skip specific columns during seeding â the database default will be used instead:
```ts
await seed(db, schema).refine((f) => ({
users: {
count: 5,
columns: {
name: f.fullName(),
photo: false, // skip â DB default will be used
},
},
}));
```
#### `min` / `max` parameters
Time-based generators now accept boundaries:
```ts
funcs.time({ min: '13:12:13', max: '15:12:13' })
funcs.timestamp({ min: '2024-01-01T00:00:00Z', max: '2025-01-01T00:00:00Z' })
funcs.datetime({ min: '2024-01-01', max: '2025-01-01' })
```
Source: https://orm.drizzle.team/docs/mysql/valibot
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# valibot
### Install the dependencies
drizzle-orm@rc valibot
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = parse(userSelectSchema, rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { mysqlView } from 'drizzle-orm/mysql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const usersView = mysqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = parse(usersViewSchema, ...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createInsertSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { id?: number | undefined, name: string, age: number } = parse(userInsertSchema, user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { id?: number | undefined, name: string, age: number } = parse(userInsertSchema, user); // Will parse successfully
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 { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createUpdateSchema } from 'drizzle-orm/valibot';
import { parse } from 'valibot';
import { eq } from "drizzle-orm";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed: {
id?: number | undefined;
name?: string | undefined;
age?: number | undefined;
} = parse(userUpdateSchema, user); // Will parse successfully
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 Valibot schema will overwrite it.
```ts copy
import { int, json, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/valibot';
import { parse, pipe, maxLength, object, string } from 'valibot';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
bio: text(),
preferences: json()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => pipe(schema, maxLength(20)), // Extends schema
bio: (schema) => pipe(schema, maxLength(1000)), // Extends schema before becoming nullable/optional
preferences: object({ theme: string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = parse(userSelectSchema, ...);
```
### Data type reference
```ts
mysql.boolean();
// Schema
boolean();
```
```ts
mysql.mysqlEnum('name', ['val1', 'val2']);
// Schema
enum({ val1: "val1", val2: "val2" });
```
```ts
mysql.date({ mode: 'date' });
mysql.datetime({ mode: 'date' });
mysql.timestamp({ mode: 'date' });
// Schema
date();
```
```ts
mysql.binary();
mysql.date({ mode: 'string' });
mysql.datetime({ mode: 'string' });
mysql.decimal();
mysql.time();
mysql.timestamp({ mode: 'string' });
mysql.varbinary();
// Schema
string();
```
```ts
mysql.tinyblob({ mode: 'string' });
mysql.tinyblob();
pipe(string(), maxLength(255))
```
```ts
mysql.blob({ mode: 'string' });
mysql.blob();
pipe(string(), maxLength(65_535))
```
```ts
mysql.mediumblob({ mode: 'string' });
mysql.mediumblob();
pipe(string(), maxLength(16_777_215))
```
```ts
mysql.longblob({ mode: 'string' });
mysql.longblob();
pipe(string(), maxLength(4_294_967_295))
```
```ts
mysql.char({ length: ... });
// Schema
pipe(string(), length(length));
```
```ts
mysql.varchar({ length: ... });
// Schema
pipe(string(), maxLength(length));
```
```ts
binary({ length: ... });
// Schema
pipe(string(), regex(/^[01]*$/), maxLength(length))
```
```ts
mysql.varbinary({ length: ... });
// Schema
pipe(string(), regex(/^[01]*$/), maxLength(length))
```
```ts
mysql.tinytext();
// Schema
pipe(string(), maxLength(255));
```
```ts
mysql.text();
// Schema
pipe(string(), maxLength(65_535));
```
```ts
mysql.mediumtext();
// Schema
pipe(string(), maxLength(16_777_215));
```
```ts
mysql.longtext();
// Schema
pipe(string(), maxLength(4_294_967_295));
```
```ts
mysql.tinytext({ enum: ... });
mysql.mediumtext({ enum: ... });
mysql.text({ enum: ... });
mysql.longtext({ enum: ... });
mysql.char({ enum: ... });
mysql.varchar({ enum: ... });
// Schema
enum(enum);
```
```ts
mysql.tinyint();
// Schema
pipe(number(), minValue(-128), maxValue(127), int()); // 8-bit integer lower and upper limit
```
```ts
mysql.tinyint({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(255), int()); // unsigned 8-bit integer lower and upper limit
```
```ts
mysql.smallint();
// Schema
pipe(number(), minValue(-32_768), maxValue(32_767), int()); // 16-bit integer lower and upper limit
```
```ts
mysql.smallint({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(65_535), int()); // unsigned 16-bit integer lower and upper limit
```
```ts
mysql.float();
// Schema
pipe(number(), minValue(-8_388_608), maxValue(8_388_607)); // 24-bit integer lower and upper limit
```
```ts
mysql.mediumint();
// Schema
pipe(number(), minValue(-8_388_608), maxValue(8_388_607), int()); // 24-bit integer lower and upper limit
```
```ts
mysql.float({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(16_777_215)); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.mediumint({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(16_777_215), int()); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.int();
// Schema
pipe(number(), minValue(-2_147_483_648), maxValue(2_147_483_647), int()); // 32-bit integer lower and upper limit
```
```ts
mysql.int({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(4_294_967_295), int()); // unsgined 32-bit integer lower and upper limit
```
```ts
mysql.double();
mysql.real();
// Schema
pipe(number(), minValue(-140_737_488_355_328), maxValue(140_737_488_355_327)); // 48-bit integer lower and upper limit
```
```ts
mysql.double({ unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(281_474_976_710_655)); // unsigned 48-bit integer lower and upper limit
```
```ts
mysql.decimal({ mode: 'number' });
// Schema
pipe(number(), minValue(-9_007_199_254_740_991), maxValue(9_007_199_254_740_991))
```
```ts
mysql.decimal({ mode: 'bigint' });
// Schema
pipe(bigint(), minValue(-9_223_372_036_854_775_808n), maxValue(9_223_372_036_854_775_807n))
```
```ts
mysql.decimal({ mode: 'number', unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(9_007_199_254_740_991))
```
```ts
mysql.decimal({ mode: 'bigint', unsigned: true });
// Schema
pipe(bigint(), minValue(0), maxValue(18_446_744_073_709_551_615n))
```
```ts
mysql.bigint({ mode: 'number' });
// Schema
pipe(number(), minValue(-9_007_199_254_740_991), maxValue(9_007_199_254_740_991), int()); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'number', unsigned: true });
// Schema
pipe(number(), minValue(0), maxValue(9_007_199_254_740_991), int()); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'string' });
// Schema
pipe(
string(),
regex(/^-?\d+$/),
transform((v) => BigInt(v)),
minValue(-9_223_372_036_854_775_808n),
maxValue(9_223_372_036_854_775_807n),
transform((v) => v.toString()),
)
```
```ts
mysql.bigint({ mode: 'string', unsigned: true });
// Schema
pipe(
string(),
regex(/^\d+$/),
transform((v) => BigInt(v)),
minValue(0n),
maxValue(9_223_372_036_854_775_807n),
transform((v) => v.toString()),
);
```
```ts
mysql.bigint({ mode: 'bigint' });
// Schema
pipe(bigint(), minValue(-9_223_372_036_854_775_808n), maxValue(9_223_372_036_854_775_807n)); // 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'bigint', unsigned: true });
// Schema
pipe(bigint(), minValue(0n), maxValue(18_446_744_073_709_551_615n)); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.serial();
// Schema
pipe(number(), minValue(0), maxValue(9_007_199_254_740_991), int()); // Javascript max. safe integer
```
```ts
mysql.year();
// Schema
pipe(number(), minValue(1_901), maxValue(2_155), int());
```
```ts
mysql.json();
// Schema
union([union([string(), number(), boolean(), null_()]), array(any()), record(string(), any())]);
```
Source: https://orm.drizzle.team/docs/mysql/views
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Views
There're several ways you can declare views with Drizzle ORM.
You can declare views that have to be created or you can declare views that already exist in the database.
You can declare views statements with an inline `query builder` syntax, with `standalone query builder` and with raw `sql` operators.
When views are created with either inlined or standalone query builders, view columns schema will be automatically inferred,
yet when you use `sql` you have to explicitly declare view columns schema.
### Declaring views
```ts filename="schema.ts" copy {14-15}
import { text, mysqlTable, mysqlView, int, timestamp } from "drizzle-orm/mysql-core";
import { eq } from "drizzle-orm";
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = mysqlView("user_view").as((qb) => qb.select().from(user));
export const customersView = mysqlView("customers_view").as((qb) => qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
...
CREATE ALGORITHM = undefined SQL SECURITY definer VIEW `customers_view` AS (select * from `user` where `user`.`role` = 'customer');
CREATE ALGORITHM = undefined SQL SECURITY definer VIEW `user_view` AS (select * from `user`);
```
You can also declare views using `standalone query builder`, it works exactly the same way:
```ts filename="schema.ts" copy {4,16-17}
import { text, mysqlTable, mysqlView, int, timestamp, QueryBuilder } from "drizzle-orm/mysql-core";
import { eq } from "drizzle-orm";
const qb = new QueryBuilder();
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = mysqlView("user_view").as(qb.select().from(user));
export const customersView = mysqlView("customers_view").as(qb.select().from(user).where(eq(user.role, "customer")));
```
```sql
...
CREATE ALGORITHM = undefined SQL SECURITY definer VIEW `customers_view` AS (select * from `user` where `user`.`role` = 'customer');
CREATE ALGORITHM = undefined SQL SECURITY definer VIEW `user_view` AS (select * from `user`);
```
### Declaring views with raw SQL
Whenever you need to declare view using a syntax that is not supported by the query builder,
you can directly use `sql` operator and explicitly specify view columns schema.
```ts copy {15}
// regular view
import { eq, sql } from "drizzle-orm";
import { int, mysqlTable, mysqlView, text, timestamp } from "drizzle-orm/mysql-core";
export const users = mysqlTable("users", {
id: int().primaryKey().autoincrement(),
name: text(),
cityId: int("city_id"),
});
export const newYorkers = mysqlView("new_yorkers", {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
cityId: int("city_id").notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);
```
```sql
...
CREATE ALGORITHM = undefined SQL SECURITY definer VIEW `new_yorkers` AS (select * from `users` where `users`.`city_id` = 1);
```
### Declaring existing views
When you're provided with a read only access to an existing view in the database you should use `.existing()` view configuration,
`drizzle-kit` will ignore and will not generate a `create view` statement in the generated migration.
```ts {18}
import { int, mysqlTable, mysqlView, text, timestamp } from "drizzle-orm/mysql-core";
export const user = mysqlTable("user", {
id: int().primaryKey().autoincrement(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
// regular view
export const trimmedUser = mysqlView("trimmed_user", {
id: int("id"),
name: text("name"),
email: text("email"),
}).existing();
```
Source: https://orm.drizzle.team/docs/mysql/zod
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# zod
### Install the dependencies
drizzle-orm@rc zod
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/zod';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userSelectSchema = createSelectSchema(users);
const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Error: `age` is not returned in the above query
const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Will parse successfully
```
Views are also supported.
```ts copy
import { mysqlView } from 'drizzle-orm/mysql-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/zod';
const usersView = mysqlView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
const usersViewSchema = createSelectSchema(usersView);
const parsed: { id: number; name: string; age: number } = usersViewSchema.parse(...);
```
### Insert schema
Defines the shape of data to be inserted into the database - can be used to validate API requests.
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createInsertSchema } from 'drizzle-orm/zod';
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userInsertSchema = createInsertSchema(users);
const user = { name: 'John' };
const parsed: { id?: number | undefined, name: string, age: number } = userInsertSchema.parse(user); // Error: `age` is not defined
const user = { name: 'Jane', age: 30 };
const parsed: { id?: number | undefined, name: string, age: number } = userInsertSchema.parse(user); // Will parse successfully
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 { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createUpdateSchema } from 'drizzle-orm/zod';
import { eq } from "drizzle-orm";
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const userUpdateSchema = createUpdateSchema(users);
const user = { age: 35 };
const parsed:
{
id?: number | undefined;
name?: string | undefined;
age?: number | undefined
} = userUpdateSchema.parse(user); // Will parse successfully
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 Zod schema will overwrite it.
```ts copy
import { int, json, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSelectSchema } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = mysqlTable('users', {
id: int().primaryKey(),
name: text().notNull(),
bio: text(),
preferences: json()
});
const userSelectSchema = createSelectSchema(users, {
name: (schema) => schema.max(20), // Extends schema
bio: (schema) => schema.max(1000), // Extends schema before becoming nullable/optional
preferences: z.object({ theme: z.string() }) // Overwrites the field, including its nullability
});
const parsed: {
id: number;
name: string,
bio: string | null;
preferences: {
theme: string;
};
} = userSelectSchema.parse(...);
```
### Factory functions
For more advanced use cases, you can use the `createSchemaFactory` function.
**Use case: Using an extended Zod instance**
```ts copy
import { int, mysqlTable, text } from 'drizzle-orm/mysql-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from '@hono/zod-openapi'; // Extended Zod instance
const users = mysqlTable('users', {
id: int().primaryKey().autoincrement(),
name: text().notNull(),
age: int().notNull()
});
const { createInsertSchema } = createSchemaFactory({ zodInstance: z });
const userInsertSchema = createInsertSchema(users, {
// We can now use the extended instance
name: (schema) => schema.openapi({ example: 'John' })
});
```
**Use case: Type coercion**
```ts copy
import { mysqlTable, timestamp } from 'drizzle-orm/mysql-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from 'zod/v4';
const users = mysqlTable('users', {
...,
createdAt: timestamp().notNull()
});
const { createInsertSchema } = createSchemaFactory({
// This configuration will only coerce dates. Set `coerce` to `true` to coerce all data types or specify others
coerce: {
date: true
}
});
const userInsertSchema = createInsertSchema(users);
// The above is the same as this:
const userInsertSchema = z.object({
...,
createdAt: z.coerce.date()
});
```
### Data type reference
```ts
mysql.boolean();
// Schema
z.boolean();
```
```ts
mysql.mysqlEnum('name', ['val1', 'val2']);
// Schema
z.enum(['val1', 'val2']);
```
```ts
mysql.date({ mode: 'date' });
mysql.datetime({ mode: 'date' });
mysql.timestamp({ mode: 'date' });
// Schema
z.date();
```
```ts
mysql.binary();
mysql.date({ mode: 'string' });
mysql.datetime({ mode: 'string' });
mysql.decimal();
mysql.time();
mysql.timestamp({ mode: 'string' });
mysql.varbinary();
// Schema
z.string();
```
```ts
mysql.tinyblob({ mode: 'string' });
mysql.tinyblob();
z.string().max(255)
```
```ts
mysql.blob({ mode: 'string' });
mysql.blob();
z.string().max(65_535)
```
```ts
mysql.mediumblob({ mode: 'string' });
mysql.mediumblob();
z.string().max(16_777_215)
```
```ts
mysql.longblob({ mode: 'string' });
mysql.longblob();
z.string().max(4_294_967_295)
```
```ts
mysql.char({ length: ... });
// Schema
z.string().length(length);
```
```ts
mysql.varchar({ length: ... });
// Schema
z.string().max(length);
```
```ts
mysql.binary({ length: ... });
// Schema
schema.regex(/^[01]*$/).max(length);
```
```ts
mysql.varbinary({ length: ... });
// Schema
schema.regex(/^[01]*$/).max(length);
```
```ts
mysql.tinytext();
// Schema
z.string().max(255);
```
```ts
mysql.text();
// Schema
z.string().max(65_535);
```
```ts
mysql.mediumtext();
// Schema
z.string().max(16_777_215);
```
```ts
mysql.longtext();
// Schema
z.string().max(4_294_967_295);
```
```ts
mysql.tinytext({ enum: ... });
mysql.mediumtext({ enum: ... });
mysql.text({ enum: ... });
mysql.longtext({ enum: ... });
mysql.char({ enum: ... });
mysql.varchar({ enum: ... });
// Schema
z.enum(enum);
```
```ts
mysql.tinyint();
// Schema
z.number().min(-128).max(127).int(); // 8-bit integer lower and upper limit
```
```ts
mysql.tinyint({ unsigned: true });
// Schema
z.number().min(0).max(255).int(); // unsigned 8-bit integer lower and upper limit
```
```ts
mysql.smallint();
// Schema
z.number().min(-32_768).max(32_767).int(); // 16-bit integer lower and upper limit
```
```ts
mysql.smallint({ unsigned: true });
// Schema
z.number().min(0).max(65_535).int(); // unsigned 16-bit integer lower and upper limit
```
```ts
mysql.float();
// Schema
z.number().min(-8_388_608).max(8_388_607); // 24-bit integer lower and upper limit
```
```ts
mysql.mediumint();
// Schema
z.number().min(-8_388_608).max(8_388_607).int(); // 24-bit integer lower and upper limit
```
```ts
mysql.float({ unsigned: true });
// Schema
z.number().min(0).max(16_777_215); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.mediumint({ unsigned: true });
// Schema
z.number().min(0).max(16_777_215).int(); // unsigned 24-bit integer lower and upper limit
```
```ts
mysql.int();
// Schema
z.number().min(-2_147_483_648).max(2_147_483_647).int(); // 32-bit integer lower and upper limit
```
```ts
mysql.int({ unsigned: true });
// Schema
z.number().min(0).max(4_294_967_295).int(); // unsgined 32-bit integer lower and upper limit
```
```ts
mysql.double();
mysql.real();
// Schema
z.number().min(-140_737_488_355_328).max(140_737_488_355_327); // 48-bit integer lower and upper limit
```
```ts
mysql.double({ unsigned: true });
// Schema
z.number().min(0).max(281_474_976_710_655); // unsigned 48-bit integer lower and upper limit
```
```ts
mysql.decimal({ mode: 'number' });
// Schema
z.number().min(-9_007_199_254_740_991).max(9_007_199_254_740_991);
```
```ts
mysql.decimal({ mode: 'bigint' });
// Schema
z.bigint().min(-9_223_372_036_854_775_808n).max(9_223_372_036_854_775_807n);
```
```ts
mysql.decimal({ mode: 'number', unsigned: true });
// Schema
z.number().min(0).max(9_007_199_254_740_991);
```
```ts
mysql.decimal({ mode: 'bigint', unsigned: true });
// Schema
z.bigint().min(0).max(18_446_744_073_709_551_615n);
```
```ts
mysql.bigint({ mode: 'number' });
// Schema
z.number().min(-9_007_199_254_740_991).max(9_007_199_254_740_991).int(); // Javascript min. and max. safe integers
```
```ts
mysql.bigint({ mode: 'number', unsigned: true });
// Schema
z.number().min(0).max(9_007_199_254_740_991).int(); // Javascript max safe integer
```
```ts
mysql.bigint({ mode: 'string' });
// Schema
z.string().regex(/^-?\d+$/).transform(BigInt).pipe(
zod.bigint().gte(-9_223_372_036_854_775_808n).lte(9_223_372_036_854_775_807n),
).transform(String)
```
```ts
mysql.bigint({ mode: 'string', unsigned: true });
// Schema
z.string().regex(/^\d+$/).transform(BigInt).pipe(
zod.bigint().gte(0n).lte(9_223_372_036_854_775_807n),
).transform(String)
```
```ts
mysql.bigint({ mode: 'bigint' });
// Schema
z.bigint().min(-9_223_372_036_854_775_808n).max(9_223_372_036_854_775_807n); // 64-bit integer lower and upper limit
```
```ts
mysql.bigint({ mode: 'bigint', unsigned: true });
// Schema
z.bigint().min(0).max(18_446_744_073_709_551_615n); // unsigned 64-bit integer lower and upper limit
```
```ts
mysql.serial();
// Schema
z.number().min(0).max(9_007_199_254_740_991).int(); // Javascript max. safe integer
```
```ts
mysql.year();
// Schema
z.number().min(1_901).max(2_155).int();
```
```ts
mysql.json();
// Schema
z.union([z.union([z.string(), z.number(), z.boolean(), z.null()]), z.record(z.any()), z.array(z.any())]);
```
Source: https://orm.drizzle.team/docs/overview
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro'
# Drizzle ORM
Drizzle ORM is a headless TypeScript ORM with a head. đ˛
> Drizzle is a good friend who's there for you when necessary and doesn't bother when you need some space.
It looks and feels simple, performs on day _1000_ of your project,\
lets you do things your way, and is there when you need it.
**It's the only ORM with both [relational](/docs/rqb) and [SQL-like](/docs/select) query APIs**,
providing you the best of both worlds when it comes to accessing your relational data.
Drizzle is lightweight, performant, typesafe, non-lactose, gluten-free, sober, flexible and **serverless-ready by design**.
Drizzle is not just a library, it's an experience. đ¤Š
[](https://bestofjs.org/projects/drizzle-orm)
## Headless ORM?
First and foremost, Drizzle is a library and a collection of complementary opt-in tools.
**ORM** stands for _object relational mapping_, and developers tend to call Django-like or Spring-like tools an ORM.
We truly believe it's a misconception based on legacy nomenclature, and we call them **data frameworks**.
With data frameworks you have to build projects **around them** and not **with them**.
**Drizzle** lets you build your project the way you want, without interfering with your project or structure.
Using Drizzle you can define and manage database schemas in TypeScript, access your data in a SQL-like
or relational way, and take advantage of opt-in tools
to push your developer experience _through the roof_. đ¤¯
## Why SQL-like?
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate/abstract you away from SQL, which
leads to a double learning curve: needing to know 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 can have zero to no
learning curve and access to the full power of SQL.
We bring all the familiar **[SQL schema](/docs/sql-schema-declaration)**, **[queries](/docs/select)**,
**[automatic migrations](/docs/migrations)** and **[one more thing](/docs/rqb)**. â¨
```typescript copy
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
```
```typescript copy
// manage your schema
export const countries = pgTable('countries', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
});
export const cities = pgTable('cities', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
countryId: integer('country_id').references(() => countries.id),
});
```
```sql
-- generate migrations
CREATE TABLE IF NOT EXISTS "countries" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar(256)
);
CREATE TABLE IF NOT EXISTS "cities" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar(256),
"country_id" integer
);
ALTER TABLE "cities" ADD CONSTRAINT "cities_country_id_countries_id_fk" FOREIGN KEY ("country_id") REFERENCES "countries"("id") ON DELETE no action ON UPDATE no action;
```
## Why not SQL-like?
We're always striving for a perfectly balanced solution, and while SQL-like does cover 100% of the needs,
there are certain common scenarios where you can query data in a better way.
We've built the **[Queries API](/docs/rqb)** for you, so you can fetch relational nested data from the database
in the most convenient and performant way, and never think about joins and data mapping.
**Drizzle always outputs exactly 1 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
},
});
```
## Serverless?
The best part is no part. **Drizzle has exactly 0 dependencies!**

Drizzle ORM is dialect-specific, slim, performant and serverless-ready **by design**.
We've spent a lot of time to make sure you have best-in-class SQL dialect support, including Postgres, MySQL, and others.
Drizzle operates natively through industry-standard database drivers. We support all major **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)** or **[SingleStore](/docs/singlestore/get-started-singlestore)** drivers out there, and we're adding new ones **[really fast](https://twitter.com/DrizzleORM/status/1653082492742647811?s=20)**.
## Welcome on board!
More and more companies are adopting Drizzle in production, experiencing immense benefits in both DX and performance.
**We're always there to help, so don't hesitate to reach out. We'll gladly assist you in your Drizzle journey!**
We have an outstanding **[Discord community](https://driz.link/discord)** and welcome all builders to our **[Twitter](https://twitter.com/drizzleorm)**.
Now go build something awesome with Drizzle and your **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)** or **[SQLite](/docs/sqlite/get-started-sqlite)** database. đ
### Video Showcase
{/* tRPC + NextJS App Router = Simple Typesafe APIs
Jack Herrington 19:17
https://www.youtube.com/watch?v=qCLV0Iaq9zU */}
{/* https://www.youtube.com/watch?v=qDunJ0wVIec */}
{/* https://www.youtube.com/watch?v=NZpPMlSAez0 */}
{/* https://www.youtube.com/watch?v=-A0kMiJqQRY */}
Source: https://orm.drizzle.team/docs/perf-serverless
# Drizzle Serverless performance
You can get immense benefits with `serverless functions` like AWS Lambda or Vercel Server Functions (they're AWS Lambda based),
since they can live up to 15mins and reuse both database connections and prepared statements.
On the other, hand `edge functions` tend to clean up straight after they're invoked which leads to little to no performance benefits.
To reuse your database connection and prepared statements you just have to declare them outside of handler scope:
```ts
const databaseConnection = ...;
const db = drizzle({ client: databaseConnection });
const prepared = db.select().from(...).prepare();
// AWS handler
export const handler = async (event: APIGatewayProxyEvent) => {
return prepared.execute();
}
```
Source: https://orm.drizzle.team/docs/pg/aliases
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, pgTable, integer, text } from "drizzle-orm/pg-core";
const employees = pgTable("employees", {
id: integer().primaryKey(),
name: text(),
managerId: integer("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:
```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);
```
```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));
```
```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);
```
```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);
```
```sql
with "sq" as (select upper("name") as "name" from "users")
select "name" from "sq";
```
Source: https://orm.drizzle.team/docs/pg/arktype
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# arktype
### Install the dependencies
drizzle-orm@rc arktype
### Select schema
Defines the shape of data queried from the database - can be used to validate API responses.
```ts copy
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from "arktype";
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().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 { pgEnum, pgView } from 'drizzle-orm/pg-core';
import { gt } from 'drizzle-orm';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from "arktype";
const roles = pgEnum('roles', ['admin', 'basic']);
const rolesSchema = createSelectSchema(roles);
const parsed: ArkErrors | 'admin' | 'basic' = rolesSchema(...);
const usersView = pgView('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 { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createInsertSchema } from 'drizzle-orm/arktype';
import { ArkErrors } from "arktype";
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().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 { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { createUpdateSchema } from 'drizzle-orm/arktype';
import { eq } from "drizzle-orm";
import { ArkErrors } from "arktype";
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
age: integer().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 { pgTable, text, integer, json } from 'drizzle-orm/pg-core';
import { createSelectSchema } from 'drizzle-orm/arktype';
import { ArkErrors, type } from 'arktype';
const users = pgTable('users', {
id: integer().generatedAlwaysAsIdentity().primaryKey(),
name: text().notNull(),
bio: text(),
preferences: json()
});
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
```ts
pg.boolean();
// Schema
type.boolean;
```
```ts
pgEnum('name', ['val1', 'val2']);
// Schema
type.enumerated('val1', 'val2')
```
```ts
pg.date({ mode: 'date' });
pg.timestamp({ mode: 'date' });
// Schema
type.Date;
```
```ts
pg.date({ mode: 'string' });
pg.timestamp({ mode: 'string' });
pg.cidr();
pg.inet();
pg.interval();
pg.macaddr();
pg.macaddr8();
pg.numeric();
pg.text();
pg.sparsevec();
pg.time();
// Schema
type.string;
```
```ts
pg.bit({ dimensions: ... });
// Schema
type(`/^[01]{${column.dimensions}}$/`);
```
```ts
pg.uuid();
// Schema
type(/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/iu);
```
```ts
pg.char({ length: ... });
// Schema
type.string.exactlyLength(length);
```
```ts
pg.varchar({ length: ... });
// Schema
type.string.atMostLength(length);
```
```ts
pg.text({ enum: ... });
pg.char({ enum: ... });
pg.varchar({ enum: ... });
// Schema
type.enumerated(...enum);
```
```ts
pg.smallint();
pg.smallserial();
// Schema
type.keywords.number.integer.atLeast(-32_768).atMost(32_767); // 16-bit integer lower and upper limit
```
```ts
pg.real();
// Schema
type.number.atLeast(-8_388_608).atMost(8_388_607); // 24-bit integer lower and upper limit
```
```ts
pg.integer();
pg.serial();
// Schema
type.keywords.number.integer.atLeast(-2_147_483_648).atMost(2_147_483_647); // 32-bit integer lower and upper limit
```
```ts
pg.doublePrecision();
// Schema
type.number.atLeast(-140_737_488_355_328).atMost(140_737_488_355_327); // 48-bit integer lower and upper limit
```
```ts
pg.bigint({ mode: 'number' });
pg.bigserial({ mode: 'number' });
// Schema
type.keywords.number.integer.atLeast(-9_007_199_254_740_991).atMost(9_007_199_254_740_991); // Javascript min. and max. safe integers
```
```ts
pg.bigint({ mode: 'bigint' });
pg.bigserial({ mode: 'bigint' });
// Schema
type.bigint.narrow(
(value, ctx) => value < -9_223_372_036_854_775_808n ? ctx.mustBe('greater than') : value > 9_223_372_036_854_775_807n ? ctx.mustBe('less than') : true
); // 64-bit integer lower and upper limit
```
```ts
pg.geometry({ type: 'point', mode: 'tuple' });
pg.point({ mode: 'tuple' });
// Schema
type([type.number, type.number]);
```
```ts
pg.geometry({ type: 'point', mode: 'xy' });
pg.point({ mode: 'xy' });
// Schema
type({ x: type.number, y: type.number });
```
```ts
pg.halfvec({ dimensions: ... });
pg.vector({ dimensions: ... });
// Schema
type.number.array().exactlyLength(dimensions);
```
```ts
pg.line({ mode: 'abc' });
// Schema
type({ a: type.number, b: type.number, c: type.number });
```
```ts
pg.line({ mode: 'tuple' });
// Schema
type([type.number, type.number, type.number]);
```
```ts
pg.json();
pg.jsonb();
// Schema
type('string | number | boolean | null').or(type('unknown.any[] | Record'));
```
```ts
pg.dataType().array(...);
// Schema
baseDataTypeSchema.array().exactlyLength(size);
```
```ts
pg.bytea();
// Schema
type.unknown.narrow((value) => value instanceof Buffer).as()
```
Source: https://orm.drizzle.team/docs/pg/batch-api
import Section from '@mdx/Section.astro';
# Batch API
Drizzle supports running SQL statements in a batch with the Neon HTTP driver for PostgreSQL.
```ts
const batchResponse = await db.batch([
db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }),
db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)),
db.query.usersTable.findMany({}),
db.select().from(usersTable).where(eq(usersTable.id, 1)),
db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable),
]);
```
```ts
type BatchResponse = [
{ id: number }[],
NeonHttpQueryResult,
{ id: number; name: string; verified: number; invitedBy: number | null }[],
{ id: number; name: string; verified: number; invitedBy: number | null }[],
{ id: number; invitedBy: number | null }[],
]
```
All possible builders that can be used inside `db.batch`:
```ts
db.query..findMany(),
db.query..findFirst(),
db.select()...,
db.update()...,
db.delete()...,
db.insert()...,
```
Source: https://orm.drizzle.team/docs/pg/cache
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: "cacheman@upstash.com" });
```
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() });
```
```ts
import Keyv from "keyv";
export class TestGlobalCache extends Cache {
private globalTtl: number = 1000;
// This object will be used to store which query keys were used
// for a specific table, so we can later use it for invalidation.
private usedTablesPerKey: Record = {};
constructor(private kv: Keyv = new Keyv()) {
super();
}
// For the strategy, we have two options:
// - 'explicit': The cache is used only when .$withCache() is added to a query.
// - 'all': All queries are cached globally.
// The default behavior is 'explicit'.
override strategy(): "explicit" | "all" {
return "all";
}
// This function accepts query and parameters that cached into key param,
// allowing you to retrieve response values for this query from the cache.
override async get(key: string): Promise {
const res = (await this.kv.get(key)) ?? undefined;
return res;
}
// This function accepts several options to define how cached data will be stored:
// - 'key': A hashed query and parameters.
// - 'response': An array of values returned by Drizzle from the database.
// - 'tables': An array of tables involved in the select queries. This information is needed for cache invalidation.
// - 'isTag': A boolean flag indicating whether this cache entry represents a tag-based query.
// When true, the entry is associated with a specific tag rather than table-level invalidation,
// allowing you to invalidate cached queries by tag name. See the UpstashCache:
// https://github.com/drizzle-team/drizzle-orm/blob/beta/drizzle-orm/src/cache/upstash/cache.ts for an example
//
// For example, if a query uses the "users" and "posts" tables, you can store this information. Later, when the app executes
// any mutation statements on these tables, you can remove the corresponding key from the cache.
// If you're okay with eventual consistency for your queries, you can skip this option.
override async put(
key: string,
response: any,
tables: string[],
isTag: boolean = false,
config?: CacheConfig,
): Promise {
const ttl = config?.px ?? (config?.ex ? config.ex * 1000 : this.globalTtl);
await this.kv.set(key, response, ttl);
for (const table of tables) {
const keys = this.usedTablesPerKey[table];
if (keys === undefined) {
this.usedTablesPerKey[table] = [key];
} else {
keys.push(key);
}
}
}
// This function is called when insert, update, or delete statements are executed.
// You can either skip this step or invalidate queries that used the affected tables.
//
// The function receives an object with two keys:
// - 'tags': Used for queries labeled with a specific tag, allowing you to invalidate by that tag.
// - 'tables': The actual tables affected by the insert, update, or delete statements,
// helping you track which tables have changed since the last cache update.
override async onMutate(params: {
tags: string | string[];
tables: string | string[] | Table | Table[];
}): Promise {
const tagsArray = params.tags
? Array.isArray(params.tags)
? params.tags
: [params.tags]
: [];
const tablesArray = params.tables
? Array.isArray(params.tables)
? params.tables
: [params.tables]
: [];
const keysToDelete = new Set();
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
const keys = this.usedTablesPerKey[tableName] ?? [];
for (const key of keys) keysToDelete.add(key);
}
if (keysToDelete.size > 0 || tagsArray.length > 0) {
for (const tag of tagsArray) {
await this.kv.delete(tag);
}
for (const key of keysToDelete) {
await this.kv.delete(key);
for (const table of tablesArray) {
const tableName = is(table, Table)
? getTableName(table)
: (table as string);
this.usedTablesPerKey[tableName] = [];
}
}
}
}
}
```
## Limitations
#### Queries that won't be handled by the `cache` extension:
- Using cache with raw queries, such as:
```ts
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
```ts
await db.query.users.findMany();
```
- Using cache with AWS Data API drivers
- Using cache with views
Source: https://orm.drizzle.team/docs/pg/codecs
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# Codecs
## What are codecs?
Codecs are a driver-aware transform layer that sits between your JavaScript values and the database. They solve the problem of **different drivers returning and accepting data in different formats** for the same column types.
## Why are they needed?
1. **Driver differences** â each PG driver (`node-postgres`, `postgres-js`, `pglite`, etc.) parses and serializes types differently. Codecs normalize these differences so your app code doesn't have to care which driver you're using.
2. **JSON context** â when a column is selected inside a JSON function (like `jsonAgg`, `jsonBuildObject`, or relational queries), the database returns values in JSON format which may differ from the regular format. For example, `bigint` in JSON becomes a number (losing precision). Codecs handle this by casting to text before JSON-ification (`castInJson`) and then parsing it back (`normalizeInJson`).
## How codecs work
Codecs have two layers: **cast** and **normalize**.
```
âââââââââââââââââââââââââââââââââââââââââ
â Cast layer (DB level) â
â SELECT "bigint"::text FROM "users" â
âââââââââââââââââââââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââ
â Normalize layer (Code level) â
â "123" â BigInt("123") â 123n â
ââââââââââââââââââââââââââââââââââââââ
```
**Cast** operates at the database level â it wraps a column in a SQL `::type`
**Normalize** operates at the code level â it transforms the raw value into the type you/driver expect
Both layers can have separate variants for different query contexts:
For reads (SELECT):
```
SQL generation: SELECT "col"::text FROM ... â cast layer modifies SQL
âŧ
Database executes: returns text representation
âŧ
JS result mapping: parseLineABC(rawValue) â normalize layer transforms in JS
âŧ
Your code gets: { a: 1, b: 2, c: 3 }
```
For writes (INSERT/UPDATE):
```
Your JS value: { key: "val" }
âŧ
JS param building: JSON.stringify(value) â normalize layer transforms in JS
âŧ
SQL generation: INSERT ... VALUES ($1::jsonb) â cast layer modifies SQL
âŧ
Database receives: '{"key":"val"}' with type hint
```
Each layer operates across 3 contexts, each with scalar and array variants:
```
Cast (SQL) Normalize (JS)
ââââââââââââââââââââ ââââââââââââââââââââââââ
Regular SELECT â cast â â normalize â
â castArray â â normalizeArray â
âââââââââââââââââââ⤠ââââââââââââââââââââââââ¤
Inside JSON â castInJson â â normalizeInJson â
(jsonAgg, RQB) â castArrayInJson â â normalizeArrayInJson â
âââââââââââââââââââ⤠ââââââââââââââââââââââââ¤
Params â castParam â â normalizeParam â
(INSERT/UPDATE) â castArrayParam â â normalizeParamArray â
ââââââââââââââââââââ ââââââââââââââââââââââââ
```
JSON context exists because databases serialize values differently inside JSON â a `bigint` that normally comes back as a string from the driver will come back as a lossy number inside a JSON object. So codecs need separate handling: cast to `::text` in SQL before JSON wrapping, then parse the text back in JS after.
| Method | When | Example SQL |
|---|---|:---:|
| `cast` | Column in SELECT | `"col"::text` |
| `castArray` | Array column in SELECT | `"col"::text[]` |
| `castInJson` | Column inside JSON functions | `"col"::text` (inside `json_agg`) |
| `castArrayInJson` | Array column inside JSON | |
| `castParam` | Param placeholder in INSERT/UPDATE/WHERE | `$1::date` |
| `castArrayParam` | Array param placeholder | `$1::date[]` |
| Method | When | Example |
|---|---|:---:|
| `normalize` | SELECT result â JS value | `"123"` â `123n` |
| `normalizeArray` | SELECT array result â JS array | `["123", "456"]` â `[123n, 456n]` |
| `normalizeInJson` | SELECT result inside JSON â JS | JSON `bigint` â `BigInt` |
| `normalizeArrayInJson` | SELECT array inside JSON â JS | |
| `normalizeParam` | JS value â driver param (INSERT/UPDATE/WHERE) | `{ key: "val" }` â `'{"key":"val"}'` |
| `normalizeParamArray` | JS array â driver array param | `[1, 2]` â `{1,2}` (PG array literal) |
## Are they enabled by default?
Yes. Every driver ships default codecs. When you call `drizzle(client)`, the driver's codecs are automatically used.
You don't need to do anything to enable them.
## How codecs work in built-in column types
Every built-in PG column class declares a `codec` string identifier:
```ts {3,8,11,14,19,22,27}
// pg-core/columns/integer.ts
class PgInteger extends PgColumn {
override readonly codec = 'int';
}
// pg-core/columns/bigint.ts
class PgBigInt53 extends PgColumn {
override readonly codec = 'bigint:number';
}
class PgBigInt64 extends PgColumn {
override readonly codec = 'bigint';
}
class PgBigIntString extends PgColumn {
override readonly codec = 'bigint:string';
}
// pg-core/columns/line.ts
class PgLineABC extends PgColumn {
override readonly codec = 'line';
}
class PgLineTuple extends PgColumn {
override readonly codec = 'line:tuple'
}
...
```
This identifier is a lookup key into the driver's codec map. If the driver defines transforms for that key, they're applied. If not, the value passes through untouched.
For example, `integer` has codec `'int'`. The `node-postgres` codec map has **no entry** for `'int'` â integers don't need any transformation. But `bigint` has codec `'bigint'`, and node-postgres defines:
```ts
// node-postgres/codecs.ts
bigint: {
normalize: BigInt, // SELECT: "123" â 123n
normalizeArray: arrayCompatNormalize(BigInt), // SELECT array: ["123"] â [123n]
}
```
## Overriding codecs for built-in types
Built-in columns (like `integer()`, `bigint()`, `date()`, `text()`, etc.) have a hardcoded `codec` string â you can't change which codec identifier a column uses. But you **can** change what that codec identifier does by overriding the driver's codec map.
Every PG driver accepts a `codecs` option in `drizzle()`:
```ts {3-6}
const db = drizzle(client, {
codecs: {
"bigint:number": {
cast: (name) => sql`${name}::text`,
normalize: BigInt,
},
},
});
```
## How codecs work in `customType()`
`toDriver`/`fromDriver` on `customType` and codecs are **separate layers**.
`toDriver`/`fromDriver` are per-column instance transforms. Codecs are driver-level transforms and both are applied
On reads - `codec normalize` first â then `fromDriver`
On writes - `toDriver` first â then `codec normalizeParam`
Read more about custom types [here](/docs/custom-types)
When defining a custom column type, you can specify which codec to use via the `codec` property:
```ts
import { customType } from 'drizzle-orm/pg-core';
// Use an existing codec by name
const customBigint = customType<{ data: bigint; driverData: string }>({
dataType() {
return "bigint";
},
fromDriver(value: string) {
return BigInt(Number(value) * 1000); // â extra column mapping logic
},
codec: "bigint", // â uses the driver's bigint codec
});
// Codec as a function (useful when codec depends on config)
const customDataType = customType<{
data: number;
driverData: bigint | number;
config?: { mode: "bigint" | "number" };
configRequired: true;
}>({
dataType() {
return `custom_type`;
},
fromDriver(value: bigint | number) {
return Number(value) / 1000; // â extra column mapping logic
},
codec: (config) => {
// â can be dynamic based on config
if (!config || config.mode === "bigint") {
return "bigint";
}
return "bigint:number";
},
});
// No codec â skip codec transforms entirely
const rawCustom = customType<{ data: string; driverData: string }>({
dataType() {
return 'my_type';
},
// codec is undefined by default â no codec transforms applied
});
```
The `codec` field accepts:
- A `string` â one of the PostgreSQL type identifiers (e.g. `'bigint'`, `'date'`, `'json'`, `'text'`, etc.)
- A `function` `(config) => string | undefined` â dynamically resolve the codec based on the column's config
- `undefined` (default) â no codec, values pass through as-is
Source: https://orm.drizzle.team/docs/pg/column-types
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
We have native support for all of them, yet if that's not enough for you, feel free to create **[custom types](/docs/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 API` to define a mapping strategy for Drizzle.
You can read more about it [here](/docs/sql-schema-declaration#shape-your-data-schema)
### integer
`integer` `int` `int4`
Signed 4-byte integer
If you need `integer autoincrement` please refer to **[serial.](#serial)**
```typescript
import { integer, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
int: integer()
});
```
```sql
CREATE TABLE "table" (
"int" integer
);
```
```typescript
import { sql } from "drizzle-orm";
import { integer, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
int1: integer().default(10),
int2: integer().default(sql`10`)
});
```
```sql
CREATE TABLE "table" (
"int1" integer DEFAULT 10,
"int2" integer DEFAULT 10
);
```
### smallint
`smallint` `int2`
Small-range signed 2-byte integer
If you need `smallint autoincrement` please refer to **[smallserial.](#smallserial)**
```typescript
import { smallint, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
smallint: smallint()
});
```
```sql
CREATE TABLE "table" (
"smallint" smallint
);
```
```typescript
import { sql } from "drizzle-orm";
import { smallint, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
smallint1: smallint().default(10),
smallint2: smallint().default(sql`10`)
});
```
```sql
CREATE TABLE "table" (
"smallint1" smallint DEFAULT 10,
"smallint2" smallint DEFAULT 10
);
```
### bigint
`bigint` `int8`
Signed 8-byte integer
If you need `bigint autoincrement` please refer to **[bigserial.](#bigserial)**
If you're expecting values above 2^31 but below 2^53, you can utilise `mode: 'number'` and deal with javascript number as opposed to bigint.
```typescript
import { bigint, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
bigint: bigint({ mode: 'number' })
});
// will be inferred as `number`
bigint: bigint({ mode: 'number' })
// will be inferred as `bigint`
bigint: bigint({ mode: 'bigint' })
```
```sql
CREATE TABLE "table" (
"bigint" bigint
);
```
```typescript
import { sql } from "drizzle-orm";
import { bigint, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
bigint1: bigint({ mode: "number" }).default(10),
bigint2: bigint({ mode: "number" }).default(sql`10`),
});
```
```sql
CREATE TABLE "table" (
"bigint1" bigint DEFAULT 10,
"bigint2" bigint DEFAULT 10
);
```
## ---
### serial
`serial` `serial4`
Auto incrementing 4-bytes integer, notational convenience for creating unique identifier columns (similar to the `AUTO_INCREMENT` property supported by some other databases).
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL)**
```typescript
import { serial, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
serial: serial(),
});
```
```sql
CREATE TABLE "table" (
"serial" serial NOT NULL
);
```
### smallserial
`smallserial` `serial2`
Auto incrementing 2-bytes integer, notational convenience for creating unique identifier columns (similar to the `AUTO_INCREMENT` property supported by some other databases).
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL)**
```typescript
import { smallserial, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
smallserial: smallserial(),
});
```
```sql
CREATE TABLE "table" (
"smallserial" smallserial NOT NULL
);
```
### bigserial
`bigserial` `serial8`
Auto incrementing 8-bytes integer, notational convenience for creating unique identifier columns (similar to the `AUTO_INCREMENT` property supported by some other databases).
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL)**
If you're expecting values above 2^31 but below 2^53, you can utilise `mode: 'number'` and deal with javascript number as opposed to bigint.
```typescript
import { bigserial, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
bigserial: bigserial({ mode: 'number' }),
});
```
```sql
CREATE TABLE "table" (
"bigserial" bigserial NOT NULL
);
```
### ---
### boolean
PostgreSQL provides the standard SQL type boolean.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-boolean.html)**
```typescript
import { boolean, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
boolean: boolean(),
boolean1: boolean().default(true),
boolean2: boolean().default(sql`true`),
});
```
```sql
CREATE TABLE "table" (
"boolean" boolean,
"boolean1" boolean DEFAULT true,
"boolean2" boolean DEFAULT true
);
```
## ---
### bytea
PostgreSQL provides the standard SQL type bytea.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-binary.html)**
```typescript
import { bytea, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
bytea: bytea()
});
```
```sql
CREATE TABLE "table" (
"bytea" bytea,
);
```
## ---
### text
`text`
Variable-length(unlimited) character string.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-character.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
```typescript
import { text, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
text: text()
});
// will be inferred as text: "value1" | "value2" | null
text: text({ enum: ["value1", "value2"] })
```
```sql
CREATE TABLE "table" (
"text" text
);
```
### varchar
`character varying(n)` `varchar(n)`
Variable-length character string, can store strings up to **`n`** characters (not bytes).
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-character.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to PostgreSQL docs.
```typescript
import { varchar, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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)`
Fixed-length, blank padded character string, can store strings up to **`n`** characters(not bytes).
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-character.html)**
You can define `{ enum: ["value1", "value2"] }` config to infer `insert` and `select` types, it **won't** check runtime values.
The `length` parameter is optional according to PostgreSQL docs.
```typescript
import { char, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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)
);
```
## ---
### numeric
`numeric` `decimal`
Exact numeric of selectable precision. Can store numbers with a very large number of digits, up to 131072 digits before the decimal point and up to 16383 digits after the decimal point.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL)**
```typescript
import { numeric, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
numeric1: numeric(),
numeric2: numeric({ precision: 100 }),
numeric3: numeric({ precision: 100, scale: 20 }),
numericNum: numeric({ mode: 'number' }),
numericBig: numeric({ mode: 'bigint' }),
});
```
```sql
CREATE TABLE "table" (
"numeric1" numeric,
"numeric2" numeric(100),
"numeric3" numeric(100, 20),
"numericNum" numeric,
"numericBig" numeric
);
```
### decimal
An alias of **[numeric.](#numeric)**
### real
`real` `float4`
Single precision floating-point number (4 bytes)
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html)**
```typescript
import { sql } from "drizzle-orm";
import { real, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
real1: real(),
real2: real().default(10.1),
real3: real().default(sql`10.1`),
});
```
```sql
CREATE TABLE "table" (
"real1" real,
"real2" real DEFAULT 10.1,
"real3" real DEFAULT 10.1
);
```
### double precision
`double precision` `float8`
Double precision floating-point number (8 bytes)
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-numeric.html)**
```typescript
import { sql } from "drizzle-orm";
import { doublePrecision, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
double1: doublePrecision(),
double2: doublePrecision().default(10.1),
double3: doublePrecision().default(sql`10.1`),
});
```
```sql
CREATE TABLE "table3" (
"double1" double precision,
"double2" double precision DEFAULT 10.1,
"double3" double precision DEFAULT 10.1
);
```
## ---
### json
`json`
Textual JSON data, as specified in **[RFC 7159.](https://tools.ietf.org/html/rfc7159)**
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-json.html)**
```typescript
import { sql } from "drizzle-orm";
import { json, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
json1: json(),
json2: json().default({ foo: "bar" }),
json3: json().default(sql`'{"foo": "bar"}'`),
});
```
```sql
CREATE TABLE "table" (
"json1" json,
"json2" json DEFAULT '{"foo":"bar"}',
"json3" json DEFAULT '{"foo": "bar"}'
);
```
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 }
json: json().$type<{ foo: string }>();
// will be inferred as string[]
json: json().$type();
// won't compile
json: json().$type().default({});
```
### jsonb
`jsonb`
Binary JSON data, decomposed.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-json.html)**
```typescript
import { jsonb, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
jsonb1: jsonb(),
jsonb2: jsonb().default({ foo: "bar" }),
jsonb3: jsonb().default(sql`'{"foo": "bar"}'`),
});
```
```sql
CREATE TABLE "table" (
"jsonb1" jsonb,
"jsonb2" jsonb DEFAULT '{"foo":"bar"}',
"jsonb3" jsonb DEFAULT '{"foo": "bar"}'
);
```
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();
// won't compile
jsonb: jsonb().$type().default({});
```
## ---
### uuid
`uuid`
The data type uuid stores Universally Unique Identifiers (UUID) as defined by RFC 4122, ISO/IEC 9834-8:2005, and related standards
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-uuid.html)**
```ts
import { uuid, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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`
Time of day with or without time zone.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-datetime.html)**
```typescript
import { time, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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 time zone,
"time3" time(6),
"time4" time(6) with time zone
);
```
### timestamp
`timestamp` `timestamptz` `timestamp with time zone` `timestamp without time zone`
Date and time with or without time zone.
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-datetime.html)**
```typescript
import { sql } from "drizzle-orm";
import { timestamp, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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
How mapping works for `timestamp` and `timestamp with timezone`:
As PostgreSQL docs stated:
> In a literal that has been determined to be timestamp without time zone, PostgreSQL will silently ignore any time zone indication.
> That is, the resulting value is derived from the date/time fields in the input value, and is not adjusted for time zone.
>
> For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT).
An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone.
If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter,
and is converted to UTC using the offset for the timezone zone.
So for `timestamp with timezone` you will get back string converted to a timezone set in your Postgres instance.
You can check timezone using this sql query:
```sql
show timezone;
```
### date
`date`
Calendar date (year, month, day)
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-datetime.html)**
```typescript
import { date, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('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" }),
```
### interval
`interval`
Time span
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-datetime.html)**
```typescript
import { interval, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
interval1: interval(),
interval2: interval({ fields: "day to second", precision: 6 }),
interval3: interval({ fields: "month" })
});
```
```sql
CREATE TABLE "table" (
"interval1" interval,
"interval2" interval day to second(6),
"interval3" interval month
);
```
## ---
### point
`point`
Geometric point type
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-geometric.html#DATATYPE-GEOMETRIC-POINTS)**
Type `point` has 2 modes for mappings from the database: `tuple` and `xy`.
- `tuple` will be accepted for insert and mapped on select to a tuple. So, the database Point(1,2) will be typed as [1,2] with drizzle.
- `xy` will be accepted for insert and mapped on select to an object with x, y coordinates. So, the database Point(1,2) will be typed as `{ x: 1, y: 2 }` with drizzle
```typescript
export const items = pgTable('items', {
point: point(),
pointObj: point({ mode: 'xy' }),
});
```
```sql
CREATE TABLE "items" (
"point" point,
"pointObj" point
);
```
### line
`line`
Geometric line type
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-geometric.html#DATATYPE-LINE)**
Type `line` has 2 modes for mappings from the database: `tuple` and `abc`.
- `tuple` will be accepted for insert and mapped on select to a tuple. So, the database Line `{1,2,3}` will be typed as `[1,2,3]` with drizzle.
- `abc` will be accepted for insert and mapped on select to an object with a, b, and c constants from the equation `Ax + By + C = 0`. So, the database Line `{1,2,3}` will be typed as `{ a: 1, b: 2, c: 3 }` with drizzle.
```typescript
export const items = pgTable('items', {
line: line(),
lineObj: line({ mode: 'abc' }),
});
```
```sql
CREATE TABLE "items" (
"line" line,
"lineObj" line
);
```
## ---
### 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 PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-enum.html)**
```typescript
import { pgEnum, pgTable } from "drizzle-orm/pg-core";
export const moodEnum = pgEnum('mood', ['sad', 'ok', 'happy']);
export const table = pgTable('table', {
mood: moodEnum(),
});
```
```sql
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE "table" (
"mood" "mood"
);
```
## ---
### inet
`inet`
The `inet` type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-INET)**
```typescript
import { inet, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
inet: inet(),
inet2: inet().default('127.0.0.1')
});
```
```sql
CREATE TABLE "table" (
"inet" inet,
"inet2" inet DEFAULT '127.0.0.1'
);
```
### cidr
`cidr`
The `cidr` type holds an IPv4 or IPv6 network specification
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-CIDR)**
```typescript
import { cidr, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
cidr: cidr(),
cidr2: cidr().default('127.0.0.1/32')
});
```
```sql
CREATE TABLE "table" (
"cidr" cidr,
"cidr2" cidr DEFAULT '127.0.0.1/32'
);
```
### macaddr
`macaddr`
The `macaddr` type stores MAC (Media Access Control) address
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-MACADDR)**
```typescript
import { macaddr, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
macaddr: macaddr(),
macaddr2: macaddr().default('08:00:2b:01:02:03')
});
```
```sql
CREATE TABLE "table" (
"macaddr" macaddr,
"macaddr2" macaddr DEFAULT '08:00:2b:01:02:03'
);
```
### macaddr8
`macaddr8`
The `macaddr8` type stores MAC addresses in EUI-64 format
For more info please refer to the official PostgreSQL **[docs.](https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-MACADDR8)**
```typescript
import { macaddr8, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
macaddr8: macaddr8(),
macaddr8_2: macaddr8().default('08:00:2b:01:02:03:04:05')
});
```
```sql
CREATE TABLE "table" (
"macaddr8" macaddr8,
"macaddr8_2" macaddr8 DEFAULT '08:00:2b:01:02:03:04:05'
);
```
## ---
### 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;
};
export const users = pgTable('users', {
id: serial().$type().primaryKey(),
jsonField: json().$type(),
});
```
### Identity Columns
PostgreSQL supports identity columns as a way to automatically generate unique integer 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.
**Key Features**
- Automatic Value Generation: Utilizes sequences to generate unique values for each new row.
- Customizable Sequence Options: You can define starting values, increments, and other sequence options.
- Support for Multiple Identity Columns: PostgreSQL allows more than one identity column per table.
**Limitations**
- Manual Insertion Restrictions: For columns defined with GENERATED ALWAYS AS IDENTITY, manual insertion or updates require the OVERRIDING SYSTEM VALUE clause.
- Sequence Constraints: Identity columns depend on sequences, which must be managed correctly to avoid conflicts or gaps.
**Usage example**
```ts
import { pgTable, integer, text } from 'drizzle-orm/pg-core'
export const ingredients = pgTable('ingredients', {
id: integer().primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }),
name: text().notNull(),
description: text(),
});
```
You can specify all properties available for sequences in the `.generatedAlwaysAsIdentity()` function. Additionally, you can specify custom names for these sequences
PostgreSQL docs [reference](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-IDENTITY).
### 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 { integer, pgTable, uuid } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
integer1: integer().default(42),
integer2: integer().default(sql`42`),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sql`gen_random_uuid()`),
});
```
```sql
CREATE TABLE "table" (
"integer1" integer DEFAULT 42,
"integer2" integer 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, pgTable } from "drizzle-orm/pg-core";
import { createId } from '@paralleldrive/cuid2';
export const table = pgTable('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 { integer, timestamp, text, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
updateCounter: integer().default(sql`1`).$onUpdateFn((): SQL => sql`${table.update_counter} + 1`),
updatedAt: timestamp({ mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text().$type().$onUpdate(() => null),
});
```
### Not null
`NOT NULL` constraint dictates that the associated column may not contain a `NULL` value.
```typescript
import { integer, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
integer: integer().notNull(),
});
```
```sql
CREATE TABLE "table" (
"integer" integer 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 { serial, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
id: serial().primaryKey(),
});
```
```sql
CREATE TABLE "table" (
"id" serial PRIMARY KEY
);
```
Source: https://orm.drizzle.team/docs/pg/connect-aws-data-api-pg
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> AWS Data API Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- AWS Data API - [website](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html)
- AWS SDK - [website](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-rds-data/)
#### Step 1 - Install packages
drizzle-orm@rc @aws-sdk/client-rds-data
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import { drizzle } from 'drizzle-orm/aws-data-api/pg';
// These three properties are required. You can also specify
// any property from the RDSDataClient type inside the connection object.
const db = drizzle({ connection: {
database: process.env['DATABASE']!,
secretArn: process.env['SECRET_ARN']!,
resourceArn: process.env['RESOURCE_ARN']!,
}});
await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy
import { drizzle } from 'drizzle-orm/aws-data-api/pg';
import { RDSDataClient } from '@aws-sdk/client-rds-data';
const rdsClient = new RDSDataClient({ region: 'us-east-1' });
const db = drizzle({
client: rdsClient,
database: process.env['DATABASE']!,
secretArn: process.env['SECRET_ARN']!,
resourceArn: process.env['RESOURCE_ARN']!,
});
await db.select().from(...);
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-bun-sql
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Steps from '@mdx/Steps.astro';
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> Bun SQL
- Database [connection basics](/docs/connect-overview) with Drizzle
- Bun - [website](https://bun.sh/docs)
- Bun SQL - native bindings for working with PostgreSQL databases - [read here](https://bun.sh/docs/api/sql)
According to the **[official website](https://bun.sh/)**, Bun is a fast all-in-one JavaScript runtime.
Drizzle ORM natively supports **[`bun sql`](https://bun.sh/docs/api/sql)** module and it's crazy fast đ
#### Step 1 - Install packages
drizzle-orm@rc
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/bun-sql';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy
import 'dotenv/config';
import { drizzle } from 'drizzle-orm/bun-sql';
import { SQL } from 'bun';
const client = new SQL(process.env.DATABASE_URL!);
const db = drizzle({ client });
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-drizzle-proxy
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import Section from "@mdx/Section.astro";
# Drizzle HTTP proxy
- Database [connection basics](/docs/connect-overview) with Drizzle
How an HTTP Proxy works and why you might need it
Drizzle Proxy is used when you need to implement your own driver communication with the database.
It can be used in several cases, such as adding custom logic at the query stage with existing drivers.
The most common use is with an HTTP driver, which sends queries to your server with the database, executes the query
on your database, and responds with raw data that Drizzle ORM can then map to results
```
âââââââââââââââââââââââââââââ âââââââââââââââââââââââââââââââ
â Drizzle ORM â â HTTP Server with Database â
âââŦââââââââââââââââââââââââââ âââââââââââââââââââââââââââŦââââ
â ^ â
â-- 1. Build query 2. Send built query --â â
â â â
â âââââââââââââââââââââââââââââ â â
ââââââââââââââ>â âââââââ â
â HTTP Proxy Driver â â
ââââââââââââââââ â<ââââââââââââââŦââââââââââââ
â âââââââââââââââââââââââââââââ â
â 3. Execute a query + send raw results back
â-- 4. Map data and return
â
v
```
Drizzle ORM also supports simply using asynchronous callback function for executing SQL.
- `sql` is a query string with placeholders.
- `params` is an array of parameters.
- One of the following values will set for `method` depending on the SQL statement - `all`, `execute`.
Drizzle always waits for `{rows: string[][]}` or `{rows: string[]}` for the return value.
- When the `method` is `execute`, you should return a value as `{rows: string[]}`.
- Otherwise, you should return `{rows: string[][]}`.
```typescript copy
// Example of driver implementation
import { drizzle } from 'drizzle-orm/pg-proxy';
import axios from "axios";
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post('http://localhost:3000/query', { sql, params, method });
return { rows: rows.data };
} catch (e: any) {
console.error('Error from pg proxy server: ', e.response.data)
return { rows: [] };
}
});
```
```ts
// Example of server implementation
import { Client } from 'pg';
import express from 'express';
const app = express();
app.use(express.json());
const port = 3000;
const client = new Client('postgres://postgres:postgres@localhost:5432/postgres');
app.post('/query', async (req, res) => {
const { sql, params, method } = req.body;
// prevent multiple queries
const sqlBody = sql.replace(/;/g, '');
try {
const result = await client.query({
text: sqlBody,
values: params,
rowMode: method === 'all' ? 'array': undefined,
});
res.send(result.rows);
} catch (e: any) {
res.status(500).json({ error: e });
}
res.status(500).json({ error: 'Unknown method value' });
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
```
Source: https://orm.drizzle.team/docs/pg/connect-effect-postgres
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Steps from '@mdx/Steps.astro';
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> Effect Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- Effect - [website](https://effect.website/docs)
- **@effect/sql-pg** - A PostgreSQL toolkit for Effect - [read here](https://effect-ts.github.io/effect/docs/sql-pg)
Drizzle has native support for Effect PostgreSQL connections with the `@effect/sql-pg` driver.
#### Step 1 - Install packages
drizzle-orm@rc effect @effect/sql-pg pg
-D drizzle-kit@rc @types/pg
#### Step 2 - Initialize the driver and make a query
Drizzle provides an Effect-native API that integrates with Effect's service pattern. Use `PgDrizzle.makeWithDefaults()`
to quickly create a Drizzle database instance with sensible defaults (no logging, no caching).
```typescript copy
import 'dotenv/config';
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { PgClient } from '@effect/sql-pg';
import * as Effect from 'effect/Effect';
import * as Redacted from 'effect/Redacted';
import { sql } from 'drizzle-orm';
import { types } from 'pg';
// Configure the PgClient layer with type parsers
const PgClientLive = PgClient.layer({
url: Redacted.make(process.env.DATABASE_URL!),
types: {
getTypeParser: (typeId, format) => {
// Return raw values for date/time types to let Drizzle handle parsing
if ([1184, 1114, 1082, 1186, 1231, 1115, 1185, 1187, 1182].includes(typeId)) {
return (val: any) => val;
}
return types.getTypeParser(typeId, format);
},
},
});
const program = Effect.gen(function*() {
// Create the database with default services (no logging, no caching)
const db = yield* PgDrizzle.makeWithDefaults();
// Execute queries
const result = yield* db.execute<{ id: number }>(sql`SELECT 1 as id`);
console.log(result);
});
// Run the program with the PgClient layer
Effect.runPromise(program.pipe(Effect.provide(PgClientLive)));
```
#### Step 3 - Create a DB Layer for dependency injection
For larger applications, create a reusable DB layer that can be composed with other services. This follows Effect's
recommended pattern for dependency injection:
```typescript copy
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { PgClient } from '@effect/sql-pg';
import * as Context from 'effect/Context';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as Redacted from 'effect/Redacted';
import { types } from 'pg';
import * as relations from './schema/relations';
// Configure the PgClient layer
const PgClientLive = PgClient.layer({
url: Redacted.make(process.env.DATABASE_URL!),
types: {
getTypeParser: (typeId, format) => {
if ([1184, 1114, 1082, 1186, 1231, 1115, 1185, 1187, 1182].includes(typeId)) {
return (val: any) => val;
}
return types.getTypeParser(typeId, format);
},
},
});
// Create the DB effect with default services
const dbEffect = PgDrizzle.make({ relations }).pipe(
Effect.provide(PgDrizzle.DefaultServices)
);
// Define a DB service tag for dependency injection
class DB extends Context.Tag('DB')>() {}
// Create a layer that provides the DB service
const DBLive = Layer.effect(
DB,
Effect.gen(function*() {
return yield* dbEffect;
}),
);
// Compose all layers together
const AppLive = Layer.provideMerge(DBLive, PgClientLive);
// Use the DB service in your application
const program = Effect.gen(function*() {
const db = yield* DB;
const users = yield* db.select().from(usersTable);
return users;
});
// Run with all dependencies provided
Effect.runPromise(program.pipe(Effect.provide(AppLive)));
```
#### Advanced: Custom logger and cache configuration
For more control over logging and caching, use `PgDrizzle.make()` directly and provide your own service implementations.
##### Logger configuration
By default, `makeWithDefaults()` uses a no-op logger (no logging). You can enable logging by providing
a different `EffectLogger` implementation:
```typescript copy
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { EffectLogger } from 'drizzle-orm/effect-postgres';
import * as Effect from 'effect/Effect';
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.make({ /* schema, relations, casing */ }).pipe(
// Enable Effect-based logging (uses Effect.log with annotations)
Effect.provide(EffectLogger.layer),
// Provide remaining default services (cache)
Effect.provide(PgDrizzle.DefaultServices),
);
const users = yield* db.select().from(usersTable);
return users;
});
```
**Available logger options:**
- `EffectLogger.Default` - No-op logger (no logging occurs) - this is the default
- `EffectLogger.layer` - Logs queries using Effect's `Effect.log()` with annotations for query SQL and parameters. Integrates with Effect's logging infrastructure.
- `EffectLogger.fromDrizzle(logger)` - Wraps a Drizzle `Logger` instance for use with Effect
- `EffectLogger.layerFromDrizzle(logger)` - Creates an Effect Layer from a Drizzle logger
When using `EffectLogger.layer`, queries are logged via Effect's logging system. You can configure the output
format by providing a different Effect logger layer (e.g., `Logger.pretty` for development, `Logger.json` for production).
**Using a Drizzle logger:**
```typescript copy
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { EffectLogger } from 'drizzle-orm/effect-postgres';
import * as Effect from 'effect/Effect';
import { DefaultLogger } from 'drizzle-orm';
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.make({ /* schema, relations, casing */ }).pipe(
// Use a Drizzle logger wrapped for Effect
Effect.provide(EffectLogger.layerFromDrizzle(new DefaultLogger())),
// Provide remaining default services (cache)
Effect.provide(PgDrizzle.DefaultServices),
);
const users = yield* db.select().from(usersTable);
return users;
});
```
##### Cache configuration
Similarly, you can provide a custom cache implementation:
```typescript copy
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import { EffectLogger } from 'drizzle-orm/effect-postgres';
import { EffectCache } from 'drizzle-orm/cache/core/cache-effect';
import * as Effect from 'effect/Effect';
import { MyCustomCache } from './cache';
const program = Effect.gen(function*() {
const db = yield* PgDrizzle.make({ /* schema, relations, casing */ }).pipe(
// Provide a custom cache wrapped for Effect
Effect.provide(EffectCache.layerFromDrizzle(new MyCustomCache())),
// Provide remaining default services (logger)
Effect.provide(PgDrizzle.DefaultServices),
);
const users = yield* db.select().from(usersTable);
return users;
});
```
**Available cache options:**
- `EffectCache.Default` - No-op cache (no caching occurs) - this is the default
- `EffectCache.fromDrizzle(cache)` - Wraps a Drizzle `Cache` instance for use with Effect
- `EffectCache.layerFromDrizzle(cache)` - Creates an Effect Layer from a Drizzle cache (useful for composing with other layers)
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-neon
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Neon Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- Neon serverless database - [website](https://neon.tech)
- Neon serverless driver - [docs](https://neon.tech/docs/serverless/serverless-driver) & [GitHub](https://github.com/neondatabase/serverless)
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
Drizzle has native support for Neon connections with the `neon-http` and `neon-websockets` drivers. These use the **neon-serverless** driver under the hood.
With the `neon-http` and `neon-websockets` drivers, you can access a Neon database from serverless environments over HTTP or WebSockets instead of TCP.
Querying over HTTP is faster for single, non-interactive transactions.
If you need session or interactive transaction support, or a fully compatible drop-in replacement for the `pg` driver, you can use the WebSocket-based `neon-serverless` driver.
You can connect to a Neon database directly using [Postgres](/docs/get-started/postgresql-new)
For an example of using Drizzle ORM with the Neon Serverless driver in a Cloudflare Worker, **[see here.](http://driz.link/neon-cf-ex)**
To use Neon from a serverful environment, you can use the node-postgres or Postgres.js drivers, as described in Neon's **[official Node.js docs](https://neon.tech/docs/guides/node)**
#### Step 1 - Install packages
drizzle-orm@rc @neondatabase/serverless
-D drizzle-kit@rc
drizzle-orm@rc pg
-D drizzle-kit@rc
drizzle-orm@rc postres
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript
// Make sure to install the '@neondatabase/serverless' package
import { drizzle } from 'drizzle-orm/neon-http';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript
// Make sure to install the '@neondatabase/serverless' package
import { drizzle } from 'drizzle-orm/neon-serverless';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript
// For Node.js - make sure to install the 'ws' and 'bufferutil' packages
import { drizzle } from 'drizzle-orm/neon-serverless';
import ws from 'ws';
const db = drizzle({
connection: process.env.DATABASE_URL,
ws: ws,
});
const result = await db.execute('select 1');
```
Additional configuration is required to use WebSockets in environments where the `WebSocket` global is not defined, such as Node.js.
Add the `ws` and `bufferutil` packages to your project's dependencies, and set `ws` in the Drizzle config.
```typescript
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript
// Make sure to install the 'postgres' package
import { drizzle } from 'drizzle-orm/postgres-js';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
If you need to provide your existing drivers:
```typescript
// Make sure to install the '@neondatabase/serverless' package
import { neon } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle({ client: sql });
const result = await db.execute('select 1');
```
```typescript
// Make sure to install the '@neondatabase/serverless' package
import { Pool } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle({ client: pool })
const result = await db.execute('select 1');
```
```typescript
// For Node.js - make sure to install the 'ws' and 'bufferutil' packages
import { Pool, neonConfig } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-serverless';
neonConfig.webSocketConstructor = ws;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle({ client: pool })
const result = await db.execute('select 1');
```
Additional configuration is required to use WebSockets in environments where the `WebSocket` global is not defined, such as Node.js.
Add the `ws` and `bufferutil` packages to your project's dependencies, and set `ws` in the Drizzle config.
```typescript
// Make sure to install the 'pg' package
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
```
```typescript
// Make sure to install the 'postgres' package
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const queryClient = postgres(process.env.DATABASE_URL);
const db = drizzle({ client: queryClient });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-netlify-db
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Netlify Database
- Database [connection basics](/docs/connect-overview) with Drizzle
- Netlify Database - [website](https://docs.netlify.com/build/data-and-storage/netlify-database/)
The Netlify Database driver is developed and maintained by the Netlify team.
Drizzle has native support for Netlify Database connections, that intelligently selects the optimal underlying Postgres driver based on the runtime environment.
Netlify Database is a managed Postgres database. On Netlify's platform, the same application code runs in two very different contexts:
- Serverless functions: no persistent connections, best served by HTTP-based queries
- Server mode: persistent connections via node-postgres
This adapter abstracts that decision away so consumers can write `drizzle()` once and get the right driver automatically.
#### Step 1 - Install packages
drizzle-orm@rc @netlify/database
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript
import { drizzle } from 'drizzle-orm/netlify-db';
// Connection string is set automatically by the platform
const db = drizzle();
const result = await db.execute('select 1');
```
```typescript
import { drizzle } from 'drizzle-orm/netlify-db';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript
import { drizzle } from 'drizzle-orm/netlify-db';
// Explicit client â consumer controls the driver
const db = drizzle({ client: netlifyDbClient });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-nile
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Nile
- Database [connection basics](/docs/connect-overview) with Drizzle
- Nile Database - [website](https://thenile.dev)
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
According to the **[official website](https://thenile.dev)**, Nile is PostgreSQL re-engineered for multi-tenant apps.
Checkout official **[Nile + Drizzle Quickstart](https://www.thenile.dev/docs/getting-started/languages/drizzle)** and **[Migration](https://www.thenile.dev/docs/getting-started/schema_migrations/drizzle)** docs.
You can use Nile with any of Drizzle's Postgres drivers, we'll be showing the use of `node-postgres` below.
#### Step 1 - Install packages
drizzle-orm@rc pg
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy filename="index.ts"
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/node-postgres'
const db = drizzle(process.env.NILEDB_URL);
const response = await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy filename="index.ts"
// Make sure to install the 'pg' package
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const response = await db.select().from(...);
```
#### Connecting to a virtual tenant database
Nile provides virtual tenant databases, when you set the tenant context, Nile will direct your queries to the virtual database for this particular tenant and all queries will apply to that tenant (i.e. `select * from table` will result records only for this tenant).
In order to set the tenant context, we wrap each query in a transaction that sets the appropriate tenant context before running the transaction.
The tenant ID can simply be passed into the wrapper as an argument:
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/node-postgres';
import { todosTable, tenants } from "./db/schema";
import { sql } from 'drizzle-orm';
import 'dotenv/config';
const db = drizzle(process.env.NILEDB_URL);
function tenantDB(tenantId: string, cb: (tx: any) => T | Promise): Promise {
return db.transaction(async (tx) => {
if (tenantId) {
await tx.execute(sql`set local nile.tenant_id = '${sql.raw(tenantId)}'`);
}
return cb(tx);
}) as Promise;
}
// In a webapp, you'll likely get it from the request path parameters or headers
const tenantId = '01943e56-16df-754f-a7b6-6234c368b400'
const response = await tenantDB(tenantId, async (tx) => {
// No need for a "where" clause here
return await tx.select().from(todosTable);
});
console.log(response);
```
If you are using a web framwork that supports it, you can set up [AsyncLocalStorage](https://nodejs.org/api/async_context.html) and use middleware to populate it with the tenant ID. In this case, your Drizzle client setup will be:
```typescript copy filename="db/index.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import dotenv from "dotenv/config";
import { sql } from "drizzle-orm";
import { AsyncLocalStorage } from "async_hooks";
export const db = drizzle(process.env.NILEDB_URL);
export const tenantContext = new AsyncLocalStorage();
export function tenantDB(cb: (tx: any) => T | Promise): Promise {
return db.transaction(async (tx) => {
const tenantId = tenantContext.getStore();
console.log("executing query with tenant: " + tenantId);
// if there's a tenant ID, set it in the transaction context
if (tenantId) {
await tx.execute(sql`set local nile.tenant_id = '${sql.raw(tenantId)}'`);
}
return cb(tx);
}) as Promise;
}
```
And then, configure a middleware to populate the the AsyncLocalStorage and use `tenantDB` method when handling requests:
```typescript copy filename="app.ts"
// Middleware to set tenant context
app.use("/api/tenants/:tenantId/*", async (c, next) => {
const tenantId = c.req.param("tenantId");
console.log("setting context to tenant: " + tenantId);
return tenantContext.run(tenantId, () => next());
});
// Route handler
app.get("/api/tenants/:tenantId/todos", async (c) => {
const todos = await tenantDB(c, async (tx) => {
return await tx
.select({
id: todoSchema.id,
tenant_id: todoSchema.tenantId,
title: todoSchema.title,
estimate: todoSchema.estimate,
})
.from(todoSchema);
});
return c.json(todos);
});
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-overview
# Database connection with Drizzle
Use these PostgreSQL drivers and providers with Drizzle ORM.
## PostgreSQL connection docs
- [PostgreSQL](/docs/get-started-postgresql)
- [PlanetScale Postgres](/docs/connect-planetscale-postgres)
- [Neon](/docs/connect-neon)
- [Vercel Postgres](/docs/connect-vercel-postgres)
- [Prisma Postgres](/docs/connect-prisma-postgres)
- [Supabase](/docs/connect-supabase)
- [Xata](/docs/connect-xata)
- [PGLite](/docs/connect-pglite)
- [Nile](/docs/connect-nile)
- [Bun SQL](/docs/connect-bun-sql)
- [Effect Postgres](/docs/connect-effect-postgres)
- [Netlify Database](/docs/connect-netlify-db)
- [AWS Data API Postgres](/docs/connect-aws-data-api-pg)
- [Drizzle HTTP proxy](/docs/connect-drizzle-proxy)
Source: https://orm.drizzle.team/docs/pg/connect-pglite
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> PGlite
- Database [connection basics](/docs/connect-overview) with Drizzle
- ElectricSQL - [website](https://electric-sql.com/)
- PgLite driver - [docs](https://pglite.dev/) & [GitHub](https://github.com/electric-sql/pglite)
According to the **[official repo](https://github.com/electric-sql/pglite)**, PGlite is a WASM Postgres build packaged into a TypeScript client library that enables you to run Postgres in the browser, Node.js and Bun, with no need to install any other dependencies. It is only 2.6mb gzipped.
It can be used as an ephemeral in-memory database, or with persistence either to the file system (Node/Bun) or indexedDB (Browser).
Unlike previous "Postgres in the browser" projects, PGlite does not use a Linux virtual machine - it is simply Postgres in WASM.
#### Step 1 - Install packages
drizzle-orm@rc @electric-sql/pglite
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy"
import { drizzle } from 'drizzle-orm/pglite';
const db = drizzle();
await db.select().from(...);
```
```typescript copy"
import { drizzle } from 'drizzle-orm/pglite';
const db = drizzle('path-to-dir');
await db.select().from(...);
```
```typescript copy"
import { drizzle } from 'drizzle-orm/pglite';
// connection is a native PGLite configuration
const db = drizzle({ connection: { dataDir: 'path-to-dir' }});
await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy"
import { PGlite } from '@electric-sql/pglite';
import { drizzle } from 'drizzle-orm/pglite';
// In-memory Postgres
const client = new PGlite();
const db = drizzle({ client });
await db.select().from(users);
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-planetscale-postgres
import Npm from "@mdx/Npm.astro";
import Callout from "@mdx/Callout.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
import Prerequisites from "@mdx/Prerequisites.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> PlanetScale Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- PlanetScale Postgres database - [docs](https://planetscale.com/docs/postgres)
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
PlanetScale offers both MySQL (Vitess) and PostgreSQL databases. This page covers connecting to PlanetScale Postgres.
For PlanetScale MySQL, see the [PlanetScale MySQL connection guide](/docs/mysql/connect-planetscale).
With Drizzle ORM you can connect to PlanetScale Postgres using:
- The standard `node-postgres` driver
- The `@neondatabase/serverless` driver for serverless environments
For detailed instructions on creating a PlanetScale Postgres database and obtaining credentials, see the [PlanetScale Postgres documentation](https://planetscale.com/docs/postgres/tutorials/planetscale-postgres-drizzle).
## node-postgres
#### Step 1 - Install packages
drizzle-orm@rc pg
-D drizzle-kit@rc @types/pg
#### Step 2 - Initialize the driver and make a query
```typescript copy
import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
````
```typescript copy
import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle({
connection: {
connectionString: process.env.DATABASE_URL,
ssl: true
}
});
const result = await db.execute('select 1');
````
```typescript copy
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const result = await db.execute("select 1");
```
## Neon serverless driver
PlanetScale Postgres also supports connections via the [Neon serverless driver](https://planetscale.com/docs/postgres/connecting/neon-serverless-driver). This is a good option for serverless environments like Vercel Functions, Cloudflare Workers, or AWS Lambda.
The driver supports two modes:
- **HTTP mode** â Faster for single queries and non-interactive transactions
- **WebSocket mode** â Required for interactive transactions or session-based features
#### Step 1 - Install packages
drizzle-orm@rc @neondatabase/serverless
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import { neon, neonConfig } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
// Required for PlanetScale Postgres connections
neonConfig.fetchEndpoint = (host) => `https://${host}/sql`;
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle({ client: sql });
const result = await db.execute('select 1');
````
```typescript copy
import { Pool, neonConfig } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-serverless';
// Required for PlanetScale Postgres connections
neonConfig.pipelineConnect = false;
neonConfig.wsProxy = (host, port) => `${host}/v2?address=${host}:${port}`;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
````
```typescript copy
// For Node.js environments - install 'ws' package
import ws from "ws";
import { Pool, neonConfig } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
neonConfig.webSocketConstructor = ws;
// Required for PlanetScale Postgres connections
neonConfig.pipelineConnect = false;
neonConfig.wsProxy = (host, port) => `${host}/v2?address=${host}:${port}`;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle({ client: pool });
const result = await db.execute("select 1");
```
{`postgresql://{username}:{password}@{host}:{port}/postgres?sslmode=verify-full`}
PlanetScale Postgres supports two connection ports:
`5432`: Direct connection to PostgreSQL. Total connections are limited by your cluster's `max_connections` setting.
`6432`: Connection via PgBouncer for connection pooling. Recommended when you have many simultaneous connections.
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-prisma-postgres
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import Section from "@mdx/Section.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Prisma Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- Prisma Postgres serverless database - [website](https://prisma.io/postgres)
- Prisma Postgres direct connections - [docs](https://www.prisma.io/docs/postgres/database/direct-connections)
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
Prisma Postgres is a serverless database built on [unikernels](https://www.prisma.io/blog/announcing-prisma-postgres-early-access). It has a large free tier, [operation-based pricing](https://www.prisma.io/blog/operations-based-billing) and no cold starts.
You can connect to it using either the [`node-postgres`](https://node-postgres.com/) or [`postgres.js`](https://github.com/porsager/postgres) drivers for PostgreSQL.
Prisma Postgres also has a [serverless driver](https://www.prisma.io/docs/postgres/database/serverless-driver) that will be supported with Drizzle ORM in the future.
#### Step 1 - Install packages
drizzle-orm@rc pg
-D drizzle-kit@rc
drizzle-orm@rc postres
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript
// Make sure to install the 'pg' package
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
```
```typescript
// Make sure to install the 'postgres' package
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const queryClient = postgres(process.env.DATABASE_URL);
const db = drizzle({ client: queryClient });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-supabase
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Supabase
- Database [connection basics](/docs/connect-overview) with Drizzle
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
According to the **[official website](https://supabase.com/docs)**, Supabase is an open source Firebase alternative for building secure and performant Postgres backends with minimal configuration.
Checkout official **[Supabase + Drizzle](https://supabase.com/docs/guides/database/drizzle)** docs.
#### Step 1 - Install packages
drizzle-orm@rc postgres
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/postgres-js'
const db = drizzle(process.env.DATABASE_URL);
const allUsers = await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
const client = postgres(process.env.DATABASE_URL)
const db = drizzle({ client });
const allUsers = await db.select().from(...);
```
If you decide to use connection pooling via Supabase (described [here](https://supabase.com/docs/guides/database/connecting-to-postgres#poolers)), and have "Transaction" pool mode enabled, then ensure to turn off prepare, as prepared statements are not supported.
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
// Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(process.env.DATABASE_URL, { prepare: false })
const db = drizzle({ client });
const allUsers = await db.select().from(...);
```
Connect to your database using the Connection Pooler for **serverless environments**, and the Direct Connection for **long-running servers**.
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-vercel-postgres
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> Vercel Postgres
- Database [connection basics](/docs/connect-overview) with Drizzle
- Vercel Postgres database - [website](https://vercel.com/docs/storage/vercel-postgres)
- Vercel Postgres driver - [docs](https://vercel.com/docs/storage/vercel-postgres/sdk) & [GitHub](https://github.com/vercel/storage/tree/main/packages/postgres)
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
According to their **[official website](https://vercel.com/docs/storage/vercel-postgres)**,
Vercel Postgres is a serverless SQL database designed to integrate with Vercel Functions.
Drizzle ORM natively supports both **[@vercel/postgres](https://vercel.com/docs/storage/vercel-postgres)** serverless
driver with `drizzle-orm/vercel-postgres` package and **[`postgres`](#postgresjs)** or **[`pg`](#node-postgres)**
drivers to access Vercel Postgres through `postgesql://`
Check out the official **[Vercel Postgres + Drizzle](https://vercel.com/docs/storage/vercel-postgres/using-an-orm#drizzle)** docs.
#### Step 1 - Install packages
drizzle-orm@rc @vercel/postgres
-D drizzle-kit@rc
#### Step 2 - Prepare Vercel Postgres
Setup a project according to the **[official docs.](https://vercel.com/docs/storage/vercel-postgres/quickstart)**
#### Step 3 - Initialize the driver and make a query
```typescript copy
import { drizzle } from 'drizzle-orm/vercel-postgres';
const db = drizzle();
const result = await db.execute('select 1');
```
If you need to provide your existing driver:
```typescript copy
import { sql } from '@vercel/postgres';
import { drizzle } from 'drizzle-orm/vercel-postgres';
const db = drizzle({ client: sql })
const result = await db.execute('select 1');
```
With **[@vercel/postgres](https://vercel.com/docs/storage/vercel-postgres)** severless package
you can access Vercel Postgres from either serverful or serverless environments with no TCP available,
like Cloudflare Workers, through websockets.
If you're about to use Vercel Postgres from a _serverfull_ environment, you can do it
either with `@vercel/postgres` or directly access the DB through `postgesql://` with
either **[`postgres`](#postgresjs)** or **[`pg`](#node-postgres)**.
#### What's next?
Source: https://orm.drizzle.team/docs/pg/connect-xata
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
# Drizzle \<\> Xata
- Database [connection basics](/docs/connect-overview) with Drizzle
- Drizzle PostgreSQL drivers - [docs](/docs/get-started-postgresql)
**[Xata](https://xata.io)** is a PostgreSQL database platform designed to help developers operate and scale databases with enhanced productivity and performance. Xata provides features like instant copy-on-write database branches, zero-downtime schema changes, data anonymization, AI-powered performance monitoring, and BYOC.
Checkout official **[Xata + Drizzle](https://xata.io/documentation/quickstarts/drizzle)** docs.
#### Step 1 - Install packages
drizzle-orm@rc postgres
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/postgres-js'
const db = drizzle(process.env.DATABASE_URL);
const allUsers = await db.select().from(...);
```
If you need to provide your existing driver:
```typescript copy filename="index.ts"
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
const client = postgres(process.env.DATABASE_URL)
const db = drizzle({ client });
const allUsers = await db.select().from(...);
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/custom-types
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
PostgreSQL exposes `customType` from `drizzle-orm/pg-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, pgTable } from 'drizzle-orm/pg-core';
const customInteger = customType<{ data: number; }>(
{
dataType() {
return 'integer';
},
},
);
export const users = pgTable("users", {
id: customInteger(),
});
```
#### Text
```ts
import { customType, pgTable } from 'drizzle-orm/pg-core';
const customText = customType<{ data: string }>({
dataType() {
return 'text';
},
});
export const users = pgTable('users', {
name: customText(),
});
```
#### Timestamp
```ts
import { customType, pgTable } from 'drizzle-orm/pg-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${precision}${config?.withTimezone ? ' with time zone' : ''}`;
},
fromDriver(value: string): Date {
return new Date(value);
},
codec: (config) => {
if (config?.withTimezone) return 'timestamp with time zone';
return 'timestamp';
},
});
export const users = pgTable('users', {
createdAt: customTimestamp('created_at'),
});
```
Usage for all types will be same as defined functions in Drizzle ORM. For example:
```ts
const usersTable = pgTable('users', {
id: customInteger().primaryKey(),
name: customText().notNull(),
createdAt: customTimestamp('created_at', { withTimezone: true }).notNull()
.default(sql`now()`),
});
```
### Methods and Generic types
| Type | Required | Description |
|---|---|---|
| `data` | Yes | The TypeScript type for the column after selecting/inserting. |
| `driverData` | No | The type the database driver accepts for this data type. |
| `driverOutput` | No | **Deprecated** â use codecs instead (bypasses JSON codecs if used). The type the driver returns, if different from `driverData`. |
| `jsonData` | No | **Deprecated** â use codecs instead (bypasses JSON codecs if used). The type returned when aggregated to JSON. |
| `config` | No | Config object type for `dataType` generation (e.g. `{ length: number }`). |
| `configRequired` | No | Whether the config argument is required. Default `false`. |
| `notNull` | No | Set to `true` if the custom type should be `notNull` by default. |
| `default` | No | Set to `true` if the custom type has a default value. |
| Method | Required | Description |
|---|---|---|
| `dataType` | Yes | Defines the SQL type string. If database data type needs additional params you can use them from `config` param |
| `toDriver` | No | Transform inputs from desired to be used in code format to one suitable for driver |
| `fromDriver` | No | Transform data returned by driver to desired column's output format |
| `codec` | No | Which driver codec to use (see [Codecs](/docs/codecs) section). |
| `fromJson` | No | **Deprecated** â use `codec` instead. Transforms value from JSON context. |
| `forJsonSelect` | No | **Deprecated** â use `codec` instead. Modify selection of column inside JSON functions |
## TS-doc for type definitions
You can check `ts-doc` for types, param definition and examples
```ts
interface 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;
/**
* @deprecated Use codecs instead
*
* Type helper, that represents what type database driver is returning for specific database data type
*
* Needed only in case driver's output and input for type differ
*
* Defaults to {@link driverData}
*/
driverOutput?: unknown;
/**
* @deprecated Use codecs instead
*
* Type helper, that represents what type field returns after being aggregated to JSON
*/
jsonData?: unknown;
/**
* What config type should be used for {@link CustomTypeParams} `dataType` generation
*/
config?: Record;
/**
* 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;
}
interface CustomTypeParams {
/**
* 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 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'];
/**
* @deprecated Use codecs instead; bypasses JSON codecs if used
*
* 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, 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'];
/**
* @deprecated Use codecs instead; bypasses JSON codecs if used
*
* Optional selection modifier function, that is used for modifying selection of column inside 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;
/**
* Select which column type codec will be used for this column
*/
codec?: PostgresColumnType | undefined | ((config: T['config'] | (Equal extends true ? never : undefined)) => PostgresColumnType | undefined);
}
```
## Transform layers and execution order
Custom types have two transform layers: column-level (`toDriver`/`fromDriver`) and driver-level ([codecs](/docs/codecs)). Both run in a specific order. You can read more on how to use codecs with customTypes [here](/docs/codecs#how-codecs-work-in-customtype)
- **Writes:** (Insert/Update) `toDriver` first â codec `normalizeParam` second
- **Reads:** (Select) codec `normalize` first â `fromDriver` second
- If no codec is set, only `toDriver`/`fromDriver` run
- If no `toDriver`/`fromDriver` is set, only codecs run (if defined)
- If neither is set, values pass through untouched
Use both (`toDriver`/`fromDriver` and `codecs`) when you need driver-level normalization AND column-level transformation:
```ts
// DB stores as bigint, driver returns string, codec converts to BigInt,
// but you want a number in your app code
const myColumn = customType<{ data: number; driverData: string }>({
dataType() { return 'bigint'; },
codec: 'bigint', // driver "123" â BigInt(123n)
fromDriver(value) { // BigInt(123n) â Number(123)
return Number(value);
},
});
```
But often you can simplify by choosing the right codec variant:
```ts
// Same result, no fromDriver needed
const myColumn = customType<{ data: number; driverData: string }>({
dataType() { return 'bigint'; },
codec: 'bigint:number', // driver "123" â Number(123) directly
});
```
Source: https://orm.drizzle.team/docs/pg/data-querying
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/sql-schema-declaration)
- How to connect to the database - [Connection Fundamentals](/docs/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.
```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
```ts
await db.insert(users).values({ email: 'user@gmail.com' })
```
```sql
INSERT INTO "users" ("email") VALUES ('user@gmail.com')
```
```ts
await db.update(users)
.set({ email: 'user@gmail.com' })
.where(eq(users.id, 1))
```
```sql
UPDATE "users"
SET "email" = 'user@gmail.com'
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?
Source: https://orm.drizzle.team/docs/pg/delete
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/select#with-clause), [insert](/docs/insert#with-insert-clause), [update](/docs/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"
```
Source: https://orm.drizzle.team/docs/pg/drizzle-config-file
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](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-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: "postgresql",
schema: "./src/schema.ts",
out: "./drizzle",
});
```
```js
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
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: "postgresql",
schema: "./src/schema.ts",
driver: "pglite",
dbCredentials: {
url: "./database/",
},
extensionsFilters: ["postgis"],
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: "postgresql",
schema: "./src/schema/*",
out: "./drizzle",
});
```
## ---
### `dialect`
Dialect of the database you're using
| | |
| :------------ | :----------------------------------- |
| type | |
| default | -- |
| commands | `generate`, `push`, `pull`, `studio`, `migrate`, `up`, `export` |
```ts {4}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
});
```
### `schema`
[`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 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",
});
```
### `driver`
Drizzle Kit automatically picks available database driver from your current project based on the provided `dialect`,
yet some vendor specific databases require a different subset of connection params.
`driver` option let's you explicitely pick those exceptions drivers.
| | |
| :------------ | :----------------- |
| type | `aws-data-api`, `pglite` |
| default | -- |
| commands | `push`, `migrate`, `pull`, `studio` |
```ts {6}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
driver: "aws-data-api",
dbCredentials: {
database: "database",
resourceArn: "resourceArn",
secretArn: "secretArn",
},
});
```
```ts {6}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
driver: "pglite",
dbCredentials: {
// inmemory
url: ":memory:"
// or database folder
url: "./database/"
},
});
```
## ---
### `dbCredentials`
Database connection credentials in a form of `url`,
`user:password@host:port/db` params or exceptions drivers() specific connection options.
| | |
| :------------ | :----------------- |
| type | |
| default | -- |
| commands | `push`, `pull`, `migrate`, `studio` |
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
url: "postgres://user:password@host:port/db",
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
// via connection params
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
host: "host",
port: 5432,
user: "user",
password: "password",
database: "dbname",
ssl: true, // can be boolean | "require" | "allow" | "prefer" | "verify-full" | options from node:tls
}
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
driver: "aws-data-api",
dbCredentials: {
database: "database",
resourceArn: "resourceArn",
secretArn: "secretArn",
},
});
```
```ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: "postgresql",
driver: "pglite",
dbCredentials: {
url: "./database/", // database folder path
}
});
```
### `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: "postgresql",
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` |
```ts
import * as p from "drizzle-orm/pg-core"
export const users = p.pgTable("users", {
id: p.serial(),
firstName: p.text("first-name"),
lastName: p.text("LastName"),
email: p.text(),
phoneNumber: p.text("phone_number"),
});
```
```sql
SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) as data_type FROM pg_catalog.pg_attribute a;
```
```
column_name | data_type
---------------+------------------------
id | serial
first-name | text
LastName | text
email | text
phone_number | text
```
```ts
import * as p from "drizzle-orm/pg-core"
export const users = p.pgTable("users", {
id: p.serial(),
"first-name": p.text("first-name"),
LastName: p.text("LastName"),
email: p.text(),
phone_number: p.text("phone_number"),
});
```
```sql
SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) as data_type FROM pg_catalog.pg_attribute a;
```
```
column_name | data_type
---------------+------------------------
id | serial
first-name | text
LastName | text
email | text
phone_number | text
```
## ---
### `tablesFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/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`](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 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: "postgresql",
tablesFilter: ["users", "posts", "project1_*"],
});
```
### `schemaFilter`
If you want to run multiple projects with one database - check out [our guide](/docs/goodies#multi-project-schema).
`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` |
```ts
export default defineConfig({
dialect: "postgresql",
schemaFilter: ["public", "schema1", "schema2"],
});
```
### `extensionsFilters`
Some extensions like [`postgis`](https://postgis.net/), when installed on the database, create its own tables in public schema.
Those tables have to be ignored by `drizzle-kit push` or `drizzle-kit pull`.
`extensionsFilters` option lets you declare list of installed extensions for drizzle kit to ignore their tables in the schema.
| | |
| :------------ | :----------------- |
| type | `["postgis"]` |
| default | `[]` |
| commands | `push` `pull` |
```ts
export default defineConfig({
dialect: "postgresql",
extensionsFilters: ["postgis"],
});
```
## ---
### `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: "postgresql",
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: "postgresql",
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: "postgresql",
breakpoints: false,
});
```
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-check
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit check`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/drizzle-kit-generate)
`drizzle-kit check` command lets you check consistency of your generated SQL migrations history.
That's extremely useful when you have multiple developers working on the project and
altering database schema on different branches - read more about [migrations for teams](/docs/kit-migrations-for-teams).
`drizzle-kit check` command requires you to specify `dialect`,
you can provide it either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
});
```
```shell
npx drizzle-kit check
```
```shell
npx drizzle-kit check --dialect=postgresql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit check --config=drizzle-dev.config.ts
drizzle-kit check --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Ignore conflicts
In case you need `check` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit check --ignore-conflicts
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit check --dialect=postgresql
drizzle-kit check --dialect=postgresql --out=./migrations-folder
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-export
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit export`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
`drizzle-kit export` lets you export SQL representation of Drizzle schema and print in console SQL DDL representation on it.
Drizzle Kit `export` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Based on the current json snapshot it will generate SQL DDL statements
3. Output SQL DDL statements to console
It's designed to cover [codebase first](/docs/migrations) approach of managing Drizzle migrations.
You can export the SQL representation of the Drizzle schema, allowing external tools like Atlas to handle all the migrations for you
`drizzle-kit export` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit export
```
```shell
npx drizzle-kit export --dialect=postgresql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 export --config=drizzle-dev.config.ts
drizzle-kit export --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of available configurations
`drizzle-kit export` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `--sql` | generating SQL representation of Drizzle Schema |
By default, Drizzle Kit outputs SQL files, but in the future, we want to support different formats
drizzle-kit export --sql=true
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
### Example
Example of how to export drizzle schema to console with Drizzle schema located in `./src/schema.ts`
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ configs
â â đ drizzle.config.ts
â đ src
â â đ schema.ts
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
});
```
```ts filename='schema.ts'
import { pgTable, serial, text } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').notNull(),
name: text('name')
});
```
Now let's run
```shell
npx drizzle-kit export --config=./configs/drizzle.config.ts
```
And it will successfully output SQL representation of drizzle schema
```bash
CREATE TABLE "users" (
"id" serial PRIMARY KEY,
"email" text NOT NULL,
"name" text
);
```
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-generate
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 Prerequisites from "@mdx/Prerequisites.astro"
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx"
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit generate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
`drizzle-kit generate` lets you generate SQL migrations based on your Drizzle schema upon declaration or on subsequent schema changes.
Drizzle Kit `generate` command triggers a sequence of events:
1. It will read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. It will read through your previous migrations folders and compare current json snapshot to the most recent one
3. Based on json differences it will generate SQL migrations
4. Save `migration.sql` and `snapshot.json` in migration folder under current timestamp
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ migration.sql
â â đ snapshot.json
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT UNIQUE
);
```
It's designed to cover [code first](/docs/migrations) approach of managing Drizzle migrations.
You can apply generated migrations using [`drizzle-kit migrate`](/docs/drizzle-kit-migrate), using drizzle-orm's `migrate()`,
using external migration tools like [bytebase](https://www.bytebase.com/) or running migrations yourself directly on the database.
`drizzle-kit generate` command requires you to provide both `dialect` and `schema` path options,
you can set them either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
});
```
```shell
npx drizzle-kit generate
```
```shell
npx drizzle-kit generate --dialect=postgresql --schema=./src/schema.ts
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Custom migration file name
You can set custom migration file names by providing `--name` CLI option
```shell
npx drizzle-kit generate --name=init
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ snapshot.json
â â đ migration.sql
â đ src
â âĻ
```
### Multiple configuration files in one project
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
```
### Custom migrations
You can generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding. Extended docs on custom migrations - [see here](/docs/kit-custom-migrations)
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {5}
đĻ
â đ drizzle
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed/migration.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
### Ignore conflicts
In case you need `generate` command to skip commutativity checks and bypass it, you can use `--ignore-conflicts`. If there is a situation you want to use it, then
there is a big chance that `drizzle-kit` didn't check migrations right and it's a bug. Please report us your case, so we can fix it
```shell
drizzle-kit generate --ignore-conflicts
```
### Extended list of available configurations
`drizzle-kit generate` has a list of cli-only options
| | |
| :-------- | :--------------------------------------------------- |
| `custom` | generate empty SQL for custom migration |
| `name` | generate migration with custom name |
| `ignore-conflicts` | skip commutativity conflict checks, default is `false` |
drizzle-kit generate --name=init
drizzle-kit generate --name=seed_users --custom
drizzle-kit generate --ignore-conflicts
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------ | :------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `driver` | | Driver one of `aws-data-api`, `pglite` |
| `out` | | Migrations output folder, default is `./drizzle` |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `breakpoints` | | SQL statements breakpoints, default is `true` |
### Extended example
Example of how to create a custom postgresql migration file named `0001_seed-users.sql`
with Drizzle schema located in `./src/schema.ts` and migrations folder named `./migrations` instead of default `./drizzle`.
We will also place drizzle config file in the `configs` folder.
Let's create config file:
```plaintext {4}
đĻ
â đ migrations
â đ configs
â â đ drizzle.config.ts
â đ src
â âĻ
```
```ts filename='drizzle.config.ts'
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
out: "./migrations",
});
```
Now let's run
```shell
npx drizzle-kit generate --config=./configs/drizzle.config.ts --name=seed-users --custom
```
And it will successfully generate
```plaintext {6}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â â đ 20242409125510_seed-users
â âĻ
```
```sql
-- ./drizzle/20242409125510_seed-users/migration.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-migrate
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit migrate`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/drizzle-kit-generate)
`drizzle-kit migrate` lets you apply SQL migrations generated by [`drizzle-kit generate`](/docs/drizzle-kit-generate).
It's designed to cover [code first(option 3)](/docs/migrations) approach of managing Drizzle migrations.
Drizzle Kit `migrate` command triggers a sequence of events:
1. Reads through migration folder and read all `.sql` migration files
2. Connects to the database and fetches entries from drizzle migrations log table
3. Based on previously applied migrations it will decide which new migrations to run
4. Runs SQL migrations and logs applied migrations to drizzle migrations table
```plaintext
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ 20242409135510_delicate_professor_xavie
â âĻ
```
```plaintext
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. reads migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
`drizzle-kit migrate` command requires you to specify both `dialect` and database connection credentials,
you can provide them either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit migrate
```
```shell
npx drizzle-kit migrate --dialect=postgresql --url=postgresql://user:password@host:port/dbname
```
### Applied migrations log in the database
Upon running migrations Drizzle Kit will persist records about successfully applied migrations in your database.
It will store them in migrations log table named `__drizzle_migrations` in `drizzle` schema.
You can customise both **table** and **schema** of that table via drizzle config file:
```ts filename="drizzle.config.ts" {8-9}
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
migrations: {
table: 'my-migrations-table', // `__drizzle_migrations` by default
schema: 'public', // `drizzle` by default
},
});
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended example
Let's generate SQL migration and apply it to our database using `drizzle-kit generate` and `drizzle-kit migrate` commands
```plaintext
đĻ
â đ drizzle
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
migrations: {
table: 'journal',
schema: 'drizzle',
},
});
```
```ts
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
})
```
Now let's run
```shell
npx drizzle-kit generate --name=init
```
it will generate
```plaintext {5}
đĻ
â âĻ
â đ migrations
â â đ 20242409125510_init
â âĻ
```
```sql
-- ./drizzle/0000_init.sql
CREATE TABLE "users"(
"id" serial primary key,
"name" text
)
```
Now let's run
```shell
npx drizzle-kit migrate
```
and our SQL migration is now successfully applied to the database â
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-pull
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 Prerequisites from "@mdx/Prerequisites.astro";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import Npm from "@mdx/Npm.astro"
import Npx from "@mdx/Npx.astro"
# `drizzle-kit pull`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file) docs
`drizzle-kit pull` lets you literally pull(introspect) your existing database schema and generate `schema.ts` drizzle schema file,
it is designed to cover [database first](/docs/migrations) approach of Drizzle migrations.
When you run Drizzle Kit `pull` command it will:
1. Pull database schema(DDL) from your existing database
2. Generate `schema.ts` drizzle schema file and save it to `out` folder
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââ
â â <--- CREATE TABLE "users" (
ââââââââââââââââââââââââââââ â â "id" SERIAL PRIMARY KEY,
â ~ drizzle-kit pull â â â "name" TEXT,
âââŦâââââââââââââââââââââââââ â DATABASE â "email" TEXT UNIQUE
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
It is a great approach if you need to manage database schema outside of your TypeScript project or
you're using database, which is managed by somebody else.
`drizzle-kit pull` requires you to specify `dialect` and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit pull
```
```shell
npx drizzle-kit pull --dialect=postgresql --url=postgresql://user:password@host:port/dbname
```
### Multiple configuration files in one project
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 pull --config=drizzle-dev.config.ts
drizzle-kit pull --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Specifying database driver
Drizzle Kit does not come with a pre-bundled database driver,
it will automatically pick available database driver from your current project based on the `dialect` - [see discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2203).
Mostly all drivers of the same dialect share the same set of connection params,
as for exceptions like `aws-data-api`, `pglite` and `d1-http` - you will have to explicitely specify `driver` param.
```ts {6}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
driver: "aws-data-api",
dbCredentials: {
database: "database",
resourceArn: "resourceArn",
secretArn: "secretArn",
},
});
```
```ts {6}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
driver: "pglite",
dbCredentials: {
// inmemory
url: ":memory:"
// or database folder
url: "./database/"
},
});
```
```ts {6}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
driver: "d1-http",
dbCredentials: {
accountId: "accountId",
databaseId: "databaseId",
token: "token",
},
});
```
### Initial pull
You can use the `--init` flag to mark the pulled schema as an applied migration in your database,
so that all subsequent migrations are diffed against the initial one
```shell
npx drizzle-kit pull --init
```
### Including tables, schemas and extensions
`drizzle-kit pull` will by default manage all tables in all schemas.
You can configure list of tables, schemas and extensions via `tablesFilters`, `schemaFilter` and `extensionFilters` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | `glob` based schema names filter, e.g. `["public", "drizzle"]` or `"drizzle*"`. Default is `"*"` |
| `extensionsFilters` | List of installed database extensions, e.g. `["postgis"]`. Default is `[]` |
Let's configure drizzle-kit to only operate with **all tables** in **public** schema
and let drizzle-kit know that there's a **postgis** extension installed,
which creates it's own tables in public schema, so drizzle can ignore them.
```ts filename="drizzle.config.ts" {9-11}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
extensionsFilters: ["postgis"],
schemaFilter: ["public"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit pull
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `driver` | | Drivers exceptions |
| `out` | | Migrations output folder path, default is `./drizzle` |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default is `drizzle.config.ts` |
| `introspect-casing` | | Strategy for JS keys creation in columns, tables, etc. `preserve` `camel` |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
| `extensionsFilters` | | Database extensions internal database filters |
drizzle-kit pull --dialect=postgresql --url=postgresql://user:password@host:port/dbname
drizzle-kit pull --dialect=postgresql --driver=pglite url=database/
drizzle-kit pull --dialect=postgresql --tablesFilter='user*' --extensionsFilters=postgis url=postgresql://user:password@host:port/dbname
{/* TODO update gif */}

Source: https://orm.drizzle.team/docs/pg/drizzle-kit-push
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import SchemaFilePaths from "@mdx/SchemaFilePaths.mdx";
import Drivers from "@mdx/Drivers.mdx"
import Dialects from "@mdx/Dialects.mdx"
import DriversExamples from "@mdx/DriversExamples.mdx"
# `drizzle-kit push`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file) docs
`drizzle-kit push` lets you literally push your schema and subsequent schema changes directly to the
database while omitting SQL files generation, it's designed to cover [code first](/docs/migrations)
approach of Drizzle migrations.
When you run Drizzle Kit `push` command it will:
1. Read through your Drizzle schema file(s) and compose a json snapshot of your schema
2. Pull(introspect) database schema
3. Based on differences between those two it will generate SQL migrations
4. Apply SQL migrations to the database
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
});
```
```
âââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´âââââââââââââââââ
create table users(id serial primary key, name text);
```
It's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
It pairs exceptionally well with blue/green deployment strategy and serverless databases like
[Planetscale](https://planetscale.com), [Neon](https://neon.tech), [Turso](https://turso.tech/drizzle) and others.
`drizzle-kit push` requires you to specify `dialect`, path to the `schema` file(s) and either
database connection `url` or `user:password@host:port/db` params, you can provide them
either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
});
```
```shell
npx drizzle-kit push
```
```shell
npx drizzle-kit push --dialect=postgresql --schema=./src/schema.ts --url=postgresql://user:password@host:port/dbname
```
### Schema files path
You can have a single `schema.ts` file or as many schema files as you want spread out across the project.
Drizzle Kit requires you to specify path(s) to them as a [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) via `schema` configuration option.
### Multiple configuration files in one project
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 push --config=drizzle-dev.config.ts
drizzle-kit push --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Specifying database driver
Drizzle Kit does not come with a pre-bundled database driver,
it will automatically pick an available database driver from your current project based on the `dialect` - [see discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2203).
Mostly all drivers of the same dialect share the same set of connection params,
as for exceptions like `aws-data-api`, `pglite` and `d1-http` - you will have to explicitly specify `driver` param.
### Including tables, schemas and extensions
`drizzle-kit push` will manage all tables and schemas by default.
You can configure list of tables, schemas and extensions via `tablesFilter`, `schemaFilter` and `extensionFilters` options.
| | |
| :------------------ | :-------------------------------------------------------------------------------------------- |
| `tablesFilter` | `glob` based table names filter, e.g. `["users", "user_info"]` or `"user*"`. Default is `"*"` |
| `schemaFilter` | Schema names filter, e.g. `["public", "drizzle"]` |
| `extensionsFilters` | List of installed database extensions, e.g. `["postgis"]`. Default is `[]` |
Let's configure drizzle-kit to only operate with **all tables** in **public** schema
and let drizzle-kit know that there's a **postgis** extension installed,
which creates it's own tables in public schema, so drizzle can ignore them.
```ts filename="drizzle.config.ts" {9-11}
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname",
},
extensionsFilters: ["postgis"],
schemaFilter: ["public"],
tablesFilter: ["*"],
});
```
```shell
npx drizzle-kit push
```
### Extended list of configurations
`drizzle-kit push` has a list of cli-only options
| | |
| :-------- | :---------------------------------------------------------------|
| `verbose` | print all SQL statements prior to execution |
| `explain` | print the planned SQL changes, without applying them (dry run) |
| `force` | auto-accept all data-loss statements |
drizzle-kit push --explain --verbose --force
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :------------------ | :--------- | :------------------------------------------------------------------------ |
| `dialect` | `required` | Database dialect, one of |
| `schema` | `required` | Path to typescript schema file(s) or folder(s) with multiple schema files |
| `driver` | | Drivers exceptions, for example `aws-data-api`, `pglite` |
| `tablesFilter` | | Table name filter |
| `schemaFilter` | | Schema name filter. Default: `["*"]` |
| `extensionsFilters` | | Database extensions internal database filters |
| `url` | | Database connection string |
| `user` | | Database user |
| `password` | | Database password |
| `host` | | Host |
| `port` | | Port |
| `database` | | Database name |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit push dialect=postgresql schema=src/schema.ts url=postgresql://user:password@host:port/dbname
drizzle-kit push dialect=postgresql schema=src/schema.ts driver=pglite url=database/
drizzle-kit push dialect=postgresql schema=src/schema.ts --tablesFilter='user*' --extensionsFilters=postgis url=postgresql://user:password@host:port/dbname
### Extended example
Let's declare drizzle schema in the project and push it to the database via `drizzle-kit push` command
```plaintext
đĻ
â đ src
â â đ schema.ts
â â đ index.ts
â đ drizzle.config.ts
â âĻ
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```ts
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
})
```
Now let's run
```shell
npx drizzle-kit push
```
it will pull existing(empty) schema from the database and generate SQL migration and apply it under the hood
```sql
CREATE TABLE "users"(
id serial primary key,
name text
)
```
DONE â
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-studio
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npm from "@mdx/Npm.astro";
import Npx from "@mdx/Npx.astro";
# `drizzle-kit studio`
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
- Drizzle Studio, our database browser - [read here](/drizzle-studio/overview)
`drizzle-kit studio` command spins up a server for [Drizzle Studio](/drizzle-studio/overview) hosted on [local.drizzle.studio](https://local.drizzle.studio).
It requires you to specify database connection credentials via [drizzle.config.ts](/docs/drizzle-config-file) config file.
By default it will start a Drizzle Studio server on `127.0.0.1:4983`
```ts {6}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
url: "postgresql://user:password@host:port/dbname"
},
});
```
```shell
npx drizzle-kit studio
```
### Configuring `host` and `port`
By default Drizzle Studio server starts on `127.0.0.1:4983`,
you can config `host` and `port` via CLI options
drizzle-kit studio --port=3000
drizzle-kit studio --host=0.0.0.0
drizzle-kit studio --host=0.0.0.0 --port=3000
### Logging
You can enable logging of every SQL statement by providing `verbose` flag
drizzle-kit studio --verbose
### Safari and Brave support
Safari and Brave block access to localhost by default.
You need to install [mkcert](https://github.com/FiloSottile/mkcert) and generate self-signed certificate:
1. Follow the mkcert [installation steps](https://github.com/FiloSottile/mkcert#installation)
2. Run `mkcert -install`
3. Restart your `drizzle-kit studio`
### Embeddable version of Drizzle Studio
While hosted version of Drizzle Studio for local development is free forever and meant to just enrich Drizzle ecosystem,
we have a B2B offering of an embeddable version of Drizzle Studio for businesses.
**Drizzle Studio component** - is a pre-bundled framework agnostic web component of Drizzle Studio
which you can embed into your UI `React` `Vue` `Svelte` `VanillaJS` etc.
That is an extremely powerful UI element that can elevate your offering
if you provide Database as a SaaS or a data centric SaaS solutions based
on SQL or for private non-customer facing in-house usage.
Database platforms using Drizzle Studio:
- [Turso](https://turso.tech), our first customers since Oct 2023!
- [Neon](https://neon.tech), [launch post](https://neon.tech/docs/changelog/2024-05-24)
- [SQLite Cloud](https://sqlitecloud.io/), [launch post](https://blog.sqlitecloud.io/release-notes-introducing-database-studio-in-sqlite-cloud)
AI powered platforms for building apps and websites:
- [Replit](https://repl.it), [launch post](https://blog.replit.com/database-editor)
- [Deno](https://deno.com/deploy), [launch post](https://x.com/rough__sea/status/1950231545807327329)
- [Kinsta](https://kinsta.com/)
- [Create.xyz](https://create.xyz), [launch post](https://x.com/create_xyz/status/1889479526499098830)
- [Sevalla](https://sevalla.com/)
- [Orchids](https://www.orchids.app/)
- [Netlify](https://www.netlify.com/)
- [QwikBuild](https://qwikbuild.com/)
- [Specific](https://specific.dev/)
Startups using Drizzle Studio:
- [Sample Health Care](https://samplehc.com/)
Platforms that used before:
- [Nuxt Hub](https://hub.nuxt.com/), SÊbastien Chopin's [launch post](https://x.com/Atinux/status/1768663789832929520)
- [Deco.cx](https://deco.cx/)
- [Hydra](https://www.hydra.so/)
- [Runable](https://runable.com/)
- [Tembo](https://www.tembo.io/)
- [Squadbase](https://www.squadbase.dev/)
We also have a set of companies using embeddable Drizzle Studio components for internal use cases
You can read a detailed overview [here](https://www.npmjs.com/package/@drizzle-team/studio) and
if you're interested - hit us in DMs on [Twitter](https://x.com/drizzleorm) or in [Discord #drizzle-studio](https://driz.link/discord) channel.
### Drizzle Studio chrome extension
Drizzle Studio [chrome extension](https://chromewebstore.google.com/detail/drizzle-studio/mjkojjodijpaneehkgmeckeljgkimnmd)
lets you browse your [PlanetScale](https://planetscale.com),
[Cloudflare](https://developers.cloudflare.com/d1/) and [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres)
serverless databases directly in their vendor admin panels!
### Limitations
Our hosted version Drizzle Studio is meant to be used for local development and not meant to be used on remote (VPS, etc).
If you want to deploy Drizzle Studio to your VPS - we have an alpha version of [Drizzle Studio Gateway](https://gateway.drizzle.team).
### Is it open source?
No. Drizzle ORM and Drizzle Kit are fully open sourced, while Studio is not.
Drizzle Studio for local development is free to use forever to enrich Drizzle ecosystem,
open sourcing one would've break our ability to provide B2B offerings and monetise it, unfortunately.
Source: https://orm.drizzle.team/docs/pg/drizzle-kit-up
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 Prerequisites from "@mdx/Prerequisites.astro";
import Npx from "@mdx/Npx.astro";
import Dialects from "@mdx/Dialects.mdx"
# `drizzle-kit up`
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/drizzle-kit-generate)
`drizzle-kit up` command lets you upgrade drizzle schema snapshots to a newer version.
It's required whenever we introduce breaking changes to the json snapshots of the schema and upgrade the internal version.
`drizzle-kit up` command requires you to specify `dialect` param,
you can provide it either via [drizzle.config.ts](/docs/drizzle-config-file) config file or via CLI options
```ts {5,8}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
});
```
```shell
npx drizzle-kit up
```
```shell
npx drizzle-kit up --dialect=postgresql
```
### Multiple configuration files in one project
You can have multiple config files in the project, it's very useful when you have multiple database stages or multiple databases on the same project:
drizzle-kit migrate --config=drizzle-dev.config.ts
drizzle-kit migrate --config=drizzle-prod.config.ts
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
### Extended list of configurations
We recommend configuring `drizzle-kit` through [drizzle.config.ts](/docs/drizzle-config-file) file,
yet you can provide all configuration options through CLI if necessary, e.g. in CI/CD pipelines, etc.
| | | |
| :-------- | :--------- | :---------------------------------------------------------------------- |
| `dialect` | `required` | Database dialect you are using. Can be one of |
| `out` | | Migrations folder, default=`./drizzle` |
| `config` | | Configuration file path, default=`drizzle.config.ts` |
drizzle-kit up --dialect=postgresql
drizzle-kit up --dialect=postgresql --out=./migrations-folder
{/* TODO: update up gif */}

Source: https://orm.drizzle.team/docs/pg/dynamic-query-building
import Callout from '@mdx/Callout.astro';
# Dynamic query building
By default, as all the query builders in Drizzle try to conform to SQL as much as possible, you can only invoke most of the methods once.
For example, in a `SELECT` statement there might only be one `WHERE` clause, so you can only invoke `.where()` once:
```ts
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // â Type error - where() can only be invoked once
```
In the previous ORM versions, when such restrictions weren't implemented, this example in particular was a source of confusion for many users, as they expected the query builder to "merge" multiple `.where()` calls into a single condition.
This behavior is useful for conventional query building, i.e. when you create the whole query at once.
However, it becomes a problem when you want to build a query dynamically, i.e. if you have a shared function that takes a query builder and enhances it.
To solve this problem, Drizzle provides a special 'dynamic' mode for query builders, which removes the restriction of invoking methods only once.
To enable it, you need to call `.$dynamic()` on a query builder.
Let's see how it works by implementing a simple `withPagination` function that adds `LIMIT` and `OFFSET` clauses to a query based on the provided page number and an optional page size:
```ts
function withPagination(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // â Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // â
OK
```
Note that the `withPagination` function is generic, which allows you to modify the result type of the query builder inside it, for example by adding a join:
```ts
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
This is possible, because `PgSelect` and other similar types are specifically designed to be used in dynamic query building. They can only be used in dynamic mode.
Here is the list of all types that can be used as generic parameters in dynamic query building:
| Query | Type |
|:--------|:-----------------------------------|
| Select | `PgSelect` / `PgSelectQueryBuilder` |
| Insert | `PgInsert` |
| Update | `PgUpdate` |
| Delete | `PgDelete` |
The `...QueryBuilder` types are for usage with [standalone query builder
instances](/docs/goodies#standalone-query-builder). DB query builders are
subclasses of them, so you can use them as well.
```ts
import { QueryBuilder } from 'drizzle-orm/pg-core';
function withFriends(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
```
Source: https://orm.drizzle.team/docs/pg/effect-schema
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
# effect-schema
Allows you to generate [effect](https://effect.website/) schemas from Drizzle ORM schemas.
**Features**
- Create a select schema for tables, views and enums.
- Create insert and update schemas for tables.
#### Usage
```ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/effect-schema';
import { Effect, Schema } from "effect";
const users = pgTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
email: text().notNull(),
role: text({ enum: ['admin', 'user'] }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// Schema for inserting a user - can be used to validate API requests
const UserInsert = createInsertSchema(users);
// Schema for updating a user - can be used to validate API requests
const UserUpdate = createUpdateSchema(users);
// Schema for selecting a user - can be used to validate API responses
const UserSelect = createSelectSchema(users);
// Overriding the fields
const UserInsert = createInsertSchema(users, {
role: Schema.String,
});
// Refining the fields - useful if you want to change the fields before they become nullable/optional in the final schema
const UserInsert = createInsertSchema(users, {
id: (schema) => schema.check(Schema.isGreaterThanOrEqualTo(0)),
role: Schema.String,
});
// Usage
const program = Effect.gen(function*() {
const parsedUser = yield* Schema.decodeUnknownEffect(UserInsert)({
name: 'John Doe',
email: 'johndoe@test.com',
role: 'admin',
});
});
```
Source: https://orm.drizzle.team/docs/pg/extensions
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
### `pg_vector`
There is no specific code to create an extension inside the Drizzle schema. We assume that if you are using vector types,
indexes, and queries, you have a PostgreSQL database with the pg_vector extension installed.
[`pg_vector`](https://github.com/pgvector/pgvector) is open-source vector similarity search for Postgres
Store your vectors with the rest of your data. Supports:
- exact and approximate nearest neighbor search
- single-precision, half-precision, binary, and sparse vectors
- L2 distance, inner product, cosine distance, L1 distance, Hamming distance, and Jaccard distance
#### Column Types
**`vector`**
Store your vectors with the rest of your data
For more info please refer to the official pg_vector docs **[docs.](https://github.com/pgvector/pgvector)**
```ts
const table = pgTable('table', {
embedding: vector({ dimensions: 3 }),
embedding2: vector({ dimensions: 3 }).default([0, -2, 3])
})
```
```sql
CREATE TABLE "table" (
"embedding" vector(3),
"embedding2" vector(3) DEFAULT '[0,-2,3]'
);
```
**`halfvec`**
Half-precision vector type, stores dense vectors of half-precision float values
For more info please refer to the official pg_vector docs **[docs.](https://github.com/pgvector/pgvector)**
```ts
export const table = pgTable('table', {
halfvec: halfvec({ dimensions: 3 }),
halfvec2: halfvec({ dimensions: 3 }).default([0, -2, 3])
})
```
```sql
CREATE TABLE "table" (
"halfvec" halfvec(3),
"halfvec2" halfvec(3) DEFAULT '[0,-2,3]'
);
```
**`sparsevec`**
Sparse vector type, stores vectors with mostly zero values efficiently
For more info please refer to the official pg_vector docs **[docs.](https://github.com/pgvector/pgvector)**
```ts
export const table = pgTable('table', {
sparsevec: sparsevec({ dimensions: 3 }),
sparsevec2: sparsevec({ dimensions: 5 }).default(`{1:-1,3:2,5:3}/5`)
})
```
```sql
CREATE TABLE "table" (
"sparsevec" sparsevec(3),
"sparsevec2" sparsevec(5) DEFAULT '{1:-1,3:2,5:3}/5'
);
```
**`bit`**
Bit string type, used for binary vectors in pgvector
For more info please refer to the official pg_vector **[docs.](https://github.com/pgvector/pgvector)**
```ts
export const table = pgTable('table', {
bit: bit({ dimensions: 5 }),
bit2: bit({ dimensions: 3 }).default(`101`)
})
```
```sql
CREATE TABLE "table" (
"bit" bit(5),
"bit2" bit(3) DEFAULT '101'
);
```
#### Indexes
You can now specify indexes for `pg_vector` and utilize `pg_vector` functions for querying, ordering, etc.
Let's take a few examples of `pg_vector` indexes from the `pg_vector` docs and translate them to Drizzle
#### L2 distance, Inner product and Cosine distance for `vector`
```ts
// CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
// CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);
// CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
export const table = pgTable('items', {
embedding: vector({ dimensions: 3 })
}, (table) => [
index('l2_index').using('hnsw', table.embedding.op('vector_l2_ops')),
index('ip_index').using('hnsw', table.embedding.op('vector_ip_ops')),
index('cosine_index').using('hnsw', table.embedding.op('vector_cosine_ops'))
])
```
#### L2 distance, Inner product and Cosine distance for `halfvec`
```ts
// CREATE INDEX ON items USING hnsw (embedding halfvec_l2_ops);
// CREATE INDEX ON items USING hnsw (embedding halfvec_ip_ops);
// CREATE INDEX ON items USING hnsw (embedding halfvec_cosine_ops);
export const table = pgTable('items', {
embedding: halfvec({ dimensions: 3 })
}, (table) => [
index('l2_index').using('hnsw', table.embedding.op('halfvec_l2_ops')),
index('ip_index').using('hnsw', table.embedding.op('halfvec_ip_ops')),
index('cosine_index').using('hnsw', table.embedding.op('halfvec_cosine_ops')),
])
```
#### L2 distance, Inner product and Cosine distance for `sparsevec`
```ts
// CREATE INDEX ON items USING hnsw (embedding sparsevec_l2_ops);
// CREATE INDEX ON items USING hnsw (embedding sparsevec_ip_ops);
// CREATE INDEX ON items USING hnsw (embedding sparsevec_cosine_ops);
export const table = pgTable('items', {
embedding: sparsevec({ dimensions: 3 })
}, (table) => [
index('l2_index').using('hnsw', table.embedding.op('sparsevec_l2_ops')),
index('ip_index').using('hnsw', table.embedding.op('sparsevec_ip_ops')),
index('cosine_index').using('hnsw', table.embedding.op('sparsevec_cosine_ops')),
])
```
#### Hamming distance and Jaccard distance for `bit`
```ts
// CREATE INDEX "hamming_index" ON "items" USING hnsw ("embedding" bit_hamming_ops);
// CREATE INDEX "bit_jaccard_index" ON "items" USING hnsw ("embedding" bit_jaccard_ops);
export const table = pgTable("items", {
embedding: bit({ dimensions: 3 }),
}, (table) => [
index("hamming_index").using("hnsw", table.embedding.op("bit_hamming_ops")),
index("bit_jaccard_index").using("hnsw", table.embedding.op("bit_jaccard_ops"))
]);
```
#### Helper Functions
For queries, you can use predefined functions for vectors or create custom ones using the SQL template operator.
You can also use the following helpers:
```ts
import { l2Distance, l1Distance, innerProduct,
cosineDistance, hammingDistance, jaccardDistance } from 'drizzle-orm'
l2Distance(table.column, [3, 1, 2]) // table.column <-> '[3, 1, 2]'
l1Distance(table.column, [3, 1, 2]) // table.column <+> '[3, 1, 2]'
innerProduct(table.column, [3, 1, 2]) // table.column <#> '[3, 1, 2]'
cosineDistance(table.column, [3, 1, 2]) // table.column <=> '[3, 1, 2]'
hammingDistance(table.column, '101') // table.column <~> '101'
jaccardDistance(table.column, '101') // table.column <%> '101'
```
If `pg_vector` has some other functions to use, you can replicate implementation from existing one we have. Here is how it can be done
```ts
export function l2Distance(
column: SQLWrapper | AnyColumn,
value: number[] | string[] | TypedQueryBuilder | string,
): SQL {
if (is(value, TypedQueryBuilder) || typeof value === 'string') {
return sql`${column} <-> ${value}`;
}
return sql`${column} <-> ${JSON.stringify(value)}`;
}
```
Name it as you wish and change the operator. This example allows for a numbers array, strings array, string, or even a select query. Feel free to create any other type you want or even contribute and submit a PR
#### Examples
Let's take a few examples of `pg_vector` queries from the `pg_vector` docs and translate them to Drizzle
```ts
import { l2Distance } from 'drizzle-orm';
// SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
db.select().from(items).orderBy(l2Distance(items.embedding, [3,1,2]))
// SELECT embedding <-> '[3,1,2]' AS distance FROM items;
db.select({ distance: l2Distance(items.embedding, [3,1,2]) })
// SELECT * FROM items ORDER BY embedding <-> (SELECT embedding FROM items WHERE id = 1) LIMIT 5;
const subquery = db.select({ embedding: items.embedding }).from(items).where(eq(items.id, 1));
db.select().from(items).orderBy(l2Distance(items.embedding, subquery)).limit(5)
// SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;
db.select({ innerProduct: sql`(${innerProduct(items.embedding, [3,1,2])}) * -1` }).from(items)
// and more!
```
### `postgis`
There is no specific code to create an extension inside the Drizzle schema. We assume that if you are using postgis types, indexes, and queries, you have a PostgreSQL database with the `postgis` extension installed.
As [PostGIS](https://postgis.net/) website mentions:
> PostGIS extends the capabilities of the PostgreSQL relational database by adding support for storing, indexing, and querying geospatial data.
If you are using the `introspect` or `push` commands with the PostGIS extension and don't want PostGIS tables to be included, you can use [extensionsFilters](/docs/drizzle-config-file#extensionsfilters) to ignore all the PostGIS tables
#### Column Types
**`geometry`**
Store your geometry data with the rest of your data
For more info please refer to the official PostGIS docs **[docs.](https://postgis.net/workshops/postgis-intro/geometries.html)**
```ts
const items = pgTable('items', {
geo: geometry('geo', { type: 'point' }),
geoObj: geometry('geo_obj', { type: 'point', mode: 'xy' }),
geoSrid: geometry('geo_options', { type: 'point', mode: 'xy', srid: 4000 }),
});
```
**mode**
Type `geometry` has 2 modes for mappings from the database: `tuple` and `xy`.
- `tuple` will be accepted for insert and mapped on select to a tuple. So, the database geometry will be typed as [1,2] with drizzle.
- `xy` will be accepted for insert and mapped on select to an object with x, y coordinates. So, the database geometry will be typed as `{ x: 1, y: 2 }` with drizzle
**type**
The current release has a predefined type: `point`, which is the `geometry(Point)` type in the PostgreSQL PostGIS extension. You can specify any string there if you want to use some other type
#### Indexes
With the available Drizzle indexes API, you should be able to write any indexes for PostGIS
**Examples**
```ts
// CREATE INDEX custom_idx ON table USING GIST (geom);
const table = pgTable('table', {
geo: geometry({ type: 'point' }),
}, (table) => [
index('custom_idx').using('gist', table.geo)
])
```
Source: https://orm.drizzle.team/docs/pg/generated-columns
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Callout from '@mdx/Callout.astro';
# Generated Columns
Stored (or persistent) Generated Columns: These columns are computed when a row is inserted or updated and their values are stored in the database. This allows them to be indexed and can improve query performance since the values do not need to be recomputed for each query.
#### Database side
**Types**: `STORED` only
**How It Works**
- Automatically computes values based on other columns during insert or update.
**Capabilities**
- Simplifies data access by precomputing complex expressions.
- Enhances query performance with index support on generated columns.
**Limitations**
- Cannot specify default values.
- Expressions cannot reference other generated columns or include subqueries.
- Schema changes required to modify generated column expressions.
- Cannot directly use in primary keys, foreign keys, or unique constraints
For more info, please check [PostgreSQL](https://www.postgresql.org/docs/current/ddl-generated-columns.html) docs
#### Drizzle side
In Drizzle you can specify `.generatedAlwaysAs()` function on any column type and add a supported sql query,
that will generate this column data for you.
#### Features
This function can accept generated expression in 2 ways:
**`sql`** tag - if you want drizzle to escape some values for you
```ts
export const test = pgTable("test", {
generatedName: text("gen_name").generatedAlwaysAs(sql`'hello "world"!'`),
});
```
```sql
CREATE TABLE "test" (
"gen_name" text GENERATED ALWAYS AS ('hello "world"!') STORED
);
```
**`callback`** - if you need to reference columns from a table
```ts
export const test = pgTable("test", {
name: text("first_name"),
generatedName: text("gen_name").generatedAlwaysAs(
(): SQL => sql`'hi, ' || ${test.name} || '!'`
),
});
```
```sql
CREATE TABLE "test" (
"first_name" text,
"gen_name" text GENERATED ALWAYS AS ('hi, ' || "test"."first_name" || '!') STORED
);
```
**Example** generated columns with full-text search
```typescript copy {17-19}
import { SQL, sql } from "drizzle-orm";
import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core";
const tsVector = customType<{ data: string }>({
dataType() {
return "tsvector";
},
});
export const test = pgTable(
"test",
{
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
content: text("content"),
contentSearch: tsVector("content_search", {
dimensions: 3,
}).generatedAlwaysAs(
(): SQL => sql`to_tsvector('english', ${test.content})`
),
},
(t) => [
index("idx_content_search").using("gin", t.contentSearch)
]
);
```
```sql {4}
CREATE TABLE "test" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "test_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"content" text,
"content_search" tsvector GENERATED ALWAYS AS (to_tsvector('english', "test"."content")) STORED
);
CREATE INDEX "idx_content_search" ON "test" USING gin ("content_search");
```
Source: https://orm.drizzle.team/docs/pg/get-started-postgresql
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
import AnchorCards from '@mdx/AnchorCards.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTabs from "@mdx/CodeTabs.astro";
import WhatsNextPostgres from "@mdx/WhatsNextPostgres.astro";
# Drizzle \<\> PostgreSQL
- Database [connection basics](/docs/connect-overview) with Drizzle
- node-postgres [basics](https://node-postgres.com/)
- postgres.js [basics](https://github.com/porsager/postgres?tab=readme-ov-file#usage)
Drizzle has native support for PostgreSQL connections with the `node-postgres` and `postgres.js` drivers.
There are a few differences between the `node-postgres` and `postgres.js` drivers that we discovered while using both and integrating them with the Drizzle ORM. For example:
- With `node-postgres`, you can install `pg-native` to boost the speed of both `node-postgres` and Drizzle by approximately 10%.
- `node-postgres` supports providing type parsers on a per-query basis without globally patching things. For more details, see [Types Docs](https://node-postgres.com/features/queries#types).
- `postgres.js` uses prepared statements by default, which you may need to opt out of. This could be a potential issue in AWS environments, among others, so please keep that in mind.
- If there's anything else you'd like to contribute, we'd be happy to receive your PRs [here](https://github.com/drizzle-team/drizzle-orm-docs/pulls)
## node-postgres
#### Step 1 - Install packages
drizzle-orm@rc pg
-D drizzle-kit@rc @types/pg
#### Step 2 - Initialize the driver and make a query
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from 'drizzle-orm/node-postgres';
// You can specify any property from the node-postgres connection options
const db = drizzle({
connection: {
connectionString: process.env.DATABASE_URL,
ssl: true
}
});
const result = await db.execute('select 1');
```
If you need to provide your existing driver:
```typescript copy
// Make sure to install the 'pg' package
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle({ client: pool });
const result = await db.execute('select 1');
```
## postgres.js
#### Step 1 - Install packages
drizzle-orm@rc postgres
-D drizzle-kit@rc
#### Step 2 - Initialize the driver and make a query
```typescript copy
import { drizzle } from 'drizzle-orm/postgres-js';
const db = drizzle(process.env.DATABASE_URL);
const result = await db.execute('select 1');
```
```typescript copy
import { drizzle } from 'drizzle-orm/postgres-js';
// You can specify any property from the postgres-js connection options
const db = drizzle({
connection: {
url: process.env.DATABASE_URL,
ssl: true
}
});
const result = await db.execute('select 1');
```
If you need to provide your existing driver:
```typescript copy
// Make sure to install the 'postgres' package
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const queryClient = postgres(process.env.DATABASE_URL);
const db = drizzle({ client: queryClient });
const result = await db.execute('select 1');
```
#### What's next?
Source: https://orm.drizzle.team/docs/pg/goodies
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
## Type API
Use Drizzle's type helpers to infer `select` and `insert` models from a PostgreSQL table schema.
```ts
import { serial, text, pgTable } from 'drizzle-orm/pg-core';
import { type InferSelectModel, type InferInsertModel } from 'drizzle-orm';
const users = pgTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
});
type SelectUser = typeof users.$inferSelect;
type InsertUser = typeof users.$inferInsert;
type SelectUserAlt = InferSelectModel;
type InsertUserAlt = InferInsertModel;
```
## Logging
Enable default query logging by passing `{ logger: true }` to `drizzle`.
```ts
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DB_URL, { logger: true });
```
You can change the logs destination by creating a `DefaultLogger` instance and providing a custom `writer` to it:
```typescript copy
import { DefaultLogger, type LogWriter } from "drizzle-orm/logger";
import { drizzle } from "drizzle-orm/...";
class MyLogWriter implements LogWriter {
write(message: string) {
// Write to file, stdout, etc.
}
}
const logger = new DefaultLogger({ writer: new MyLogWriter() });
const db = drizzle(process.env.DB_URL, { logger });
```
You can also provide a custom logger.
```ts
import type { Logger } from 'drizzle-orm/logger';
import { drizzle } from "drizzle-orm/...";
class MyLogger implements Logger {
logQuery(query: string, params: unknown[]): void {
console.log({ query, params });
}
}
const db = drizzle(process.env.DB_URL, { logger: new MyLogger() });
```
## Multi-project schema
Use `pgTableCreator` to customize table names when several projects share one database.
```ts
import { serial, text, pgTableCreator } from 'drizzle-orm/pg-core';
const pgTable = pgTableCreator((name) => `project1_${name}`);
export const users = pgTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
});
```
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema/*',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
tablesFilter: ['project1_*'],
});
```
```sql
CREATE TABLE "project1_users" (
"id" serial PRIMARY KEY,
"name" text NOT NULL
);
```
## Printing SQL Query
Print generated SQL from a query with `.toSQL()`.
```ts
const query = db
.select({ id: users.id, name: users.name })
.from(users)
.groupBy(users.id)
.toSQL();
```
## Raw SQL Queries
Use `db.execute` for raw parametrized SQL.
```ts
import { sql } from 'drizzle-orm';
const statement = sql`select * from ${users} where ${users.id} = ${userId}`;
const result = await db.execute(statement);
```
## Standalone Query Builder
Use the PostgreSQL query builder without creating a database instance.
```ts
import { QueryBuilder } from 'drizzle-orm/pg-core';
const qb = new QueryBuilder();
const query = qb.select().from(users).where(eq(users.name, 'Dan'));
const { sql, params } = query.toSQL();
```
## Get Typed Columns
Use `getColumns` to get a typed column map, which is useful when excluding fields from a selection.
```ts
import { getColumns } from 'drizzle-orm';
import { users } from './schema';
const { password, role, ...rest } = getColumns(users);
await db.select({ ...rest }).from(users);
```
```ts
import { serial, text, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial().primaryKey(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
});
```
## Get Table Information
Use `getTableConfig` to inspect PostgreSQL table metadata.
```ts
import { getTableConfig, pgTable } from 'drizzle-orm/pg-core';
export const table = pgTable(...);
const { columns, indexes, foreignKeys, checks, primaryKeys, name, schema } =
getTableConfig(table);
```
## Compare Object Types
Use `is()` instead of `instanceof` to check Drizzle object types.
```ts
import { Column, is } from 'drizzle-orm';
if (is(value, Column)) {
// value is narrowed to Column
}
```
## Mock Driver
Use `drizzle.mock()` when you need a typed database object without a real PostgreSQL connection.
```ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { relations } from './relations';
const db = drizzle.mock({ relations });
```
Source: https://orm.drizzle.team/docs/pg/indexes-constraints
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
# Indexes & Constraints
## Constraints
SQL constraints are the rules enforced on table columns. They are used to prevent invalid data from being entered into the database.
This ensures the accuracy and reliability of your data in the database.
### Default
The `DEFAULT` clause specifies a default value to use for the column if no value 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 { integer, uuid, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
integer1: integer().default(42),
integer2: integer().default(sql`24`),
uuid1: uuid().defaultRandom(),
uuid2: uuid().default(sql`gen_random_uuid()`),
});
```
```sql
CREATE TABLE "table" (
"integer1" integer DEFAULT 42,
"integer2" integer DEFAULT 24,
"uuid1" uuid DEFAULT gen_random_uuid(),
"uuid2" uuid DEFAULT gen_random_uuid()
);
```
### Not null
By default, a column can hold **NULL** values. The `NOT NULL` constraint enforces a column to **NOT** accept **NULL** values.
This enforces a field to always contain a value, which means that you cannot insert a new record,
or update a record without adding a value to this field.
```typescript copy
import { integer, pgTable } from "drizzle-orm/pg-core";
export const table = pgTable('table', {
integer: integer().notNull(),
});
```
```sql
CREATE TABLE "table" (
"integer" integer NOT NULL
);
```
### Unique
The `UNIQUE` constraint ensures that all values in a column are different.
Both the `UNIQUE` and `PRIMARY KEY` constraints provide a guarantee for uniqueness for a column or set of columns.
A `PRIMARY KEY` constraint automatically has a `UNIQUE` constraint.
You can have many `UNIQUE` constraints per table, but only one `PRIMARY KEY` constraint per table.
```typescript copy
import { integer, text, unique, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable('user', {
id: integer().unique(),
});
export const table = pgTable('table', {
id: integer().unique('custom_name'),
});
export const composite = pgTable('composite_example', {
id: integer(),
name: text(),
}, (t) => [
unique().on(t.id, t.name),
unique('custom_name').on(t.id, t.name)
]);
// In Postgres 15.0+ NULLS NOT DISTINCT is available
// This example demonstrates both available usages
export const userNulls = pgTable("user_nulls_example", {
id: integer(),
id2: integer().unique("custom_name", { nulls: "not distinct" }),
}, (t) => [
unique().on(t.id).nullsNotDistinct()
]);
```
```sql
CREATE TABLE "user" (
"id" integer UNIQUE
);
CREATE TABLE "table" (
"id" integer CONSTRAINT "custom_name" UNIQUE
);
CREATE TABLE "composite_example" (
"id" integer,
"name" text,
CONSTRAINT "composite_example_id_name_unique" UNIQUE("id","name"),
CONSTRAINT "custom_name" UNIQUE("id","name")
);
CREATE TABLE "user_nulls_example" (
"id" integer UNIQUE NULLS NOT DISTINCT,
"id2" integer CONSTRAINT "custom_name" UNIQUE NULLS NOT DISTINCT
);
```
### Check
The `CHECK` constraint is used to limit the value range that can be placed in a column.
If you define a `CHECK` constraint on a column it will allow only certain values for this column.
If you define a `CHECK` constraint on a table it can limit the values in certain columns based on values in other columns in the row.
```typescript copy
import { sql } from "drizzle-orm";
import { check, integer, pgTable, text, uuid } from "drizzle-orm/pg-core";
export const users = pgTable(
"users",
{
id: uuid().defaultRandom().primaryKey(),
username: text().notNull(),
age: integer(),
},
(table) => [
check("age_check1", sql`${table.age} > 21`),
]
);
```
```sql
CREATE TABLE "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"username" text NOT NULL,
"age" integer,
CONSTRAINT "age_check1" CHECK ("age" > 21)
);
```
### Primary Key
The `PRIMARY KEY` constraint uniquely identifies each record in a table.
Primary keys must contain `UNIQUE` values, and cannot contain `NULL` values.
A table can have only **ONE** primary key; and in the table, this primary key can consist of single or multiple columns (fields).
```typescript copy
import { serial, text, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable('user', {
id: serial('id').primaryKey(),
});
export const table = pgTable('table', {
id: text('cuid').primaryKey(),
});
```
```sql
CREATE TABLE "user" (
"id" serial PRIMARY KEY
);
CREATE TABLE "table" (
"cuid" text PRIMARY KEY
);
```
### Composite Primary Key
Just like `PRIMARY KEY`, composite primary key uniquely identifies each record in a table using multiple fields.
Drizzle ORM provides a standalone `primaryKey` operator for that:
```typescript copy {17,19}
import { serial, text, integer, primaryKey, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial("id").primaryKey(),
name: text("name"),
});
export const book = pgTable("book", {
id: serial("id").primaryKey(),
name: text("name"),
});
export const booksToAuthors = pgTable("books_to_authors", {
authorId: integer("author_id"),
bookId: integer("book_id"),
}, (table) => [
primaryKey({ columns: [table.bookId, table.authorId] }),
// Or PK with custom name
primaryKey({ name: 'custom_name', columns: [table.bookId, table.authorId] }),
]);
```
```sql {6,9}
...
CREATE TABLE "books_to_authors" (
"author_id" integer,
"book_id" integer,
PRIMARY KEY("book_id","author_id")
);
ALTER TABLE "books_to_authors" ADD CONSTRAINT "custom_name" PRIMARY KEY("book_id","author_id");
```
### Foreign key
The `FOREIGN KEY` constraint is used to prevent actions that would destroy links between tables.
A `FOREIGN KEY` is a field (or collection of fields) in one table, that refers to the `PRIMARY KEY` in another table.
The table with the foreign key is called the child table, and the table with the primary key is called the referenced or parent table.
Drizzle ORM provides several ways to declare foreign keys.
You can declare them in a column declaration statement:
```typescript copy {11}
import { serial, text, integer, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial("id"),
name: text("name"),
});
export const book = pgTable("book", {
id: serial("id"),
name: text("name"),
authorId: integer("author_id").references(() => user.id)
});
```
```sql {12}
CREATE TABLE "user" (
"id" serial,
"name" text
);
CREATE TABLE "book" (
"id" serial,
"name" text,
"author_id" integer
);
ALTER TABLE "book" ADD CONSTRAINT "book_author_id_user_id_fkey" FOREIGN KEY ("author_id") REFERENCES "user"("id");
```
If you want to do a self reference, due to a TypeScript limitations you will have to either explicitly
set return type for reference callback or use a standalone `foreignKey` operator.
```typescript copy {6,16-18}
import { serial, text, integer, foreignKey, pgTable, type AnyPgColumn } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial("id"),
name: text("name"),
parentId: integer("parent_id").references((): AnyPgColumn => user.id)
});
// or
export const user = pgTable("user", {
id: serial("id"),
name: text("name"),
parentId: integer("parent_id"),
}, (table) => [
foreignKey({
columns: [table.parentId],
foreignColumns: [table.id],
name: "custom_fk"
})
]);
```
```sql {7}
CREATE TABLE "user" (
"id" serial,
"name" text,
"parent_id" integer
);
ALTER TABLE "user" ADD CONSTRAINT "user_parent_id_user_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "user"("id");
```
To declare multi-column foreign keys you can use a dedicated `foreignKey` operator:
```typescript copy {15-19}
import { serial, text, foreignKey, pgTable, AnyPgColumn } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
firstName: text("firstName"),
lastName: text("lastName"),
}, (table) => [
primaryKey({ columns: [table.firstName, table.lastName]})
]);
export const profile = pgTable("profile", {
id: serial("id").primaryKey(),
userFirstName: text("user_first_name"),
userLastName: text("user_last_name"),
}, (table) => [
foreignKey({
columns: [table.userFirstName, table.userLastName],
foreignColumns: [user.firstName, user.lastName],
name: "custom_fk"
})
])
```
```sql {13}
CREATE TABLE "user" (
"firstName" text,
"lastName" text,
CONSTRAINT "user_pkey" PRIMARY KEY("firstName","lastName")
);
CREATE TABLE "profile" (
"id" serial PRIMARY KEY,
"user_first_name" text,
"user_last_name" text
);
ALTER TABLE "profile" ADD CONSTRAINT "custom_fk" FOREIGN KEY ("user_first_name","user_last_name") REFERENCES "user"("firstName","lastName");
```
## Indexes
Drizzle ORM provides API for both `index` and `unique index` declaration:
```typescript copy {8-9}
import { serial, text, index, uniqueIndex, pgTable } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial().primaryKey(),
name: text(),
email: text(),
}, (table) => [
index("name_idx").on(table.name),
uniqueIndex("email_idx").on(table.email)
]);
```
```sql {5-6}
CREATE TABLE "user" (
...
);
CREATE INDEX "name_idx" ON "user" ("name");
CREATE UNIQUE INDEX "email_idx" ON "user" ("email");
```
Drizzle ORM provides a set of params for index creation:
```ts
// `.on()`
index('name')
.on(table.column1.asc(), table.column2.nullsFirst(), ...)
.concurrently()
.where(sql``)
.with({ fillfactor: '70' })
// `.onOnly()`
index('name')
.onOnly(table.column1.asc(), table.column2.nullsFirst(), ...)
.concurrently()
.where(sql``)
.with({ fillfactor: '70' })
// Second Example, with `.using()`
index('name')
.using('btree', table.column1.asc(), sql`lower(${table.column2})`, table.column1.op('text_ops'))
.concurrently()
.where(sql``) // sql expression
.with({ fillfactor: '70' })
```
Source: https://orm.drizzle.team/docs/pg/insert
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
# SQL Insert
All the values provided to `.values()`are parameterized automatically.
For example, this query:
```ts
await db.insert(users).values({ name: 'Andrew' });
```
will be translated to:
```sql
insert into "users" ("id", "name") values (default, $1) -- params: ['Andrew']
```
Drizzle ORM provides you the most SQL-like way to insert rows into the database tables.
Inserting data with Drizzle is extremely straightforward and sql-like. See for yourself:
```typescript copy
await db.insert(users).values({ name: 'Andrew' });
```
```sql
insert into "users" ("id", "name", "age") values (default, 'Andrew', default)
```
If you need insert type for a particular table you can use `typeof usersTable.$inferInsert` syntax.
```typescript copy
type NewUser = typeof users.$inferInsert;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);
```
## returning
You can insert a row and get it back in PostgreSQL like such:
```typescript copy
await db.insert(users).values({ name: "Dan" }).returning();
// partial return
await db.insert(users).values({ name: "Partial Dan" }).returning({ insertedId: users.id });
```
## Insert multiple rows
```typescript copy
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);
```
## Upserts and conflicts
Drizzle ORM provides simple interfaces for handling upserts and conflicts.
### On conflict do nothing
`onConflictDoNothing` will cancel the insert if there's a conflict:
```typescript copy
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing();
// explicitly specify conflict target
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing({ target: users.id });
```
### On conflict do update
`onConflictDoUpdate` will update the row if there's a conflict:
```typescript
await db.insert(users)
.values({ id: 1, name: 'Dan' })
.onConflictDoUpdate({ target: users.id, set: { name: 'John' } });
```
#### `where` clauses
`on conflict do update` can have a `where` clause in two different places -
as part of the conflict target (i.e. for partial indexes) or as part of the `update` clause:
```sql
insert into "employees" ("employee_id", "name")
values (123, 'John Doe')
on conflict ("employee_id") where name <> 'John Doe'
do update set "name" = excluded.name;
insert into "employees" ("employee_id", "name")
values (123, 'John Doe')
on conflict ("employee_id") do update set "name" = 'John Doe'
where name <> 'John Doe';
```
To specify these conditions in Drizzle, you can use `setWhere` and `targetWhere` clauses:
```typescript
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
targetWhere: sql`name <> 'John Doe'`,
set: { name: sql`excluded.name` }
});
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
set: { name: 'John Doe' },
setWhere: sql`name <> 'John Doe'`
});
```
Upsert with composite indexes, or composite primary keys for `onConflictDoUpdate`:
```typescript
await db.insert(users)
.values({ firstName: 'John', lastName: 'Doe' })
.onConflictDoUpdate({
target: [users.firstName, users.lastName],
set: { firstName: 'John1' }
});
```
## `with insert` clause
Check how to use WITH statement with [select](/docs/select#with-clause), [update](/docs/update#with-update-clause), [delete](/docs/delete#with-delete-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 userCount = db.$with('user_count').as(
db.select({ value: sql`count(*)`.as('value') }).from(users)
);
const result = await db.with(userCount)
.insert(users)
.values([
{ username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }
])
.returning({
admin: users.admin
});
```
```sql
with "user_count" as (select count(*) as "value" from "users")
insert into "users" ("username", "admin")
values ('user1', ((select * from "user_count") = 0))
returning "admin"
```
## Insert into ... select
As the PostgreSQL documentation mentions:
A query (SELECT statement) that supplies the rows to be inserted
Drizzle supports the current syntax for all dialects, and all of them share the same syntax. Let's review some common scenarios and API usage.
There are several ways to use select inside insert statements, allowing you to choose your preferred approach:
- You can pass a query builder inside the select function.
- You can use a query builder inside a callback.
- You can pass an SQL template tag with any custom select query you want to use
```ts
const insertedEmployees = await db
.insert(employees)
.select(
db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
)
.returning({
id: employees.id,
name: employees.name
});
```
```ts
const qb = new QueryBuilder();
await db.insert(employees).select(
qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
() => db.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
(qb) => qb.select({ id: users.id, name: users.name }).from(users).where(eq(users.role, 'employee'))
);
```
```ts
await db.insert(employees).select(
sql`select "users"."id" as "id", "users"."name" as "name" from "users" where "users"."role" = 'employee'`
);
```
```ts
await db.insert(employees).select(
() => sql`select "users"."id" as "id", "users"."name" as "name" from "users" where "users"."role" = 'employee'`
);
```
Source: https://orm.drizzle.team/docs/pg/joins
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Joins [SQL]
Join clause in SQL is used to combine 2 or more tables, based on related columns between them.
Drizzle ORM joins syntax is a balance between the SQL-likeness and type safety.
## Join types
Drizzle ORM has APIs for `INNER JOIN [LATERAL]`, `FULL JOIN`, `LEFT JOIN [LATERAL]`, `RIGHT JOIN`, `CROSS JOIN [LATERAL]`.
Lets have a quick look at examples based on below table schemas:
```typescript copy
export const users = pgTable("users", {
id: serial().primaryKey(),
name: text().notNull(),
age: integer(),
});
export const pets = pgTable("pets", {
id: serial().primaryKey(),
name: text().notNull(),
ownerId: integer("owner_id")
.notNull()
.references(() => users.id),
});
```
### Left Join
```typescript copy
const result = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Left Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).leftJoinLateral(subquery, sql`true`)
```
```sql
select ... from "users" left join lateral (select ... from "pets" where "users"."age" >= 16) "userPets" on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Right Join
```typescript copy
const result = await db.select().from(users).rightJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" right join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join
```typescript copy
const result = await db.select().from(users).innerJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" inner join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Inner Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).innerJoinLateral(subquery, sql`true`)
```
```sql
select ... from "users" inner join lateral (select ... from "pets" where "users"."age" >= 16) "userPets" on true
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Full Join
```typescript copy
const result = await db.select().from(users).fullJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" full join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
} | null;
pets: {
id: number;
name: string;
ownerId: number;
} | null;
}[];
```
### Cross Join
```typescript copy
const result = await db.select().from(users).crossJoin(pets)
```
```sql
select ... from "users" cross join "pets"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
pets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
### Cross Join Lateral
```typescript copy
const subquery = db.select().from(pets).where(gte(users.age, 16)).as('userPets')
const result = await db.select().from(users).crossJoinLateral(subquery)
```
```sql
select ... from "users" cross join lateral (select ... from "pets" where "users"."age" >= 16) "userPets"
```
```typescript
// result type
const result: {
users: {
id: number;
name: string;
age: number | null;
};
userPets: {
id: number;
name: string;
ownerId: number;
};
}[];
```
## Partial select
If you need to select a particular subset of fields or to have a flat response type, Drizzle ORM
supports joins with partial select and will automatically infer return type based on `.select({ ... })` structure.
```typescript copy
await db.select({
userId: users.id,
petId: pets.id,
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select "users"."id", "pets"."id" from "users" left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
}[];
```
You might've noticed that `petId` can be null now, it's because we're left joining and there can be users without a pet.
It's very important to keep in mind when using `sql` operator for partial selection fields and aggregations when needed,
you should to use `sql` for proper result type inference, that one is on you!
```typescript copy
const result = await db.select({
userId: users.id,
petId: pets.id,
petName1: sql`upper(${pets.name})`,
petName2: sql`upper(${pets.name})`,
//Ëwe should explicitly tell 'string | null' in type, since we're left joining that field
}).from(users).leftJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select "users"."id", "pets"."id", upper("pets"."name"), upper("pets"."name")
from "users"
left join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number;
petId: number | null;
petName1: unknown;
petName2: string | null;
}[];
```
To avoid plethora of nullable fields when joining tables with lots of columns we can utilise our **nested select object syntax**,
our smart type inference will make whole object nullable instead of making all table fields nullable!
```typescript copy
await db.select({
userId: users.id,
userName: users.name,
pet: {
id: pets.id,
name: pets.name,
upperName: sql`upper(${pets.name})`
}
}).from(users).fullJoin(pets, eq(users.id, pets.ownerId))
```
```sql
select ... from "users" full join "pets" on "users"."id" = "pets"."owner_id"
```
```typescript
// result type
const result: {
userId: number | null;
userName: string | null;
pet: {
id: number;
name: string;
upperName: string;
} | null;
}[];
```
## Aliases & Selfjoins
Drizzle ORM supports table aliases which comes really handy when you need to do selfjoins.
Lets say you need to fetch users with their parents:
```typescript copy
import { user } from "./schema";
import { alias } from "drizzle-orm/pg-core";
const parent = alias(user, "parent");
const result = await db
.select()
.from(user)
.leftJoin(parent, eq(parent.id, user.parentId));
```
```sql
select ... from "user" left join "user" "parent" on "parent"."id" = "user"."parent_id"
```
```typescript
// result type
const result: {
user: {
id: number;
name: string;
parentId: number;
};
parent: {
id: number;
name: string;
parentId: number;
} | null;
}[];
```
```typescript
export const user = pgTable("user", {
id: integer().primaryKey(),
name: text().notNull(),
parentId: integer("parent_id").notNull().references((): AnyPgColumn => user.id)
});
```
## Aggregating results
Drizzle ORM delivers name-mapped results from the driver without changing the structure.
You're free to operate with results the way you want, here's an example of mapping many-one relational data:
```typescript
type User = typeof users.$inferSelect;
type Pet = typeof pets.$inferSelect;
const rows = await db
.select({
user: users,
pet: pets,
})
.from(users)
.leftJoin(pets, eq(users.id, pets.ownerId));
const result = rows.reduce>(
(acc, row) => {
const user = row.user;
const pet = row.pet;
if (typeof acc[user.id] === "undefined") {
acc[user.id] = { user, pets: [] };
}
if (pet) {
acc[user.id]!.pets.push(pet);
}
return acc;
},
{}
);
// result type
const result: Record;
```
## Many-to-one example
```typescript
import { pgTable, text, integer } from "drizzle-orm/pg-core";
import { drizzle } from "drizzle-orm/node-postgres";
const cities = pgTable("cities", {
id: integer().primaryKey(),
name: text(),
});
const users = pgTable("users", {
id: integer().primaryKey(),
name: text(),
cityId: integer("city_id").references(() => cities.id),
});
const db = drizzle(process.env.DATABASE_URL);
const result = await db.select().from(cities).leftJoin(users, eq(cities.id, users.cityId));
```
## Many-to-many example
```typescript
const users = pgTable("users", {
id: integer().primaryKey(),
name: text(),
});
const chatGroups = pgTable("chat_groups", {
id: integer().primaryKey(),
name: text(),
});
const usersToChatGroups = pgTable("users_to_chat_groups", {
userId: integer("user_id")
.notNull()
.references(() => users.id),
groupId: integer("group_id")
.notNull()
.references(() => chatGroups.id),
});
// querying user group with id 1 and all the participants(users)
await db.select()
.from(usersToChatGroups)
.leftJoin(users, eq(usersToChatGroups.userId, users.id))
.leftJoin(chatGroups, eq(usersToChatGroups.groupId, chatGroups.id))
.where(eq(chatGroups.id, 1));
```
Source: https://orm.drizzle.team/docs/pg/kit-custom-migrations
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
- Drizzle Kit [overview](/docs/kit-overview) and [config file](/docs/drizzle-config-file)
- `drizzle-kit generate` command - [read here](/docs/drizzle-kit-generate)
- `drizzle-kit migrate` command - [read here](/docs/drizzle-kit-migrate)
Drizzle lets you generate empty migration files to write your own custom SQL migrations
for DDL alternations currently not supported by Drizzle Kit or data seeding, which you can then run with [`drizzle-kit migrate`](/docs/drizzle-kit-migrate) command.
```shell
drizzle-kit generate --custom --name=seed-users
```
```plaintext {4}
đĻ
â đ drizzle
â â đ 20242409125510_init_sql
â â đ 20242409135510_seed-users
â đ src
â âĻ
```
```sql
-- ./drizzle/20242409135510_seed-users.sql
INSERT INTO "users" ("name") VALUES('Dan');
INSERT INTO "users" ("name") VALUES('Andrew');
INSERT INTO "users" ("name") VALUES('Dandrew');
```
### Running JavaScript and TypeScript migrations
We will add ability to run custom JavaScript and TypeScript migration/seeding scripts in the upcoming release, you can follow [github discussion](https://github.com/drizzle-team/drizzle-orm/discussions/2832).
Source: https://orm.drizzle.team/docs/pg/kit-overview
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 Npm from '@mdx/Npm.astro';
import Npx from '@mdx/Npx.astro';
import Steps from '@mdx/Steps.astro';
import Prerequisites from "@mdx/Prerequisites.astro"
# Migrations with Drizzle Kit
- Get started with Drizzle and `drizzle-kit` - [read here](/docs/get-started)
- Drizzle schema fundamentals - [read here](/docs/sql-schema-declaration)
- Database connection basics - [read here](/docs/connect-overview)
- Drizzle migrations fundamentals - [read here](/docs/migrations)
**Drizzle Kit** is a CLI tool for managing SQL database migrations with Drizzle.
-D drizzle-kit
Make sure to first go through Drizzle [get started](/docs/get-started) and [migration fundamentals](/docs/migrations) and pick SQL migration flow that suits your business needs best.
Based on your schema, Drizzle Kit let's you generate and run SQL migration files,
push schema directly to the database, pull schema from database, spin up drizzle studio and has a couple of utility commands.
drizzle-kit generate
drizzle-kit migrate
drizzle-kit push
drizzle-kit pull
drizzle-kit check
drizzle-kit up
drizzle-kit studio
drizzle-kit export
| | |
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`drizzle-kit generate`](/docs/drizzle-kit-generate) | lets you generate SQL migration files based on your Drizzle schema either upon declaration or on subsequent changes, [see here](/docs/drizzle-kit-generate). |
| [`drizzle-kit migrate`](/docs/drizzle-kit-migrate) | lets you apply generated SQL migration files to your database, [see here](/docs/drizzle-kit-migrate). |
| [`drizzle-kit pull`](/docs/drizzle-kit-pull) | lets you pull(introspect) database schema, convert it to Drizzle schema and save it to your codebase, [see here](/docs/drizzle-kit-pull) |
| [`drizzle-kit push`](/docs/drizzle-kit-push) | lets you push your Drizzle schema to database either upon declaration or on subsequent schema changes, [see here](/docs/drizzle-kit-push) |
| [`drizzle-kit studio`](/docs/drizzle-kit-studio) | will connect to your database and spin up proxy server for Drizzle Studio which you can use for convenient database browsing, [see here](/docs/drizzle-kit-studio) |
| [`drizzle-kit check`](/docs/drizzle-kit-check) | will walk through all generate migrations and check for any race conditions(collisions) of generated migrations, [see here](/docs/drizzle-kit-check) |
| [`drizzle-kit up`](/docs/drizzle-kit-up) | used to upgrade snapshots of previously generated migrations, [see here](/docs/drizzle-kit-up) |
| [`drizzle-kit export`](/docs/drizzle-kit-export) | used to convert a TypeScript schema into raw SQL DDL and print it out [see here](/docs/drizzle-kit-export) |
Drizzle Kit is configured through [drizzle.config.ts](/docs/drizzle-config-file) configuration file or via CLI params.
It's required to at least provide SQL `dialect` and `schema` path for Drizzle Kit to know how to generate migrations.
```
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle.config.ts <--- Drizzle config file
â đ package.json
â đ tsconfig.json
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/schema.ts",
});
```
```ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
dialect: "postgresql",
schema: "./schema.ts",
entities: {
roles: {
exclude: ["admin"],
include: ["user"],
provider: "supabase",
},
},
driver: "pglite",
dbCredentials: {
url: "./database/",
},
extensionsFilters: ["postgis"],
schemaFilter: "public",
tablesFilter: "*",
introspect: {
casing: "camel",
},
migrations: {
table: "__drizzle_migrations__",
schema: "public",
},
breakpoints: true,
verbose: true,
});
```
You can provide Drizzle Kit config path via CLI param, it's very useful when you have multiple database stages or multiple databases or different databases on the same project:
drizzle-kit push --config=drizzle-dev.drizzle.config
drizzle-kit push --config=drizzle-prod.drizzle.config
```plaintext {5-6}
đĻ
â đ drizzle
â đ src
â đ .env
â đ drizzle-dev.config.ts
â đ drizzle-prod.config.ts
â đ package.json
â đ tsconfig.json
```
Source: https://orm.drizzle.team/docs/pg/migrations
import Callout from '@mdx/Callout.astro';
import Steps from '@mdx/Steps.astro';
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 Npm from '@mdx/Npm.astro';
import Tag from '@mdx/Tag.astro'
# Drizzle migrations fundamentals
SQL databases require you to specify a **strict schema** of entities you're going to store upfront
and if (when) you need to change the shape of those entities - you will need to do it via **schema migrations**.
There're multiple production grade ways of managing database migrations.
Drizzle is designed to perfectly suits all of them, regardless of you going **database first** or **codebase first**.
**Database first** is when your database schema is a source of truth. You manage your database schema either directly on the database or
via database migration tools and then you pull your database schema to your codebase application level entities.
**Codebase first** is when database schema in your codebase is a source of truth and is under version control. You declare and manage your database schema in JavaScript/TypeScript
and then you apply that schema to the database itself either with Drizzle, directly or via external migration tools.
#### How can Drizzle help?
We've built [**drizzle-kit**](/docs/kit-overview) - CLI app for managing migrations with Drizzle.
```shell
drizzle-kit migrate
drizzle-kit generate
drizzle-kit push
drizzle-kit pull
drizzle-kit export
```
It is designed to let you choose how to approach migrations based on your current business demands.
It fits in both database and codebase first approaches, it lets you **push your schema** or **generate SQL migration** files or **pull the schema** from database.
It is perfect wether you work alone or in a team.
**Now let's pick the best option for your project:**
**Option 1**
> I manage database schema myself using external migration tools or by running SQL migrations directly on my database.
> From Drizzle I just need to get current state of the schema from my database and save it as TypeScript schema file.
That's a **database first** approach. You have your database schema as a **source of truth** and
Drizzle lets you pull database schema to TypeScript using [`drizzle-kit pull`](/docs/drizzle-kit-pull) command.
```
ââââââââââââââââââââââââââ âââââââââââââââââââââââââââ
â â <--- CREATE TABLE "users" (
ââââââââââââââââââââââââââââ â â "id" SERIAL PRIMARY KEY,
â ~ drizzle-kit pull â â â "name" TEXT,
âââŦâââââââââââââââââââââââââ â DATABASE â "email" TEXT UNIQUE
â â â );
â Pull datatabase schema -----> â â
â Generate Drizzle <----- â â
â schema TypeScript file ââââââââââââââââââââââââââ
â
v
```
```typescript
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
**Option 2**
> I want to have database schema in my TypeScript codebase,
> I don't wanna deal with SQL migration files.
> I want Drizzle to "push" my schema directly to the database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a **source of truth** and
Drizzle lets you push schema changes to the database using [`drizzle-kit push`](/docs/drizzle-kit-push) command.
That's the best approach for rapid prototyping and we've seen dozens of teams
and solo developers successfully using it as a primary migrations flow in their production applications.
```typescript {6} filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(), // <--- added column
});
```
```
Add column to `users` table
ââââââââââââââââââââââââââââ
â + email: text().unique() â
âââŦâââââââââââââââââââââââââ
â
v
ââââââââââââââââââââââââââââ
â ~ drizzle-kit push â
âââŦâââââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â Pull current datatabase schema ---------> â â
â â
â Generate alternations based on diff <---- â DATABASE â
â â â
â Apply migrations to the database -------> â â
â ââââââââââââââââââââââââââââ
â
ââââââââââââââââââââââââââââââââââââââ´ââââââââââââââââââââââââââââââââ
ALTER TABLE "users" ADD COLUMN "email" text;
ALTER TABLE "users" ADD CONSTRAINT "users_email_key" UNIQUE("email");
```
**Option 3**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and apply them to my database
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/drizzle-kit-generate)
and then apply them to the database with [`drizzle-kit migrate`](/docs/drizzle-kit-migrate) commands.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT UNIQUE
);
```
```
âââââââââââââââââââââââââ
â $ drizzle-kit migrate â
âââŦââââââââââââââââââââââ
â ââââââââââââââââââââââââââââ
â 1. read migration.sql files in migrations folder â â
2. fetch migration history from database -------------> â â
â 3. pick previously unapplied migrations <-------------- â DATABASE â
â 4. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 4**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me and I want Drizzle to apply them during runtime
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/drizzle-kit-generate) and then
you can apply them to the database during runtime of your application.
This approach is widely used for **monolithic** applications when you apply database migrations
during zero downtime deployment and rollback DDL changes if something fails.
This is also used in **serverless** deployments with migrations running in **custom resource** once during deployment process.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous schema
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT UNIQUE
);
```
```ts
// index.ts
import { drizzle } from "drizzle-orm/node-postgres"
import { migrate } from 'drizzle-orm/node-postgres/migrator';
const db = drizzle(process.env.DATABASE_URL);
await migrate(db);
```
```
âââââââââââââââââââââââââ
â npx tsx src/index.ts â
âââŦââââââââââââââââââââââ
â
â 1. init database connection ââââââââââââââââââââââââââââ
â 2. read migration.sql files in migrations folder â â
3. fetch migration history from database -------------> â â
â 4. pick previously unapplied migrations <-------------- â DATABASE â
â 5. apply new migration to the database ---------------> â â
â â
ââââââââââââââââââââââââââââ
[â] done!
```
**Option 5**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to generate SQL migration files for me,
> but I will apply them to my database myself or via external migration tools
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you generate SQL migration files based on your schema changes with [`drizzle-kit generate`](/docs/drizzle-kit-generate) and then
you can apply them to the database either directly or via external migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit generate â
âââŦâââââââââââââââââââââââ
â
â 1. read previous migration folders
2. find diff between current and previous scheama
3. prompt developer for renames if necessary
â 4. generate SQL migration and persist to file
â âââ´ââââââââââââââââââââââââââââââââââââââââ
â đ drizzle
â â đ 20242409125510_premium_mister_fear
â â đ snapshot.json
â â đ migration.sql
v
```
```sql
-- drizzle/20242409125510_premium_mister_fear/migration.sql
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT UNIQUE
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
directly to the database
â ââââââââââââââââââââââ
ââââââââââââââââââââââââââââââââââââââŦâââ>â â
â â â Database â
or via external tools â â â
â â ââââââââââââââââââââââ
â ââââââââââââââââââââââ â
ââââ Bytebase ââââââââââââââ
ââââââââââââââââââââââ¤
â Liquibase â
ââââââââââââââââââââââ¤
â Atlas â
ââââââââââââââââââââââ¤
â etcâĻ â
ââââââââââââââââââââââ
[â] done!
```
**Option 6**
> I want to have database schema in my TypeScript codebase,
> I want Drizzle to output the SQL representation of my Drizzle schema to the console,
> and I will apply them to my database via [Atlas](https://atlasgo.io/guides/orms/drizzle)
That's a **codebase first** approach. You have your TypeScript Drizzle schema as a source of truth and
Drizzle lets you export SQL statements based on your schema changes with [`drizzle-kit export`](/docs/drizzle-kit-generate) and then
you can apply them to the database via [Atlas](https://atlasgo.io/guides/orms/drizzle) or other external SQL migration tools.
```typescript filename="src/schema.ts"
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.serial().primaryKey(),
name: p.text(),
email: p.text().unique(),
});
```
```
ââââââââââââââââââââââââââ
â $ drizzle-kit export â
âââŦâââââââââââââââââââââââ
â
â 1. read your drizzle schema
2. generated SQL representation of your schema
â 3. outputs to console
â
â
v
```
```sql
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT UNIQUE
);
```
```
âââââââââââââââââââââââââââââââââââââ
â (._.) now you run your migrations â
âââŦââââââââââââââââââââââââââââââââââ
â
via Atlas
â ââââââââââââââââ
â ââââââââââââââââââââââ â â
ââââ Atlas ââââââââââââ>â Database â
ââââââââââââââââââââââ â â
ââââââââââââââââ
[â] done!
```
Source: https://orm.drizzle.team/docs/pg/operators
import Section from '@mdx/Section.astro';
import Callout from "@mdx/Callout.astro";
# Filter and conditional operators
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select "id", "name", "age" from "users" where "users"."id" = $1; -- params: [42]
```
We natively support all dialect specific filter and conditional operators.
You can import all filter & conditional from `drizzle-orm`:
```typescript copy
import { eq, ne, gt, gte, ... } from "drizzle-orm";
```
### eq
Value equal to `n`
```typescript copy
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column, 5));
```
```sql copy
SELECT * FROM "table" WHERE "table"."column" = 5
```
```typescript
import { eq } from "drizzle-orm";
db.select().from(table).where(eq(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" = "table"."column2"
```
### ne
Value is not equal to `n`
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" <> 5
```
```typescript
import { ne } from "drizzle-orm";
db.select().from(table).where(ne(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" <> "table"."column2"
```
## ---
### gt
Value is greater than `n`
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" > 5
```
```typescript
import { gt } from "drizzle-orm";
db.select().from(table).where(gt(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" > "table"."column2"
```
### gte
Value is greater than or equal to `n`
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" >= 5
```
```typescript
import { gte } from "drizzle-orm";
db.select().from(table).where(gte(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" >= "table"."column2"
```
### lt
Value is less than `n`
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" < 5
```
```typescript
import { lt } from "drizzle-orm";
db.select().from(table).where(lt(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" < "table"."column2"
```
### lte
Value is less than or equal to `n`.
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column, 5));
```
```sql
SELECT * FROM "table" WHERE "table"."column" <= 5
```
```typescript
import { lte } from "drizzle-orm";
db.select().from(table).where(lte(table.column1, table.column2));
```
```sql
SELECT * FROM "table" WHERE "table"."column1" <= "table"."column2"
```
## ---
### exists
Value exists
```typescript
import { exists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(exists(query));
```
```sql
SELECT * FROM "table" WHERE EXISTS (SELECT * FROM "table2")
```
### notExists
```typescript
import { notExists } from "drizzle-orm";
const query = db.select().from(table2)
db.select().from(table).where(notExists(query));
```
```sql
SELECT * FROM "table" WHERE NOT EXISTS (SELECT * FROM "table2")
```
### isNull
Value is `null`
```typescript
import { isNull } from "drizzle-orm";
db.select().from(table).where(isNull(table.column));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" IS NULL)
```
### isNotNull
Value is not `null`
```typescript
import { isNotNull } from "drizzle-orm";
db.select().from(table).where(isNotNull(table.column));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" IS NOT NULL)
```
## ---
### inArray
Value is in array of values
```typescript
import { inArray } from "drizzle-orm";
db.select().from(table).where(inArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM "table" WHERE "table"."column" IN (1, 2, 3, 4)
```
```typescript
import { inArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(inArray(table.column, query));
```
```sql
SELECT * FROM "table" WHERE "table"."column" IN (SELECT "table2"."column" FROM "table2")
```
### notInArray
Value is not in array of values
```typescript
import { notInArray } from "drizzle-orm";
db.select().from(table).where(notInArray(table.column, [1, 2, 3, 4]));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT IN (1, 2, 3, 4)
```
```typescript
import { notInArray } from "drizzle-orm";
const query = db.select({ data: table2.column }).from(table2);
db.select().from(table).where(notInArray(table.column, query));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT IN (SELECT "table2"."column" FROM "table2")
```
## ---
### between
Value is between two values
```typescript
import { between } from "drizzle-orm";
db.select().from(table).where(between(table.column, 2, 7));
```
```sql
SELECT * FROM "table" WHERE "table"."column" BETWEEN 2 AND 7
```
### notBetween
Value is not between two value
```typescript
import { notBetween } from "drizzle-orm";
db.select().from(table).where(notBetween(table.column, 2, 7));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT BETWEEN 2 AND 7
```
## ---
### like
Value is like other value, case sensitive
```typescript
import { like } from "drizzle-orm";
db.select().from(table).where(like(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" LIKE '%llo wor%'
```
### notLike
Value is not like other value, case sensitive
```typescript
import { notLike } from "drizzle-orm";
db.select().from(table).where(notLike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT LIKE '%llo wor%'
```
### ilike
Value is like some other value, case insensitive
```typescript
import { ilike } from "drizzle-orm";
db.select().from(table).where(ilike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" ILIKE '%llo wor%'
```
### notIlike
Value is not like some other value, case insensitive
```typescript
import { notIlike } from "drizzle-orm";
db.select().from(table).where(notIlike(table.column, "%llo wor%"));
```
```sql
SELECT * FROM "table" WHERE "table"."column" NOT ILIKE '%llo wor%'
```
## ---
### not
All conditions must return `false`.
```typescript
import { eq, not } from "drizzle-orm";
db.select().from(table).where(not(eq(table.column, 5)));
```
```sql
SELECT * FROM "table" WHERE NOT ("table"."column" = 5)
```
### and
All conditions must return `true`.
```typescript
import { gt, lt, and } from "drizzle-orm";
db.select().from(table).where(and(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" > 5 AND "table"."column" < 7)
```
### or
One or more conditions must return `true`.
```typescript
import { gt, lt, or } from "drizzle-orm";
db.select().from(table).where(or(gt(table.column, 5), lt(table.column, 7)));
```
```sql
SELECT * FROM "table" WHERE ("table"."column" > 5 OR "table"."column" < 7)
```
## ---
### arrayContains
Test that a column or expression contains all elements of the list passed as the second argument
```typescript
import { arrayContains } from "drizzle-orm";
const contains = await db.select({ id: posts.id }).from(posts)
.where(arrayContains(posts.tags, ['Typescript', 'ORM']));
const withSubQuery = await db.select({ id: posts.id }).from(posts)
.where(arrayContains(
posts.tags,
db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)),
));
```
```sql
select "id" from "posts" where "posts"."tags" @> {Typescript,ORM};
select "id" from "posts" where "posts"."tags" @> (select "tags" from "posts" where "posts"."id" = 1);
```
### arrayContained
Test that the list passed as the second argument contains all elements of a column or expression
```typescript
import { arrayContained } from "drizzle-orm";
const contained = await db.select({ id: posts.id }).from(posts)
.where(arrayContained(posts.tags, ['Typescript', 'ORM']));
```
```sql
select "id" from "posts" where "posts"."tags" <@ {Typescript,ORM};
```
### arrayOverlaps
Test that a column or expression contains any elements of the list passed as the second argument.
```typescript
import { arrayOverlaps } from "drizzle-orm";
const overlaps = await db.select({ id: posts.id }).from(posts)
.where(arrayOverlaps(posts.tags, ['Typescript', 'ORM']));
```
```sql
select "id" from "posts" where "posts"."tags" && {Typescript,ORM}
```
Source: https://orm.drizzle.team/docs/pg/overview
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import YoutubeCards from '@mdx/YoutubeCards.astro';
import GetStartedLinks from '@mdx/GetStartedLinks/index.astro'
# Drizzle ORM
Drizzle ORM is a headless TypeScript ORM with a head. đ˛
> Drizzle is a good friend who's there for you when necessary and doesn't bother when you need some space.
It looks and feels simple, performs on day _1000_ of your project,\
lets you do things your way, and is there when you need it.
**It's the only ORM with both [relational](/docs/rqb) and [SQL-like](/docs/select) query APIs**,
providing you the best of both worlds when it comes to accessing your relational data.
Drizzle is lightweight, performant, typesafe, non-lactose, gluten-free, sober, flexible and **serverless-ready by design**.
Drizzle is not just a library, it's an experience. đ¤Š
[](https://bestofjs.org/projects/drizzle-orm)
## Headless ORM?
First and foremost, Drizzle is a library and a collection of complementary opt-in tools.
**ORM** stands for _object relational mapping_, and developers tend to call Django-like or Spring-like tools an ORM.
We truly believe it's a misconception based on legacy nomenclature, and we call them **data frameworks**.
With data frameworks you have to build projects **around them** and not **with them**.
**Drizzle** lets you build your project the way you want, without interfering with your project or structure.
Using Drizzle you can define and manage database schemas in TypeScript, access your data in a SQL-like
or relational way, and take advantage of opt-in tools
to push your developer experience _through the roof_. đ¤¯
## Why SQL-like?
**If you know SQL, you know Drizzle.**
Other ORMs and data frameworks tend to deviate/abstract you away from SQL, which
leads to a double learning curve: needing to know 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 can have zero to no
learning curve and access to the full power of SQL.
We bring all the familiar **[SQL schema](/docs/sql-schema-declaration)**, **[queries](/docs/select)**,
**[automatic migrations](/docs/migrations)** and **[one more thing](/docs/rqb)**. â¨
```typescript copy
// Access your data
await db
.select()
.from(countries)
.leftJoin(cities, eq(cities.countryId, countries.id))
.where(eq(countries.id, 10))
```
```typescript copy
// manage your schema
export const countries = pgTable('countries', {
id: serial().primaryKey(),
name: varchar({ length: 256 }),
});
export const cities = pgTable('cities', {
id: serial().primaryKey(),
name: varchar({ length: 256 }),
countryId: integer('country_id').references(() => countries.id),
});
```
```sql
CREATE TABLE "countries" (
"id" serial PRIMARY KEY,
"name" varchar(256)
);
CREATE TABLE "cities" (
"id" serial PRIMARY KEY,
"name" varchar(256),
"country_id" integer
);
ALTER TABLE "cities" ADD CONSTRAINT "cities_country_id_countries_id_fkey" FOREIGN KEY ("country_id") REFERENCES "countries"("id");
```
## Why not SQL-like?
We're always striving for a perfectly balanced solution, and while SQL-like does cover 100% of the needs,
there are certain common scenarios where you can query data in a better way.
We've built the **[Queries API](/docs/rqb)** for you, so you can fetch relational nested data from the database
in the most convenient and performant way, and never think about joins and data mapping.
**Drizzle always outputs exactly 1 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
},
});
```
## Serverless?
The best part is no part. **Drizzle has exactly 0 dependencies!**

Drizzle ORM is dialect-specific, slim, performant and serverless-ready **by design**.
We've spent a lot of time to make sure you have best-in-class SQL dialect support, including Postgres, MySQL, and others.
Drizzle operates natively through industry-standard database drivers. We support all major **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[SingleStore](/docs/singlestore/get-started-singlestore)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** drivers out there, and we're adding new ones **[really fast](https://twitter.com/DrizzleORM/status/1653082492742647811?s=20)**.
## Welcome on board!
More and more companies are adopting Drizzle in production, experiencing immense benefits in both DX and performance.
**We're always there to help, so don't hesitate to reach out. We'll gladly assist you in your Drizzle journey!**
We have an outstanding **[Discord community](https://driz.link/discord)** and welcome all builders to our **[Twitter](https://twitter.com/drizzleorm)**.
Now go build something awesome with Drizzle and your **[PostgreSQL](/docs/get-started-postgresql)**, **[MySQL](/docs/mysql/get-started-mysql)**, **[SQLite](/docs/sqlite/get-started-sqlite)**, **[MSSQL](/docs/mssql/get-started-mssql)** or **[CockroachDB](/docs/cockroach/get-started-cockroach)** database. đ
### Video Showcase
{/* tRPC + NextJS App Router = Simple Typesafe APIs
Jack Herrington 19:17
https://www.youtube.com/watch?v=qCLV0Iaq9zU */}
{/* https://www.youtube.com/watch?v=qDunJ0wVIec */}
{/* https://www.youtube.com/watch?v=NZpPMlSAez0 */}
{/* https://www.youtube.com/watch?v=-A0kMiJqQRY */}
Source: https://orm.drizzle.team/docs/pg/perf-queries
# Query performance
When it comes to **Drizzle** â we're a thin TypeScript layer on top of SQL with
almost 0 overhead and to make it actual 0, you can utilise our prepared statements API.
**When you run a query on the database, there are several things that happen:**
- all the configurations of the query builder got concatenated to the SQL string
- that string and params are sent to the database driver
- driver compiles SQL query to the binary SQL executable format and sends it to the database
With prepared statements you do SQL concatenation once on the Drizzle ORM side and then database
driver is able to reuse precompiled binary SQL instead of parsing query all the time.
It has extreme performance benefits on large SQL queries.
## Prepared statement
```typescript copy {3}
const db = drizzle(...);
const prepared = db.select().from(customers).prepare("statement_name");
const res1 = await prepared.execute();
const res2 = await prepared.execute();
const res3 = await prepared.execute();
```
## Placeholder
Whenever you need to embed a dynamic runtime value - you can use the `sql.placeholder(...)` api
```ts {6,9-10,15,18}
import { sql } from "drizzle-orm";
const p1 = db
.select()
.from(customers)
.where(eq(customers.id, sql.placeholder('id')))
.prepare("p1")
await p1.execute({ id: 10 }) // SELECT * FROM customers WHERE id = 10
await p1.execute({ id: 12 }) // SELECT * FROM customers WHERE id = 12
const p2 = db
.select()
.from(customers)
.where(sql`lower(${customers.name}) like ${sql.placeholder('name')}`)
.prepare("p2");
await p2.execute({ name: '%an%' }) // SELECT * FROM customers WHERE lower(name) like '%an%'
```
Source: https://orm.drizzle.team/docs/pg/query-utils
import $count from '@mdx/$count-pg.mdx';
# Drizzle query utils
### $count
<$count/>
Source: https://orm.drizzle.team/docs/pg/read-replicas
# Read Replicas
When your project involves a set of read replica instances, and you require a convenient method for managing
SELECT queries from read replicas, as well as performing create, delete, and update operations on the primary
instance, you can leverage the `withReplicas()` function within Drizzle
```ts copy
import { sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/node-postgres';
import { boolean, jsonb, pgTable, serial, text, timestamp, withReplicas } from 'drizzle-orm/pg-core';
const usersTable = pgTable("users", {
id: serial().primaryKey(),
name: text().notNull(),
verified: boolean().notNull().default(false),
jsonb: jsonb().$type(),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
});
const primaryDb = drizzle("postgres://user:password@host:port/primary_db");
const read1 = drizzle("postgres://user:password@host:port/read_replica_1");
const read2 = drizzle("postgres://user:password@host:port/read_replica_2");
const db = withReplicas(primaryDb, [read1, read2]);
```
You can now use the `db` instance the same way you did before. Drizzle will
handle the choice between read replica and the primary instance automatically
```ts
// Read from either the read1 connection or the read2 connection
await db.select().from(usersTable)
// Use the primary database for the delete operation
await db.delete(usersTable).where(eq(usersTable.id, 1))
```
You can use the `$primary` key to force using primary instances even for read operations
```ts
// read from primary
await db.$primary.select().from(usersTable);
```
With Drizzle, you can also specify custom logic for choosing read replicas.
You can make a weighted decision or any other custom selection method for random read replica choice.
Here is an implementation example of custom logic for selecting read replicas,
where the first replica has a 70% chance of being chosen, and the second replica has a 30%
chance of being selected.
Keep in mind that you can implement any type of random selection method for read replicas
```ts
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!
});
await db.select().from(usersTable)
```
Source: https://orm.drizzle.team/docs/pg/relations-schema-declaration
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import Callout from '@mdx/Callout.astro';
import SimpleLinkCards from '@mdx/SimpleLinkCards.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"
# Drizzle Relations Fundamentals
In the world of databases, especially relational databases, the concept of relations is absolutely fundamental.
Think of "relations" as the connections and links between different pieces of data. Just like in real life,
where people have relationships with each other, or objects are related to categories, databases use relations to model how different
types of information are connected and work together.
### Normalization
Normalization is the process of organizing data in your database to reduce redundancy (duplication) and improve data integrity
(accuracy and consistency). Think of it like tidying up a messy filing cabinet. Instead of having all sorts of papers
crammed into one folder, you organize them into logical folders and categories to make everything easier to find and manage.
- **Reduces Data Redundancy**: Imagine storing a customer's address every time they place an order. If the address changes, you'd have to update it in multiple places! Normalization helps you store information in one place and refer to it from other places, minimizing repetition.
- **Improves Data Integrity**: Less redundancy means less chance of inconsistencies. If you update an address in one place, it's updated everywhere it's needed.
- **Prevents Anomalies**: Normalization helps prevent issues like:
1. **Insertion Anomalies**: Difficulty adding new data because you're missing related information.
2. **Update Anomalies**: Having to update the same information in multiple rows.
3. **Deletion Anomalies**: Accidentally losing valuable information when you delete something seemingly unrelated.
- **Easier to Understand and Maintain**: A normalized database is generally more logically structured and easier to understand, query, and modify.
Normalization is often described in terms of "normal forms" (1NF, 2NF, 3NF, and beyond). While the details can get quite technical, the core ideas are straightforward:
#### 1NF (First Normal Form): `Atomic Values`
**Goal**: Each column should hold a single, indivisible value. No repeating groups of data within a single cell
**Example**: Instead of having a single `address` column that stores `123 Main St, City, USA`, you'd
break it down into separate columns: `street_address`, `city`, `state`, `zip_code`.
```sql
-- Unnormalized (violates 1NF)
CREATE TABLE "Customers_Unnormalized" (
"customer_id" INTEGER PRIMARY KEY,
"name" VARCHAR(255),
"address" VARCHAR(255) -- Problem: Multiple pieces of info in one column
);
-- Normalized to 1NF
CREATE TABLE "Customers_1NF" (
"customer_id" INTEGER PRIMARY KEY,
"name" VARCHAR(255),
"street_address" VARCHAR(255),
"city" VARCHAR(255),
"state" VARCHAR(255),
"zip_code" VARCHAR(10)
);
```
#### 2NF (Second Normal Form): `Eliminate Redundant Data Dependent on Part of the Key`
**Goal**: Applies when you have a table with a composite primary key (a primary key made up of two or more columns).
2NF ensures that all non-key attributes are fully dependent on the entire composite primary key, not just part of it.
Imagine we have a table called `order_items`. This table tracks items within orders, and we use a composite primary key (`order_id`, `product_id`)
because a single order can have multiple of the same product (though in this simplified example, let's assume each product appears only
once per order for clarity, but the composite key logic still applies).
```sql
CREATE TABLE "OrderItems_Unnormalized" (
"order_id" INTEGER,
"product_id" VARCHAR(10),
"product_name" VARCHAR(100),
"product_price" DECIMAL(10, 2),
"quantity" INTEGER,
"order_date" DATE,
PRIMARY KEY ("order_id", "product_id") -- Composite Primary Key
);
INSERT INTO "OrderItems_Unnormalized" ("order_id", "product_id", "product_name", "product_price", "quantity", "order_date") VALUES
(101, 'A123', 'Laptop', 1200.00, 1, '2023-10-27'),
(101, 'B456', 'Mouse', 25.00, 2, '2023-10-27'),
(102, 'A123', 'Laptop', 1200.00, 1, '2023-10-28'),
(103, 'C789', 'Keyboard', 75.00, 1, '2023-10-29');
```
```
+------------------------------------------------------------------------------------+
| OrderItems_Unnormalized |
+------------------------------------------------------------------------------------+
| PK (order_id, product_id) | product_name | product_price | quantity | order_date |
+------------------------------------------------------------------------------------+
| 101, A123 | Laptop | 1200.00 | 1 | 2023-10-27 |
| 101, B456 | Mouse | 25.00 | 2 | 2023-10-27 |
| 102, A123 | Laptop | 1200.00 | 1 | 2023-10-28 |
| 103, C789 | Keyboard | 75.00 | 1 | 2023-10-29 |
+------------------------------------------------------------------------------------+
```
**Problem**: Notice that `product_name` and `product_price` are repeated whenever the same `product_id` appears in different orders.
These attributes are only dependent on `product_id`, which is part of the composite primary key (`order_id`, `product_id`), but not the entire key.
This is a partial dependency.
To achieve 2NF, we need to remove the partially dependent attributes (`product_name`, `product_price`) and place them in a separate table where they are
fully dependent on the primary key of that new table.
```
+-------------------+ 1:M +---------------------------+
| Products | <---------- | OrderItems_2NF |
+-------------------+ +---------------------------+
| PK product_id | | PK (order_id, product_id) |
| product_name | | quantity |
| product_price | | order_date |
+-------------------+ | FK product_id |
+---------------------------+
```
```sql
CREATE TABLE "Products" (
"product_id" VARCHAR(10) PRIMARY KEY,
"product_name" VARCHAR(100),
"product_price" DECIMAL(10, 2)
);
CREATE TABLE "OrderItems_2NF" (
"order_id" INTEGER,
"product_id" VARCHAR(10),
"quantity" INTEGER,
"order_date" DATE,
PRIMARY KEY ("order_id", "product_id"), -- Composite Primary Key remains
FOREIGN KEY ("product_id") REFERENCES "Products"("product_id") -- Foreign Key to Products
);
-- Insert data into Products
INSERT INTO "Products" ("product_id", "product_name", "product_price") VALUES
('A123', 'Laptop', 1200.00),
('B456', 'Mouse', 25.00),
('C789', 'Keyboard', 75.00);
-- Insert data into OrderItems_2NF (referencing Products)
INSERT INTO "OrderItems_2NF" ("order_id", "product_id", "quantity", "order_date") VALUES
(101, 'A123', 1, '2023-10-27'),
(101, 'B456', 2, '2023-10-27'),
(102, 'A123', 1, '2023-10-28'),
(103, 'C789', 1, '2023-10-29');
```
#### 3NF (Third Normal Form): `Eliminate Redundant Data Dependent on Non-Key Attributes`
**Goal**: Remove data that is dependent on other non-key attributes. This is about eliminating transitive dependencies.
**Problem**: Let's say we have a `suppliers` table. We store supplier information, including their `zip_code`, `city`, and `state`. `supplier_id` is the primary key.
```sql
CREATE TABLE "suppliers" (
"supplier_id" VARCHAR(10) PRIMARY KEY,
"supplier_name" VARCHAR(255),
"zip_code" VARCHAR(10),
"city" VARCHAR(100),
"state" VARCHAR(50)
);
INSERT INTO "suppliers" ("supplier_id", "supplier_name", "zip_code", "city", "state") VALUES
('S1', 'Acme Corp', '12345', 'Anytown', 'NY'),
('S2', 'Beta Inc', '67890', 'Otherville', 'CA'),
('S3', 'Gamma Ltd', '12345', 'Anytown', 'NY');
```
```
+---------------------------------------------------------------+
| suppliers |
+---------------------------------------------------------------+
| PK supplier_id | supplier_name | zip_code | city | state |
+---------------------------------------------------------------+
| S1 | Acme Corp | 12345 | Anytown | NY |
| S2 | Beta Inc | 67890 | Otherville | CA |
| S3 | Gamma Ltd | 12345 | Anytown | NY |
+---------------------------------------------------------------+
```
**Solution**: To achieve 3NF, we remove the attributes dependent on the non-key attribute (`city`, `state` dependent on `zip_code`) and put
them into a separate table keyed by the non-key attribute itself (`zip_code`).
```
+-------------------+ 1:M +--------------------+
| zip_codes | <---------- | suppliers |
+-------------------+ +--------------------+
| PK zip_code | | PK supplier_id |
| city | | supplier_name |
| state | | FK zip_code |
+-------------------+ +--------------------+
```
```sql
CREATE TABLE "zip_codes" (
"zip_code" VARCHAR(10) PRIMARY KEY,
"city" VARCHAR(100),
"state" VARCHAR(50)
);
CREATE TABLE "suppliers" (
"supplier_id" VARCHAR(10) PRIMARY KEY,
"supplier_name" VARCHAR(255),
"zip_code" VARCHAR(10), -- Foreign Key to zip_codes
FOREIGN KEY ("zip_code") REFERENCES "zip_codes"("zip_code")
);
-- Insert data into zip_codes
INSERT INTO "zip_codes" ("zip_code", "city", "state") VALUES
('12345', 'Anytown', 'NY'),
('67890', 'Otherville', 'CA');
-- Insert data into suppliers (referencing zip_codes)
INSERT INTO "suppliers" ("supplier_id", "supplier_name", "zip_code") VALUES
('S1', 'Acme Corp', '12345'),
('S2', 'Beta Inc', '67890'),
('S3', 'Gamma Ltd', '12345');
```
There are additional normal forms, such as `4NF`, `5NF`, `6NF`, `EKNF`, `ETNF`, and `DKNF`. We won't cover these here, but we will create a
dedicated set of tutorials for them in our guides and tutorials section.
### Database Relationships
#### One-to-One
In a one-to-one relationship, each record in `table A` is related to at most one record in `table B`, and each record in `table B` is
related to at most one record in `table A`. It's a very direct, exclusive pairing.
1. **User Profiles and User Account Details**: Think of a website. Each user account (in a Users table) might have exactly one user profile (in a UserProfiles table) containing more detailed information.
2. **Employees and Parking Spaces**: An Employees table and a ParkingSpaces table. Each employee might be assigned at most one parking space, and each parking space is assigned to at most one employee.
3. **Splitting Tables for Organization**: Sometimes, you might split a very wide table into two for better organization or security reasons, maintaining a 1-1 relationship between them.
```
Table A (One Side) Table B (One Side)
+---------+ +---------+
| PK (A) | <---------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
```
#### One-to-Many
In a one-to-many relationship, one record in `table A` can be related to many records in `table B`, but each
record in `table B` is related to at most one record in `table A`. Think of it as a "parent-child" relationship.
1. **Customers and Orders**: One customer can place many orders, but each order belongs to only one customer.
2. **Authors and Books**: One author can write many books, but (let's simplify for now and say) each book is written by one primary author.
3. **Departments and Employees**: One department can have many employees, but each employee belongs to only one department.
```
Table A (One Side) Table B (Many Side)
+---------+ +---------+
| PK (A) | ----------> | FK (A) | (Foreign Key referencing Table A)
| ... | | ... |
+---------+ +---------+
(One) (Many)
```
#### Many-to-Many
In a many-to-many relationship, one record in `table A` can be related to many records in `table B`, and one
record in `table B` can be related to many records in `table A`. It's a more complex, bidirectional relationship.
1. **Students and Courses**: One student can enroll in many courses, and one course can have many students enrolled.
2. **Products and Categories**: One product can belong to multiple categories (e.g., a "T-shirt" can be
in "Clothing" and "Summer Wear" categories), and one category can contain many products.
3. **Authors and Books**: A book can be written by multiple authors, and an author can write multiple books.
```
Table A (Many Side) Junction Table Table B (Many Side)
+---------+ +-------------+ +---------+
| PK (A) | -------->| FK (A) | <----| FK (B) |
| ... | | FK (B) | | ... |
+---------+ +-------------+ +---------+
(Many) (Junction) (Many)
```
Many-to-many relationships are not directly implemented with foreign keys between the two main tables.
Instead, you need a `junction` table (also called an associative table or bridging table).
This table acts as an intermediary to link records from both tables.
```sql
-- Table for Students (Many side)
CREATE TABLE "students" (
"id" INTEGER PRIMARY KEY,
"name" VARCHAR(255)
);
-- Table for Courses (Many side)
CREATE TABLE "courses" (
"id" INTEGER PRIMARY KEY,
"name" VARCHAR(255),
"credits" INTEGER
);
-- Junction Table: Enrollments (Connects Students and Courses - M-M relationship)
CREATE TABLE "enrollments" (
"id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- Optional, but good practice for junction tables
"student_id" INTEGER,
"course_id" INTEGER,
"enrollment_date" DATE,
-- Composite Foreign Keys (often part of a composite primary key or unique constraint)
FOREIGN KEY ("student_id") REFERENCES "students"("id"),
FOREIGN KEY ("course_id") REFERENCES "courses"("id"),
UNIQUE ("student_id", "course_id") -- Prevent duplicate enrollments for the same student and course
);
```
### Why Foreign Keys?
You might think of foreign key constraints as simply a way to validate data - ensuring
that when you enter a value in a foreign key column, that value actually exists in the primary key
column of another table. And you'd be partially right! This value checking is the mechanism foreign keys use.
But it's crucial to understand that this validation is not the end goal, it's the means
to a much larger purpose. Foreign key constraints are fundamentally about:
We've discussed relationships like `One-to-Many` between Customers and Orders.
A foreign key is the SQL language's way of telling the database:
> Hey database, I want to enforce a 1-M relationship here. Every value in the customer_id column of the
Orders table must correspond to a valid customer_id in the Customers table.
It's not just a suggestion; it's a constraint the database actively enforces.
The database becomes relationship-aware because of the foreign key.
- This is the core of "data integrity" in the context of relationships. Referential integrity means
that relationships between tables remain consistent and valid over time.
- Foreign keys prevent orphaned records. What's an orphaned record? In our Customer-Order example,
an order that exists in the Orders table but doesn't have a corresponding customer in the Customers
table would be an orphan. Foreign keys prevent this from happening (or control what happens
if you try to delete a customer with orders - via CASCADE, SET NULL, etc.).
- Why is preventing orphans important? Orphaned records break the logical structure of your data.
If you have an order without a customer, you lose crucial context. Queries become unreliable, reports
become inaccurate, and your application's logic can break down.
**Example**:
```
Without a foreign key, you could accidentally delete a customer from the Customers
table while their orders still exist in the Orders table. Suddenly, you have orders that point to
a customer that no longer exists! A foreign key constraint prevents this data inconsistency.
```
- Foreign keys are not just about technical enforcement; they are also a crucial part of database design documentation.
- When you see a foreign key in a database schema, it immediately tells you:
`Table 'X' is related to Table 'Y' in this way.` It's a clear visual and structural indicator of relationships.
- This makes databases easier to understand, maintain, and evolve over time. New developers can quickly
grasp how different parts of the database are connected.
In essence, foreign key constraints are not just about checking values; they are about:
1. Defining the rules of your data relationships
2. Actively enforcing those rules at the database level
3. Guaranteeing data integrity and consistency within those relationships
4. Making your database more robust, reliable, and understandable
### Why NOT Foreign Keys?
While highly beneficial, there are some scenarios where you might reconsider or use Foreign Keys with caution.
These are typically edge cases and often involve trade-offs.
- **Scenario**: Extremely high-volume transactional systems (e.g., real-time logging, very high-frequency trading
platforms, massive IoT data ingestion).
- **Explanation**: Every time you insert or update data in a table with a foreign key, the database system
needs to perform checks to ensure referential integrity. In extremely high-write scenarios, these
checks can introduce a small but potentially noticeable performance overhead.
- **Scenario**: Systems where data is distributed across multiple database nodes or clusters (common in sharded databases, cloud environments, and microservices).
- **Explanation**: Cross-node foreign keys can introduce significant complexity and performance overhead. Validating referential integrity requires communication between nodes, leading to increased latency. Distributed transactions needed to maintain consistency are also more complex and can be less performant than local transactions. In such architectures, application-level data integrity checks or eventual consistency models might be considered alternatives.
- **Scenario**: Integrating a relational database with older legacy systems or non-relational data stores (e.g., NoSQL, flat files, external APIs).
- **Explanation**: Legacy systems or non-relational data might not consistently adhere to the referential integrity rules enforced by foreign keys. Imposing foreign keys in such scenarios can lead to data import issues, data inconsistencies, and might necessitate complex data transformation or application-level integrity management instead. You might need to carefully evaluate the data quality and consistency of the external sources and potentially rely on application logic or ETL processes to ensure data integrity instead of strictly enforcing foreign keys at the database level.
You can also check out some great explanations from the PlanetScale team in their [article](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints#why-does-planetscale-not-recommend-constraints)
### Polymorphic Relations
Polymorphic relationships are a more advanced concept that allows a single relationship to point to
different types of entities or tables. It's about creating more flexible and adaptable relationships when
you have different kinds of data that share some commonality.
Imagine you have an `activities` log. An activity could be a `comment` a `like` or a `share`.
Each of these `activity` types has different details. Instead of creating separate tables and
relationships for each activity type and the things they relate to, you might use a polymorphic approach.
- **Comments/Reviews**: A "Comment" might be related to different types of content: articles, products, videos, etc.
Instead of having separate article_id, product_id, video_id columns in a Comments table, you can use a
polymorphic relationship.
```
+---------------------+
| **Comments** |
+---------------------+
| PK comment_id |
| commentable_type | ------> [Polymorphic Relationship]
| commentable_id | -------->
| user_id |
| comment_text |
| ... |
+---------------------+
^
|
+---------------------+ +---------------------+ +---------------------+
| **Articles** | | **Products** | | **Videos** |
+---------------------+ +---------------------+ +---------------------+
| PK article_id | | PK product_id | | PK video_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +---------------------+
```
- **Notifications:** A notification could be related to a user, an order, a system event, etc.
```
+----------------------+
| **Notifications** |
+----------------------+
| PK notification_id |
| notifiable_type | ------> [Polymorphic Relationship]
| notifiable_id | -------->
| user_id |
| message |
| ... |
+----------------------+
^
|
+---------------------+ +---------------------+ +-----------------------+
| **Users** | | **Orders** | | **System Events** |
+---------------------+ +---------------------+ +-----------------------+
| PK user_id | | PK order_id | | PK event_id |
| ... | | ... | | ... |
+---------------------+ +---------------------+ +-----------------------+
```
Polymorphic relationships are more complex and are often handled at the application level or
using more advanced database features (depending on the specific database system). Standard SQL doesn't
have direct, built-in support for enforcing polymorphic foreign key constraints in the same way as regular foreign keys.
Source: https://orm.drizzle.team/docs/pg/relations-v1-v2
import Callout from '@mdx/Callout.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import Section from '@mdx/Section.astro';
import Npx from "@mdx/Npx.astro";
import Npm from "@mdx/Npm.astro";
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
# Migrating to Relational Queries version 2
drizzle-orm@rc
drizzle-kit@rc -D
- **Relations Fundamentals** - [read here](/docs/relations-schema-declaration)
- **drizzle-kit pull** - [read here](/docs/drizzle-kit-pull)
Below is the table of contents. Click an item to jump to that section:
- [What is working differently from v1](#what-is-working-differently-from-v1)
- [New features in v2](#what-is-new)
- [How to migrate relations definition from v1 to v2](#how-to-migrate-relations-schema-definition-from-v1-to)
- [How to migrate queries from v1 to v2](#how-to-migrate-queries-from-v1-to-v2)
- [Internal changes(imports, internal types, etc.)](#internal-changes)
### API changes
#### What is working differently from v1
{/* ##### Drizzle Relations schema definition */}
One of the biggest updates were in **Relations Schema definition**
The first difference is that you no longer need to specify `relations` for each table separately in different objects and
then pass them all to `drizzle()` along with your schema. In Relational Queries v2, you now have one dedicated place to
specify all the relations for all the tables you need.
The `r` parameter in the callback provides comprehensive autocomplete
functionality - including all tables from your schema and functions such as `one`, `many`, and `through` - essentially
offering everything you need to specify your relations.
```ts
// relations.ts
import * as schema from "./schema"
import { defineRelations } from "drizzle-orm"
export const relations = defineRelations(schema, (r) => ({
...
}));
```
```ts
// index.ts
import { relations } from "./relations"
import { drizzle } from "drizzle-orm/..."
const db = drizzle(process.env.DATABASE_URL, { relations })
```
##### What is different?
```ts copy
import * as p from 'drizzle-orm/pg-core';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
invitedBy: p.integer('invited_by'),
});
export const posts = p.pgTable('posts', {
id: p.integer().primaryKey(),
content: p.text(),
authorId: p.integer('author_id'),
});
```
**One place for all your relations**
```ts
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
invitee: one(users, {
fields: [users.invitedBy],
references: [users.id],
}),
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
}));
```
You can still separate it into different `parts`, and you can make the parts any size you want
```ts
import { defineRelations, defineRelationsPart } from 'drizzle-orm';
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
and then you can provide it to the db instance
```ts
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
```
**Define `many` without `one`**
In v1, if you wanted only the `many` side of a relationship, you had to specify the `one` side on the other end,
which made for a poor developer experience.
In v2, you can simply use the `many` side without any additional steps
```ts
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.many.posts({
from: r.users.id,
to: r.posts.authorId,
}),
},
}));
```
**New `optional` option**
`optional: false` at the type level makes the `author` key in the `posts` object required.
This should be used when you are certain that this specific entity will always exist.
Was not supported in v1
```ts {9}
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.one.posts({
from: r.users.id,
to: r.posts.authorId,
optional: false,
}),
},
}));
```
**No modes in `drizzle()`**
We found a way to use the same strategy for all MySQL dialects, so there's no need to specify them
```ts
import * as schema from './schema'
const db = drizzle(process.env.DATABASE_URL, { mode: "planetscale", schema });
// or
const db = drizzle(process.env.DATABASE_URL, { mode: "default", schema });
```
```ts
import { relations } from './relations'
const db = drizzle(process.env.DATABASE_URL, { relations });
```
**`from` and `to` upgrades**
We've renamed `fields` to `from` and `references` to `to`, and we made both accept either a single value or an array
```ts
...
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
...
```
```ts
...
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
...
```
```ts
...
author: r.one.users({
from: [r.posts.authorId],
to: [r.users.id],
}),
...
```
**`relationName` -> `alias`**
```ts {8}
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const postsRelation = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
relationName: "author_post",
}),
}));
```
```ts {9}
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "author_post",
}),
},
}));
```
**`custom types` new function**
There is a new [codec](/docs/codecs) function was added to custom types, so you can control how data is mapped
You can find out more [here](/docs/custom-types#ts-doc-for-type-definitions)
```ts
const customBytes = customType<{
data: Buffer;
driverData: Buffer;
jsonData: string;
}>({
dataType: () => "bytea",
codec: "bytea",
});
```
##### What is new?
**`through` for many-to-many relations**
Previously, you would need to query through a junction table and then map it out for every response
You don't need to do it now!
```ts
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.integer().primaryKey(),
name: p.text(),
verified: p.boolean().notNull(),
});
export const groups = p.pgTable("groups", {
id: p.integer().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.pgTable(
"users_to_groups",
{
userId: p
.integer("user_id")
.notNull()
.references(() => users.id),
groupId: p
.integer("group_id")
.notNull()
.references(() => groups.id),
},
(t) => [p.primaryKey({ columns: [t.userId, t.groupId] })]
);
```
```ts
export const usersRelations = relations(users, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, {
fields: [usersToGroups.groupId],
references: [groups.id],
}),
user: one(users, {
fields: [usersToGroups.userId],
references: [users.id],
}),
}));
```
```ts
// Query example
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});
```
```ts
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));
```
```ts
// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});
```
**Predefined filters**
Was not supported in v1
```ts {10-12}
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema,
(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
```
```ts {4}
// Query example: get groups with all verified users
const response = await db.query.groups.findMany({
with: {
verifiedUsers: true,
},
});
```
##### `where` is now object
```ts
const response = db._query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
});
```
```ts
const response = db.query.users.findMany({
where: {
id: 1,
},
});
```
For a complete API Reference please check our [Select Filters docs](/docs/rqb#select-filters)
```ts
// schema.ts
import { integer, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: integer().primaryKey(),
name: text(),
email: text().notNull(),
age: integer(),
createdAt: timestamp("created_at").defaultNow(),
lastLogin: timestamp("last_login"),
subscriptionEnd: timestamp("subscription_end"),
lastActivity: timestamp("last_activity"),
preferences: jsonb(), // JSON column for user settings/preferences
interests: text().array(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [
{ RAW: (table) => sql`LOWER(${table.name}) LIKE 'john%'` },
{ name: { ilike: "jane%" } },
],
},
{
OR: [
{ RAW: (table) => sql`${table.preferences}->>'theme' = 'dark'` },
{ RAW: (table) => sql`${table.preferences}->>'theme' IS NULL` },
],
},
{ RAW: (table) => sql`${table.age} BETWEEN 25 AND 35` },
],
},
});
```
##### `orderBy` is now object
```ts
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});
```
```ts
const response = db.query.users.findMany({
orderBy: { id: "asc" },
});
```
##### Filtering by relations
Was not supported in v1
Example: Get all users whose ID>10 and who have at least one post with content starting with âMâ
```ts
const usersWithPosts = await db.query.usersTable.findMany({
where: {
id: {
gt: 10
},
posts: {
content: {
like: 'M%'
}
}
},
});
```
##### Using offset on related objects
Was not supported in v1
```ts
await db.query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // correct â
limit: 3,
},
},
});
```
#### How to migrate relations schema definition from v1 to v2
##### **Option 1**: Using `drizzle-kit pull`
In new version `drizzle-kit pull` supports pulling `relations.ts` file in a new syntax:
##### Step 1
drizzle-kit pull
#### Step 2
Transfer generated relations code from `drizzle/relations.ts` to the file you are using to specify your relations
```plaintext {4-5}
â đ drizzle
â â đ meta
â â đ migration.sql
â â đ relations.ts âââââââââ
â â đ schema.ts |
â đ src â
â â đ db â
â â â đ relations.ts <ââââââ
â â â đ schema.ts
â â đ index.ts
â âĻ
```
`drizzle/relations.ts` includes an import of all tables from `drizzle/schema.ts`, which looks like this:
```ts
import * as schema from './schema'
```
You may need to change this import to a file where ALL your schema tables are located.
If there are multiple schema files, you can do the following:
```ts
import * as schema1 from './schema1'
import * as schema2 from './schema2'
...
```
#### Step 3
Change drizzle database instance creation and provide `relations` object instead of `schema`
```ts
import * as schema from './schema'
import { drizzle } from 'drizzle-orm/...'
const db = drizzle('', { schema })
```
```ts
// should be imported from a file in Step 2
import { relations } from './relations'
import { drizzle } from 'drizzle-orm/...'
const db = drizzle('', { relations })
```
If you had MySQL dialect, you can remove `mode` from `drizzle()` as long as it's not needed in version 2
##### **Option 2**: Manual migration
If you want to migrate manually, you can check our [Drizzle Relations section](/docs/relations) for the complete API reference and examples of one-to-one, one-to-many, and many-to-many relations.
#### How to migrate queries from v1 to v2
##### Migrate `where` statements
You can check our [Select Filters docs](/docs/rqb#select-filters) to see examples and a complete API reference.
With the new syntax, you can use `AND`, `OR`, `NOT`, and `RAW`, plus all the filtering operators that
were previously available in Relations v1.
**Examples**
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
},
});
```
```sql {8}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
"d0"."age" = 15
```
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
name: 'John'
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
("d0"."age" = 15)
and ("d0"."name" = 'John')
)
```
```ts
const response = await db.query.users.findMany({
where: {
OR: [
{
id: {
gt: 10,
},
},
{
name: {
like: "John%",
},
},
],
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
("d0"."id" > 10)
or ("d0"."name" like 'John%')
)
```
```ts
const response = await db.query.users.findMany({
where: {
NOT: {
id: {
gt: 10,
},
},
name: {
like: "John%",
},
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
(not ("d0"."id" > 10))
and ("d0"."name" like 'John%')
)
```
```ts
// schema.ts
import { integer, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: integer().primaryKey(),
name: text(),
email: text().notNull(),
age: integer(),
createdAt: timestamp("created_at").defaultNow(),
lastLogin: timestamp("last_login"),
subscriptionEnd: timestamp("subscription_end"),
lastActivity: timestamp("last_activity"),
preferences: jsonb(), // JSON column for user settings/preferences
interests: text().array(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [
{ RAW: (table) => sql`LOWER(${table.name}) LIKE 'john%'` },
{ name: { ilike: "jane%" } },
],
},
{
OR: [
{ RAW: (table) => sql`${table.preferences}->>'theme' = 'dark'` },
{ RAW: (table) => sql`${table.preferences}->>'theme' IS NULL` },
],
},
{ RAW: (table) => sql`${table.age} BETWEEN 25 AND 35` },
],
},
});
```
```sql
SELECT
"d0"."id" AS "id",
"d0"."name" AS "name",
"d0"."email" AS "email",
"d0"."age" AS "age",
"d0"."created_at" AS "createdAt",
"d0"."last_login" AS "lastLogin",
"d0"."subscription_end" AS "subscriptionEnd",
"d0"."last_activity" AS "lastActivity",
"d0"."preferences" AS "preferences",
"d0"."interests" AS "interests"
FROM
"users" AS "d0"
WHERE
(
(
(
(LOWER("d0"."name") LIKE 'john%')
OR ("d0"."name" ILIKE 'jane%')
)
)
AND (
(
("d0"."preferences" ->> 'theme' = 'dark')
OR ("d0"."preferences" ->> 'theme' IS NULL)
)
)
AND ("d0"."age" BETWEEN 25 AND 35)
)
```
##### Migrate `orderBy` statements
Order by was simplified to a single object, where you specify the column and the sort direction (`asc` or `desc`)
```ts
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});
```
```ts
const response = db.query.users.findMany({
orderBy: { id: "asc" },
});
```
##### Migrate `many-to-many` queries
Relational Queries v1 had a very complex way of managing many-to-many queries.
You had to use junction tables to query through them explicitly, and then map those tables out, like this:
```ts
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});
```
After upgrading to Relational Queries v2, your many-to-many relation will look like this:
```ts
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));
```
And when you migrate your query, it will become this:
```ts
// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});
```
### Internal changes
1. Every `drizzle` database, `session`, `migrator` and `transaction` instance updated with new generic arguments for RQB v2 queries
**migrator**
```ts
export async function migrate<
TSchema extends Record
>(
db: NodePgDatabase,
config: MigrationConfig,
) {
...
}
```
```ts {1,2}
export async function migrate(
db: NodePgDatabase,
config: MigrationConfig,
) {
...
}
```
**session**
```ts
export class NodePgSession<
TFullSchema extends Record,
TSchema extends V1.TablesRelationalConfig,
> extends PgSession
```
```ts {2,3}
export class NodePgSession<
TRelations extends AnyRelations,
> extends PgAsyncSession
```
**transaction**
```ts
export class NodePgTransaction<
TFullSchema extends Record,
TSchema extends V1.TablesRelationalConfig,
> extends PgTransaction
```
```ts {2,3}
export class NodePgTransaction<
TRelations extends AnyRelations,
> extends PgAsyncTransaction
```
**driver**
```ts
export class NodePgDatabase<
TSchema extends Record = Record,
> extends PgDatabase
```
```ts {2,3}
export class NodePgDatabase<
TRelations extends AnyRelations = EmptyRelations,
> extends PgAsyncDatabase
```
2. Updated `DrizzleConfig` generic with `TRelations` argument and `relations: TRelations` field
```ts
export interface DrizzleConfig<
TSchema extends Record = Record
> {
logger?: boolean | Logger;
schema?: TSchema;
casing?: Casing;
}
```
```ts {2,5}
export interface DrizzleConfig<
TRelationConfigs extends AnyRelations = EmptyRelations,
> {
logger?: boolean | Logger | undefined;
relations?: TRelationConfigs | undefined;
cache?: Cache | undefined;
jit?: boolean | undefined;
}
```
3. The following entities have been removed from `drizzle-orm` and `drizzle-orm/relations`. The original imports now
include new types used by Relational Queries v2, so make sure to update your imports if you intend to use the older types:
- `Relations`
- `TableRelationsKeysOnly`
- `ExtractTableRelationsFromSchema`
- `ExtractRelationsFromTableExtraConfigSchema`
- `getOperators`
- `FindTableByDBName`
- `RelationalSchemaConfig`
- `RelationConfig`
- `extractTablesRelationalConfig`
- `relations`
- `createOne`
- `createMany`
- `NormalizedRelation`
- `normalizeRelation`
- `createTableRelationsHelpers`
- `TableRelationsHelpers`
4. In the same manner, `${dialect}-core/query-builders/query` files were updated with RQB v2's alternatives
```ts
import { RelationalQueryBuilder, PgRelationalQuery } from 'drizzle-orm/pg-core/query-builders/query';
```
Source: https://orm.drizzle.team/docs/pg/relations
import Tab from '@mdx/Tab.astro';
import Tabs from '@mdx/Tabs.astro';
import IsSupportedChipGroup from '@mdx/IsSupportedChipGroup.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import Prerequisites from "@mdx/Prerequisites.astro";
import CodeTab from '@mdx/CodeTab.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import Npm from '@mdx/Npm.astro';
# Drizzle relations
drizzle-orm@rc
drizzle-kit@rc -D
- **Relations Fundamentals** - get familiar with the concepts of foreign key constraints, soft relations, database normalization, etc - [read here](/docs/relations-schema-declaration)
- **Declare schema** - get familiar with how to define drizzle schemas - [read here](/docs/sql-schema-declaration)
- **Database connection** - get familiar with how to connect to database using drizzle - [read here](/docs/get-started-postgresql)
The sole purpose of Drizzle relations is to let you query your relational data in the most simple and concise way:
```ts
import { drizzle } from 'drizzle-orm/...';
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/pg-core';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text().notNull()
});
export const posts = p.pgTable('posts', {
id: p.integer().primaryKey(),
content: p.text().notNull(),
ownerId: p.integer('owner_id'),
});
const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.ownerId,
to: r.users.id,
}),
}
}))
const db = drizzle({ client, relations });
const result = await db.query.posts.findMany({
with: {
author: true,
},
});
```
```ts
[{
id: 10,
content: "My first post!",
ownerId: 14,
author: {
id: 1,
name: "Alex"
}
}]
```
```ts
import { drizzle } from 'drizzle-orm/...';
import { eq } from 'drizzle-orm';
import { posts, users } from './schema';
const db = drizzle({ client });
const res = await db.select()
.from(posts)
.leftJoin(users, eq(posts.ownerId, users.id))
.orderBy(posts.id)
const mappedResult = ...
```
### `one()`
Here is a list of all fields available for `.one()` in drizzle relations
```ts {3-11}
const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.ownerId,
to: r.users.id,
optional: false,
alias: 'custom_name',
where: {
verified: true,
}
}),
}
}))
```
- `author` key is a custom key that appears in the `posts` object when using Drizzle relational queries.
- `r.one.users` defines that `author` will be a single object from the `users` table rather than an array of objects.
- `from: r.posts.ownerId` specifies the table from which we are establishing a soft relation.
In this case, the relation starts from the `ownerId` column in the `posts` table.
- `to: r.users.id` specifies the table to which we are establishing a soft relation.
In this case, the relation points to the `id` column in the `users` table.
- `optional: false` at the type level makes the `author` key in the posts object `required`.
This should be used when you are certain that this specific entity will always exist.
- `alias` is used to add a specific alias to relationships between tables. If you have multiple identical relationships between two tables, you should
differentiate them using `alias`
- `where` condition can be used for polymorphic relations. It fetches relations based on a `where` statement.
For example, in the case above, only `verified authors` will be retrieved. Learn more about polymorphic relations [here](/docs/relations-schema-declaration#polymorphic-relations).
### `many()`
Here is a list of all fields available for `.many()` in drizzle relations
```ts {3-10}
const relations = defineRelations({ users, posts }, (r) => ({
users: {
feed: r.many.posts({
from: r.users.id,
to: r.posts.ownerId,
alias: 'custom_name',
where: {
approved: true,
}
}),
}
}))
```
- `feed` key is a custom key that appears in the `users` object when using Drizzle relational queries.
- `r.many.posts` defines that `feed` will be an array of objects from the `posts` table rather than just an object
- `from: r.users.id` specifies the table from which we are establishing a soft relation.
In this case, the relation starts from the `id` column in the `users` table.
- `to: r.posts.ownerId` specifies the table to which we are establishing a soft relation.
In this case, the relation points to the `ownerId` column in the `posts` table.
- `alias` is used to add a specific alias to relationships between tables. If you have multiple identical relationships between two tables, you should
differentiate them using `alias`
- `where` condition can be used for polymorphic relations. It fetches relations based on a `where` statement.
For example, in the case above, only `approved posts` will be retrieved. Learn more about polymorphic relations [here](/docs/relations-schema-declaration#polymorphic-relations).
## ---
### One-to-one
Drizzle ORM provides you an API to define `one-to-one` relations between tables with the `defineRelations` function.
An example of a `one-to-one` relation between users and users, where a user can invite another (this example uses a self reference):
```typescript copy {10-17}
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = pgTable('users', {
id: integer().primaryKey(),
name: text(),
invitedBy: integer('invited_by'),
});
export const relations = defineRelations({ users }, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
})
}
}));
```
Another example would be a user having a profile information stored in separate table. In this case, because the foreign key is stored in the "profile_info" table, the user relation have neither fields or references. This tells Typescript that `user.profileInfo` is nullable:
```typescript copy {15-22}
import { pgTable, serial, text, integer, jsonb } from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = pgTable('users', {
id: integer().primaryKey(),
name: text(),
});
export const profileInfo = pgTable('profile_info', {
id: serial().primaryKey(),
userId: integer('user_id').references(() => users.id),
metadata: jsonb(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
})
}
}));
const user = await db.query.users.findFirst({ with: { profileInfo: true } });
//____^? type { id: number, name: string | null, profileInfo: { ... } | null }
```
### One-to-many
Drizzle ORM provides you an API to define `one-to-many` relations between tables with `defineRelations` function.
Example of `one-to-many` relation between users and posts they've written:
```typescript copy {15-25}
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = pgTable('users', {
id: integer('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
Now lets add comments to the posts:
```typescript copy {9-14,22,27-32}
...
export const posts = pgTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
});
export const comments = pgTable("comments", {
id: integer().primaryKey(),
text: text(),
authorId: integer("author_id"),
postId: integer("post_id"),
});
export const relations = defineRelations({ users, posts, comments }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
comments: r.many.comments(),
},
users: {
posts: r.many.posts(),
},
comments: {
post: r.one.posts({
from: r.comments.postId,
to: r.posts.id,
}),
},
}));
```
### Many-to-many
Drizzle ORM provides you an API to define `many-to-many` relations between tables through so called `junction` or `join` tables,
they have to be explicitly defined and store associations between related tables.
Example of `many-to-many` relation between users and groups we are using `through` to bypass junction table selection and directly select many `groups` for each `user`.
```typescript copy {27-39}
import { defineRelations } from 'drizzle-orm';
import { integer, pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: integer().primaryKey(),
name: text(),
});
export const groups = pgTable('groups', {
id: integer().primaryKey(),
name: text(),
});
export const usersToGroups = pgTable(
'users_to_groups',
{
userId: integer('user_id')
.notNull()
.references(() => users.id),
groupId: integer('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [primaryKey({ columns: [t.userId, t.groupId] })],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
**Query example:**
```ts
const res = await db.query.users.findMany({
with: {
groups: true
},
});
// response type
type Response = {
id: number;
name: string | null;
groups: {
id: number;
name: string | null;
}[];
}[];
```
### Predefined filters
Predefined `where` statements in Drizzle's relation definitions are a type of polymorphic relations implementation, but it's not fully it. Essentially, they allow you to
connect tables not only by selecting specific columns but also through custom `where` statements. Let's look at some examples:
We can define a relation between `groups` and `users` so that when querying group's users, we only retrieve those whose `verified` column is set to `true`
```ts
import { defineRelations } from "drizzle-orm";
import * as schema from './schema';
export const relations = defineRelations(schema,(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
...
await db.query.groups.findMany({
with: {
verifiedUsers: true,
},
});
```
```ts
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.integer().primaryKey(),
name: p.text().notNull(),
verified: p.boolean().notNull(),
});
export const groups = p.pgTable("groups", {
id: p.integer().primaryKey(),
title: p.text().notNull(),
});
export const usersToGroups = p.pgTable(
"users_to_groups",
{
userId: p
.integer("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
groupId: p
.integer("group_id")
.notNull()
.references(() => groups.id, { onDelete: "cascade" }),
},
(t) => [p.primaryKey({ columns: [t.groupId, t.userId] })]
);
```
You can only specify filters on the target (to) table. So in this example, the where clause will only include columns from the `users` table since we are establishing a relation **TO** users
```ts {7}
export const relations = defineRelations(schema,(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);
```
## ---
### Relations Parts
In a case you need to separate relations config into several parts you can use `defineRelationsPart` helpers
```ts
import { defineRelations, defineRelationsPart } from 'drizzle-orm';
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
and then you can provide it to the db instance
```ts
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
```
There are a few rules you would need to follow to make sure it `defineRelationsParts` works as expected
**Rule 1**: If you specify reltions with parts, when passing it to drizzle db function you would need to specify it in the right order(main relations goes first)
```ts
// â
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })
// â
const db = drizzle(process.env.DB_URL, { relations: { ...part, ...relations } })
```
Even if there will be no type or runtime error, this is how "..." works with objects. As long as main relation
recursively infer all tables names, so it can be available in autocomplete. Here is an example:
```ts
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));
```
Here `relations` and `part` can be represented and this object:
```json
// relations
{
"users": {"invitee": {...}, "posts": {...}},
// added here, so all tables from schema will exist in autocomplete
"posts": {}
}
// part
{
"posts": {"author": {...}}
}
```
Having `{ ...relations, ...part }` will result in
```json
{
"users": {"invitee": {...}, "posts": {...}},
"posts": {"author": {...}}
}
```
and having `{ ...relations, ...part }` will result in
```json
{
"users": {"invitee": {...}, "posts": {...}},
// As you can see in the final object, posts relations information will be lost
"posts": {}
}
```
**Rule 2**: You should have min relations, so drizzle can infer all of the table for autocomplete. If you want to have only parts, then
one of your parts should be empty, like this:
```ts
export const mainPart = defineRelationsPart(schema);
```
In this case, all tables will be inferred correctly, and you'll have complete information about your schema
## ---
### Performance
When working with relations in Drizzle ORM, especially in applications with
significant data or complex queries, optimizing database performance is crucial.
Indexes play a vital role in speeding up data retrieval, particularly when querying
related data. This section outlines recommended indexing strategies for each type
of relationship defined using Drizzle ORM.
##### One-to-one Relationships
In a one-to-one relationship, like the "user invites user" example or the
"user has profile info" example, the key performance consideration is efficient joining
of the related tables.
For optimal performance in one-to-one relationships, you should create an index
on the foreign key column in the table that is being referenced
(the "target" table in the relation).
When you query data with related one-to-one information, Drizzle performs a JOIN operation. An index on the foreign key column allows the database to quickly
locate the related row in the target table, significantly speeding up the join process.
**Example:**
```typescript
import * as p from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const profileInfo = p.pgTable('profile_info', {
id: p.integer().primaryKey(),
userId: p.integer('user_id').references(() => users.id),
metadata: p.jsonb(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
})
}
}));
```
To optimize queries fetching user data along with their profile information,
you should create an index on the `userId` column in the `profile_info` table.
```typescript {13-15}
import * as p from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const profileInfo = p.pgTable('profile_info', {
id: p.integer().primaryKey(),
userId: p.integer('user_id').references(() => users.id),
metadata: p.jsonb(),
}, (table) => [
p.index('profile_info_user_id_idx').on(table.userId)
]);
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
})
}
}));
```
```sql
CREATE INDEX "profile_info_user_id_idx" ON "profile_info" ("user_id");
```
#### One-to-many Relationships
Similar to one-to-one relationships, one-to-many relations benefit significantly
from indexing to optimize join operations. Consider the "users and posts" example where one user can have many posts.
For one-to-many relationships, create an index on the foreign key column in the table that represents the "many" side of the relationship (the table with the foreign key referencing the "one" side).
When you fetch a user with their posts or posts with their authors, joins are performed.
Indexing the foreign key (`authorId` in `posts` table) allows the database to efficiently
retrieve all posts associated with a given user or quickly find the author of a post.
**Example:**
```typescript
import * as p from "drizzle-orm/pg-core";
import { defineRelations } from 'drizzle-orm';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const posts = p.pgTable('posts', {
id: p.integer().primaryKey(),
content: p.text(),
authorId: p.integer('author_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
To optimize queries involving users and their posts, create an index on the `authorId` column in the `posts` table.
```typescript {13-15}
import * as p from "drizzle-orm/pg-core";
import { defineRelations } from 'drizzle-orm';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const posts = p.pgTable('posts', {
id: p.integer().primaryKey(),
content: p.text(),
authorId: p.integer('author_id'),
}, (t) => [
p.index('posts_author_id_idx').on(t.authorId)
]);
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
```sql
CREATE INDEX "posts_author_id_idx" ON "posts" ("author_id");
```
#### Many-to-many Relationships
Many-to-many relationships, implemented using junction tables, require a slightly
more nuanced indexing strategy to ensure optimal query performance.
Consider the "users and groups" example with the `usersToGroups` junction table.
For many-to-many relationships, it is generally recommended to create the following
indexes on the junction table:
1. **Index on each foreign key column individually:** This optimizes queries that
filter or join based on a single side of the relationship
(e.g., finding all groups for a user OR all users in a group).
2. **Composite index on both foreign key columns together:** This is crucial for
efficiently resolving the many-to-many relationship itself. It speeds up queries that need to find the connections between both entities.
When querying many-to-many relations, especially when using `through` in Drizzle ORM, the database needs to efficiently navigate the junction table.
- Indexes on individual foreign key columns (`userId`, `groupId` in `usersToGroups`) help when you are querying from one side to find the other (e.g., "find groups for a user").
- The composite index on `(userId, groupId)` in `usersToGroups` is particularly important for quickly finding all relationships defined in the junction table. This is used when Drizzle ORM resolves the `many-to-many` relation to fetch related entities.
**Example:**
In the "users and groups" example, the `usersToGroups` junction table connects `users` and `groups`.
```typescript
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/pg-core';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const groups = p.pgTable('groups', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.pgTable(
'users_to_groups',
{
userId: p.integer('user_id')
.notNull()
.references(() => users.id),
groupId: p.integer('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [p.primaryKey({ columns: [t.userId, t.groupId] })],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
To optimize queries for users and groups, create indexes on `usersToGroups` table as follows:
```typescript {26-28}
import { defineRelations } from 'drizzle-orm';
import * as p from 'drizzle-orm/pg-core';
export const users = p.pgTable('users', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const groups = p.pgTable('groups', {
id: p.integer().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.pgTable(
'users_to_groups',
{
userId: p.integer('user_id')
.notNull()
.references(() => users.id),
groupId: p.integer('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [
p.primaryKey({ columns: [t.userId, t.groupId] }),
p.index('users_to_groups_user_id_idx').on(t.userId),
p.index('users_to_groups_group_id_idx').on(t.groupId),
p.index('users_to_groups_composite_idx').on(t.userId, t.groupId),
],
);
export const relations = defineRelations({ users, groups, usersToGroups },
(r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
})
);
```
```sql
CREATE INDEX "users_to_groups_user_id_idx" ON "users_to_groups" ("user_id");
CREATE INDEX "users_to_groups_group_id_idx" ON "users_to_groups" ("group_id");
CREATE INDEX "users_to_groups_composite_idx" ON "users_to_groups" ("user_id","group_id");
```
By applying these indexing strategies, you can significantly improve the performance of your Drizzle ORM applications when working with relational data, especially as your data volume grows and your queries become more complex. Remember to choose the indexes that best suit your specific query patterns and application needs.
## ---
### Foreign keys
You might've noticed that `relations` look similar to foreign keys â they even have a `references` property. So what's the difference?
While foreign keys serve a similar purpose, defining relations between tables, they work on a different level compared to `relations`.
Foreign keys are a database level constraint, they are checked on every `insert`/`update`/`delete` operation and throw an error if a constraint is violated.
On the other hand, `relations` are a higher level abstraction, they are used to define relations between tables on the application level only.
They do not affect the database schema in any way and do not create foreign keys implicitly.
What this means is `relations` and foreign keys can be used together, but they are not dependent on each other.
You can define `relations` without using foreign keys (and vice versa), which allows them to be used with databases that do not support foreign keys.
The following two examples will work exactly the same in terms of querying the data using Drizzle relational queries.
```ts {15}
export const users = p.pgTable("users", {
id: p.integer().primaryKey(),
name: p.text(),
});
export const profileInfo = p.pgTable("profile_info", {
id: p.integer().primaryKey(),
userId: p.integer("user_id"),
metadata: p.jsonb(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
}),
},
}));
```
```ts {15}
export const users = p.pgTable("users", {
id: p.integer().primaryKey(),
name: p.text(),
});
export const profileInfo = p.pgTable("profile_info", {
id: p.integer().primaryKey(),
userId: p.integer("user_id").references(() => users.id),
metadata: p.jsonb(),
});
export const relations = defineRelations({ users, profileInfo }, (r) => ({
users: {
profileInfo: r.one.profileInfo({
from: r.users.id,
to: r.profileInfo.userId,
}),
},
}));
```
### Disambiguating relations
Drizzle also provides the `alias` option as a way to disambiguate
relations when you define multiple of them between the same two tables. For
example, if you define a `posts` table that has the `author` and `reviewer`
relations.
```ts {19,22,29,34}
import { pgTable, integer, text } from 'drizzle-orm/pg-core';
import { defineRelations } from 'drizzle-orm';
export const users = pgTable('users', {
id: integer('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
reviewerId: integer('reviewer_id'),
});
export const relations = defineRelations({ users, posts }, (r) => ({
users: {
posts: r.many.posts({
alias: "author",
}),
reviewedPosts: r.many.posts({
alias: "reviewer",
}),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "author",
}),
reviewer: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "reviewer",
}),
},
}));
```
Source: https://orm.drizzle.team/docs/pg/rls
import Callout from '@mdx/Callout.astro';
# Row-Level Security (RLS)
With Drizzle, you can enable Row-Level Security (RLS) for any Postgres table, create policies with various options, and define and manage the roles those policies apply to.
Drizzle supports a raw representation of Postgres policies and roles that can be used in any way you want. This works with popular Postgres database providers such as `Neon` and `Supabase`.
In Drizzle, we have specific predefined RLS roles and functions for RLS with both database providers, but you can also define your own logic.
## Enable RLS
If you just want to enable RLS on a table without adding policies, you can use `pgTable.withRLS(...)`
As mentioned in the PostgreSQL documentation:
> If no policy exists for the table, a default-deny policy is used, meaning that no rows are visible or can be modified.
Operations that apply to the whole table, such as TRUNCATE and REFERENCES, are not subject to row security.
```ts
import { integer, pgTable } from 'drizzle-orm/pg-core';
export const users = pgTable.withRLS('users', {
id: integer(),
});
```
If you add a policy to a table, RLS will be enabled automatically. So, there's no need to explicitly enable RLS when adding policies to a table.
## Roles
Currently, Drizzle supports defining roles with a few different options, as shown below. Support for more options will be added in a future release.
```ts
import { pgRole } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin', { createRole: true, createDb: true, inherit: true });
```
If a role already exists in your database, and you don't want drizzle-kit to 'see' it or include it in migrations, you can mark the role as existing.
```ts
import { pgRole } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin').existing();
```
## Policies
To fully leverage RLS, you can define policies within a Drizzle table.
In PostgreSQL, policies should be linked to an existing table. Since policies are always associated with a specific table, we decided that policy definitions should be defined as a parameter of `pgTable`
**Example of pgPolicy with all available properties**
```ts
import { sql } from 'drizzle-orm';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy('policy', {
as: 'permissive',
to: admin,
for: 'delete',
using: sql``,
withCheck: sql``,
}),
]);
```
**Policy options**
| | |
| :----------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| `as` | Possible values are `permissive` or `restrictive` |
| `to` | Specifies the role to which the policy applies. Possible values include `public`, `current_role`, `current_user`, `session_user`, or any other role name as a string. You can also reference a `pgRole` object. |
| `for` | Defines the commands this policy will be applied to. Possible values are `all`, `select`, `insert`, `update`, `delete`. |
| `using` | The SQL statement that will be applied to the `USING` part of the policy creation statement. |
| `withCheck` | An SQL statement that will be applied to the `WITH CHECK` part of the policy creation statement. |
**Link Policy to an existing table**
There are situations where you need to link a policy to an existing table in your database.
The most common use case is with database providers like `Neon` or `Supabase`, where you need to add a policy
to their existing tables. In this case, you can use the `.link()` API
```ts
import { sql } from "drizzle-orm";
import { pgPolicy } from "drizzle-orm/pg-core";
import { authenticatedRole, realtimeMessages } from "drizzle-orm/supabase";
export const policy = pgPolicy("authenticated role insert policy", {
for: "insert",
to: authenticatedRole,
using: sql``,
}).link(realtimeMessages);
```
{/*
*/}
## Migrations
If you are using drizzle-kit to manage your schema and roles, there may be situations where you want to refer to roles that are not defined in your Drizzle schema. In such cases, you may want drizzle-kit to skip managing these roles without having to define each role in your drizzle schema and marking it with `.existing()`.
In these cases, you can use `entities.roles` in `drizzle.config.ts`. For a complete reference, refer to the the [drizzle.config.ts](/docs/drizzle-config-file) documentation.
By default, `drizzle-kit` does not manage roles for you, so you will need to enable this feature in `drizzle.config.ts`.
```ts {12-14}
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'postgresql',
schema: "./drizzle/schema.ts",
dbCredentials: {
url: process.env.DATABASE_URL!
},
verbose: true,
strict: true,
entities: {
roles: true
}
});
```
In case you need additional configuration options, let's take a look at a few more examples.
**You have an `admin` role 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 an `admin` role and want to include it in 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 Neon-defined roles, 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 Supabase-defined roles, 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 your database provider.
In such cases, you can use the `provider` option and `exclude` additional roles:
```ts
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
...
entities: {
roles: {
provider: 'supabase',
exclude: ['new_supabase_role']
}
}
});
```
## RLS on views
With Drizzle, you can also specify RLS policies on views. For this, you need to use `security_invoker` in the view's WITH options. Here is a small example:
```ts {5}
...
export const roomsUsersProfiles = pgView("rooms_users_profiles")
.with({
securityInvoker: true,
})
.as((qb) =>
qb
.select({
...getColumns(roomsUsers),
email: profiles.email,
})
.from(roomsUsers)
.innerJoin(profiles, eq(roomsUsers.userId, profiles.id))
);
```
## Using with Neon
The Neon Team helped us implement their vision of a wrapper on top of our raw policies API. We defined a specific
`/neon` import with the `crudPolicy` function that includes predefined functions and Neon's default roles.
Here's an example of how to use the `crudPolicy` function:
```ts
import { crudPolicy } from 'drizzle-orm/neon';
import { integer, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
crudPolicy({ role: admin, read: true, modify: false }),
]);
```
This policy is equivalent to:
```ts
import { sql } from 'drizzle-orm';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`crud-${admin.name}-policy-insert`, {
for: 'insert',
to: admin,
withCheck: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-update`, {
for: 'update',
to: admin,
using: sql`false`,
withCheck: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-delete`, {
for: 'delete',
to: admin,
using: sql`false`,
}),
pgPolicy(`crud-${admin.name}-policy-select`, {
for: 'select',
to: admin,
using: sql`true`,
}),
]);
```
`Neon` exposes predefined `authenticated` and `anaonymous` roles and related functions. If you are using `Neon` for RLS, you can use these roles, which are marked as existing, and the related functions in your RLS queries.
```ts
// drizzle-orm/neon
export const authenticatedRole = pgRole('authenticated').existing();
export const anonymousRole = pgRole('anonymous').existing();
export const authUid = (userIdColumn: AnyPgColumn) => sql`(select auth.user_id() = ${userIdColumn})`;
export const neonIdentitySchema = pgSchema('neon_identity');
export const usersSync = neonIdentitySchema.table('users_sync', {
rawJson: jsonb('raw_json').notNull(),
id: text().primaryKey().notNull(),
name: text(),
email: text(),
createdAt: timestamp('created_at', { withTimezone: true, mode: 'string' }),
deletedAt: timestamp('deleted_at', { withTimezone: true, mode: 'string' }),
});
```
For example, you can use the `Neon` predefined roles and functions like this:
```ts
import { sql } from 'drizzle-orm';
import { authenticatedRole } from 'drizzle-orm/neon';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`policy-insert`, {
for: 'insert',
to: authenticatedRole,
withCheck: sql`false`,
}),
]);
```
## Using with Supabase
We also have a `/supabase` import with a set of predefined roles marked as existing, which you can use in your schema.
This import will be extended in a future release with more functions and helpers to make using RLS and `Supabase` simpler.
```ts
// drizzle-orm/supabase
export const anonRole = pgRole('anon').existing();
export const authenticatedRole = pgRole('authenticated').existing();
export const serviceRole = pgRole('service_role').existing();
export const postgresRole = pgRole('postgres_role').existing();
export const supabaseAuthAdminRole = pgRole('supabase_auth_admin').existing();
```
For example, you can use the `Supabase` predefined roles like this:
```ts
import { sql } from 'drizzle-orm';
import { serviceRole } from 'drizzle-orm/supabase';
import { integer, pgPolicy, pgRole, pgTable } from 'drizzle-orm/pg-core';
export const admin = pgRole('admin');
export const users = pgTable('users', {
id: integer(),
}, (t) => [
pgPolicy(`policy-insert`, {
for: 'insert',
to: serviceRole,
withCheck: sql`false`,
}),
]);
```
The `/supabase` import also includes predefined tables and functions that you can use in your application
```ts
// drizzle-orm/supabase
const auth = pgSchema('auth');
export const authUsers = auth.table('users', {
id: uuid().primaryKey().notNull(),
});
const realtime = pgSchema('realtime');
export const realtimeMessages = realtime.table(
'messages',
{
id: bigserial({ mode: 'bigint' }).primaryKey(),
topic: text().notNull(),
extension: text({
enum: ['presence', 'broadcast', 'postgres_changes'],
}).notNull(),
},
);
export const authUid = sql`(select auth.uid())`;
export const realtimeTopic = sql`realtime.topic()`;
```
This allows you to use it in your code, and Drizzle Kit will treat them as existing databases,
using them only as information to connect to other entities
```ts
import { foreignKey, pgPolicy, pgTable, text, uuid } from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm/sql";
import { authenticatedRole, authUsers } from "drizzle-orm/supabase";
export const profiles = pgTable(
"profiles",
{
id: uuid().primaryKey().notNull(),
email: text().notNull(),
},
(table) => [
foreignKey({
columns: [table.id],
// reference to the auth table from Supabase
foreignColumns: [authUsers.id],
name: "profiles_id_fk",
}).onDelete("cascade"),
pgPolicy("authenticated can view all profiles", {
for: "select",
// using predefined role from Supabase
to: authenticatedRole,
using: sql`true`,
}),
]
);
```
Let's check an example of adding a policy to a table that exists in `Supabase`
```ts
import { sql } from "drizzle-orm";
import { pgPolicy } from "drizzle-orm/pg-core";
import { authenticatedRole, realtimeMessages } from "drizzle-orm/supabase";
export const policy = pgPolicy("authenticated role insert policy", {
for: "insert",
to: authenticatedRole,
using: sql``,
}).link(realtimeMessages);
```
We also have a great example showcasing how to use Drizzle RLS with Supabase and how to make actual queries with it.
It also includes a great wrapper, `createDrizzle`, that can handle all the transactional work with Supabase for you.
In upcoming releases, it will be moved to drizzle-orm/supabase, allowing you to use it natively
Please check [Drizzle SupaSecureSlack repo](https://github.com/rphlmr/drizzle-supabase-rls)
Here is an example of an implementation from this repository
```ts
type SupabaseToken = {
iss?: string;
sub?: string;
aud?: string[] | string;
exp?: number;
nbf?: number;
iat?: number;
jti?: string;
role?: string;
};
export function createDrizzle(token: SupabaseToken, { admin, client }: { admin: PgDatabase; client: PgDatabase }) {
return {
admin,
rls: (async (transaction, ...rest) => {
return await client.transaction(async (tx) => {
// Supabase exposes auth.uid() and auth.jwt()
// https://supabase.com/docs/guides/database/postgres/row-level-security#helper-functions
try {
await tx.execute(sql`
-- auth.jwt()
select set_config('request.jwt.claims', '${sql.raw(
JSON.stringify(token)
)}', TRUE);
-- auth.uid()
select set_config('request.jwt.claim.sub', '${sql.raw(
token.sub ?? ""
)}', TRUE);
-- set local role
set local role ${sql.raw(token.role ?? "anon")};
`);
return await transaction(tx);
} finally {
await tx.execute(sql`
-- reset
select set_config('request.jwt.claims', NULL, TRUE);
select set_config('request.jwt.claim.sub', NULL, TRUE);
reset role;
`);
}
}, ...rest);
}) as typeof client.transaction,
};
}
```
And it can be used as
```ts
// https://github.com/orgs/supabase/discussions/23224
// Should be secure because we use the access token that is signed, and not the data read directly from the storage
export async function createDrizzleSupabaseClient() {
const {
data: { session },
} = await createClient().auth.getSession();
return createDrizzle(decode(session?.access_token ?? ""), { admin, client });
}
async function getRooms() {
const db = await createDrizzleSupabaseClient();
return db.rls((tx) => tx.select().from(rooms));
}
```
Source: https://orm.drizzle.team/docs/pg/rqb
import Npm from '@mdx/Npm.astro';
import Callout from '@mdx/Callout.astro';
import CodeTabs from '@mdx/CodeTabs.astro';
import CodeTab from '@mdx/CodeTab.astro';
import Section from '@mdx/Section.astro';
# Drizzle Queries
Drizzle ORM is designed to be a thin typed layer on top of SQL.
We truly believe we've designed the best way to operate an SQL database from TypeScript and it's time to make it better.
Relational queries are meant to provide you with a great developer experience for querying
nested relational data from an SQL database, avoiding multiple joins and complex data mappings.
It is an extension to the existing schema definition and query builder.
You can opt-in to use it based on your needs.
We've made sure you have both the best-in-class developer experience and performance.
Inside relational queries, references to a table's columns must go through the callback parameter, not through the imported table object.
This applies to every clause that accepts a callback â `orderBy`, `where`.`RAW`, `extras` and `subqueries inside extras`.
The callback exposes the aliased table for the current query scope, which is required for correct SQL generation in nested or self-referential queries.
```ts
await db.query.posts.findMany({
orderBy: sql`${posts.id} asc`, // <- â direct table usage
with: {
comments: {
orderBy: sql`${comments.id} desc`, // <- â direct table usage
},
},
});
```
```ts
await db.query.users.findMany({
where: {
RAW: sql`LOWER(${users.name}) LIKE 'john%'`, // <- â direct table usage
},
});
```
```ts
await db.query.users.findMany({
extras: {
loweredName: sql`lower(${users.name})`, // <- â direct table usage
totalPostsCount: db.$count(posts, eq(posts.authorId, users.id)), // <- â direct table usage
},
});
```
```ts
await db.query.posts.findMany({
orderBy: (t) => sql`${t.id} asc`, // <- â
callback used
with: {
comments: {
orderBy: (t, { desc }) => desc(t.id), // <- â
callback used
},
},
});
```
```ts
await db.query.users.findMany({
where: {
RAW: (t) => sql`LOWER(${t.name}) LIKE 'john%'`, // <- â
callback used
},
});
```
```ts
await db.query.users.findMany({
extras: {
loweredName: (t, { sql }) => sql`lower(${t.name})`, // <- â
callback used
totalPostsCount: (t) => db.$count(posts, eq(posts.authorId, t.id)), // <- â
callback used
},
});
```
`drizzle` import path depends on the **[database driver](/docs/connect-overview)** you're using.
```typescript copy
import { relations } from "./relations";
import { drizzle } from "drizzle-orm/...";
const db = drizzle(process.env.DATABASE_URL, { relations });
const result = await db.query.users.findMany({
with: {
posts: true,
},
});
```
```ts
[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]
```
```typescript copy
import * as p from "drizzle-orm/pg-core";
export const posts = p.pgTable("posts", {
id: p.integer().primaryKey(),
content: p.text().notNull(),
authorId: p.integer("author_id").notNull(),
});
export const users = p.pgTable("users", {
id: p.integer().primaryKey(),
name: p.text().notNull(),
});
```
```typescript copy
import { defineRelations } from "drizzle-orm";
import { posts, users } from "./schema";
export const relations = defineRelations({ users, posts }, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
users: {
posts: r.many.posts(),
},
}));
```
Relational queries are an extension to Drizzle's original **[query builder](/docs/select)**.
You need to provide all `tables` and `relations` from your schema file/files upon `drizzle()`
initialization and then just use the `db.query` API.
`drizzle` import path depends on the **[database driver](/docs/connect-overview)** you're using.
```ts
import { relations } from './relations';
import { drizzle } from 'drizzle-orm/...';
const db = drizzle(process.env.DATABASE_URL,{ relations });
await db.query.users.findMany(...);
```
```typescript copy
import { type AnyPgColumn, integer, pgTable, primaryKey, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: integer().primaryKey(),
name: text().notNull(),
invitedBy: integer('invited_by').references((): AnyPgColumn => users.id),
});
export const groups = pgTable('groups', {
id: integer().primaryKey(),
name: text().notNull(),
description: text(),
});
export const usersToGroups = pgTable('users_to_groups', {
id: integer().primaryKey(),
userId: integer('user_id').notNull().references(() => users.id),
groupId: integer('group_id').notNull().references(() => groups.id),
}, (t) => [
primaryKey({ columns: [t.userId, t.groupId] })
]);
export const posts = pgTable('posts', {
id: integer().primaryKey(),
content: text().notNull(),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
export const comments = pgTable('comments', {
id: integer().primaryKey(),
content: text().notNull(),
creator: integer().references(() => users.id),
postId: integer('post_id').references(() => posts.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
export const commentLikes = pgTable('comment_likes', {
id: integer().primaryKey(),
creator: integer().references(() => users.id),
commentId: integer('comment_id').references(() => comments.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
```
```typescript copy
import { defineRelations } from 'drizzle-orm';
import * as schema from './schema';
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
posts: r.many.posts(),
},
groups: {
users: r.many.users(),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
comments: r.many.comments(),
},
comments: {
post: r.one.posts({
from: r.comments.postId,
to: r.posts.id,
}),
author: r.one.users({
from: r.comments.creator,
to: r.users.id,
}),
likes: r.many.commentLikes(),
},
commentLikes: {
comment: r.one.comments({
from: r.commentLikes.commentId,
to: r.comments.id,
}),
author: r.one.users({
from: r.commentLikes.creator,
to: r.users.id,
}),
},
})
);
```
Drizzle provides `.findMany()` and `.findFirst()` APIs.
### Find many
```typescript copy
const users = await db.query.users.findMany();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
}[];
```
### Find first
`.findFirst()` will add `limit 1` to the query.
```typescript copy
const user = await db.query.users.findFirst();
```
```ts
// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
};
```
### Include relations
`With` operator lets you combine data from multiple related tables and properly aggregate results.
**Getting all posts with comments:**
```typescript copy
const posts = await db.query.posts.findMany({
with: {
comments: true,
},
});
```
**Getting first post with comments:**
```typescript copy
const post = await db.query.posts.findFirst({
with: {
comments: true,
},
});
```
You can chain nested with statements as much as necessary.
For any nested `with` queries Drizzle will infer types using [Core Type API](/docs/goodies#type-api).
**Get all users with posts. Each post should contain a list of comments:**
```typescript copy
const users = await db.query.users.findMany({
with: {
posts: {
with: {
comments: true,
},
},
},
});
```
### Partial fields select
`columns` parameter lets you include or omit columns you want to get from the database.
Drizzle performs partial selects on the query level, no additional data is transferred from the database.
Keep in mind that **a single SQL statement is outputted by Drizzle.**
**Get all posts with just `id`, `content` and include `comments`:**
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});
```
**Get all posts without `content`:**
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
content: false,
},
});
```
When both `true` and `false` select options are present, all `false` options are ignored.
If you include the `name` field and exclude the `id` field, `id` exclusion will be redundant,
all fields apart from `name` would be excluded anyways.
**Exclude and Include fields in the same query:**
```typescript copy
const users = await db.query.users.findMany({
columns: {
name: true,
id: false //ignored
},
});
```
```ts
// result type
const users: {
name: string;
};
```
**Only include columns from nested relations:**
```typescript copy
const res = await db.query.users.findMany({
columns: {},
with: {
posts: true
}
});
```
```ts
// result type
const res: {
posts: {
id: number,
text: string
}
}[];
```
### Nested partial fields select
Just like with **[`partial select`](#partial-select)**, you can include or exclude columns of nested relations:
```typescript copy
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});
```
### Select filters
Just like in our SQL-like query builder,
relational queries API lets you define filters and conditions with the list of our **[`operators`](/docs/operators)**.
You can either import them from `drizzle-orm` or use from the callback syntax:
```typescript copy
const users = await db.query.users.findMany({
where: {
id: 1
}
});
```
```sql {7}
select
"d0"."id" as "id",
"d0"."name" as "name"
from
"users" as "d0"
where
"d0"."id" = 1
```
Find post with `id=1` and comments that were created before particular date:
```typescript copy
await db.query.posts.findMany({
where: {
id: 1,
},
with: {
comments: {
where: {
createdAt: { lt: new Date() },
},
},
},
});
```
**List of all filter operators**
```ts
where: {
OR: [],
AND: [],
NOT: {},
RAW: (table) => sql`${table.id} = 1`,
// filter by relations
[relation]: {},
// filter by columns
[column]: {
OR: [],
AND: [],
NOT: {},
eq: 1,
ne: 1,
gt: 1,
gte: 1,
lt: 1,
lte: 1,
in: [1],
notIn: [1],
like: "",
ilike: "",
notLike: "",
notIlike: "",
isNull: true,
isNotNull: true,
arrayOverlaps: [1, 2],
arrayContained: [1, 2],
arrayContains: [1, 2]
},
},
```
**Examples**
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
},
});
```
```sql {8}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
"d0"."age" = 15
```
```ts
const response = await db.query.users.findMany({
where: {
age: 15,
name: 'John'
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
("d0"."age" = 15)
and ("d0"."name" = 'John')
)
```
```ts
const response = await db.query.users.findMany({
where: {
OR: [
{
id: {
gt: 10,
},
},
{
name: {
like: "John%",
},
},
],
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
("d0"."id" > 10)
or ("d0"."name" like 'John%')
)
```
```ts
const response = await db.query.users.findMany({
where: {
NOT: {
id: {
gt: 10,
},
},
name: {
like: "John%",
},
},
});
```
```sql {9-10}
select
"d0"."id" as "id",
"d0"."name" as "name",
"d0"."age" as "age"
from
"users" as "d0"
where
(
(not ("d0"."id" > 10))
and ("d0"."name" like 'John%')
)
```
```ts
// schema.ts
import { integer, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm";
export const users = pgTable("users", {
id: integer("id").primaryKey(),
name: text("name"),
email: text("email").notNull(),
age: integer("age"),
createdAt: timestamp("created_at").defaultNow(),
lastLogin: timestamp("last_login"),
subscriptionEnd: timestamp("subscription_end"),
lastActivity: timestamp("last_activity"),
preferences: jsonb("preferences"), // JSON column for user settings/preferences
interests: text("interests").array(), // Array column for user interests
});
```
```ts
const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [
{ RAW: (table) => sql`LOWER(${table.name}) LIKE 'john%'` },
{ name: { ilike: "jane%" } },
],
},
{
OR: [
{ RAW: (table) => sql`${table.preferences}->>'theme' = 'dark'` },
{ RAW: (table) => sql`${table.preferences}->>'theme' IS NULL` },
],
},
{ RAW: (table) => sql`${table.age} BETWEEN 25 AND 35` },
],
},
});
```
```sql
SELECT
"d0"."id" AS "id",
"d0"."name" AS "name",
"d0"."email" AS "email",
"d0"."age" AS "age",
"d0"."created_at" AS "createdAt",
"d0"."last_login" AS "lastLogin",
"d0"."subscription_end" AS "subscriptionEnd",
"d0"."last_activity" AS "lastActivity",
"d0"."preferences" AS "preferences",
"d0"."interests" AS "interests"
FROM
"users" AS "d0"
WHERE
(
(
(
(LOWER("d0"."name") LIKE 'john%')
OR ("d0"."name" ILIKE 'jane%')
)
)
AND (
(
("d0"."preferences" ->> 'theme' = 'dark')
OR ("d0"."preferences" ->> 'theme' IS NULL)
)
)
AND ("d0"."age" BETWEEN 25 AND 35)
)
```
### Relations Filters
With Drizzle Relations, you can filter not only by the table you're querying but also by any table you include in the query.
**Example:** Get all `users` whose ID>10 and who have at least one post with content starting with "M"
```ts
const usersWithPosts = await db.query.usersTable.findMany({
where: {
id: {
gt: 10
},
posts: {
content: {
like: 'M%'
}
}
},
});
```
**Example:** Get all `users` with posts, only if user has at least 1 post
```ts
const response = await db.query.users.findMany({
with: {
posts: true,
},
where: {
posts: true,
},
});
```
### Limit & Offset
Drizzle ORM provides `limit` & `offset` API for queries and for the nested entities.
**Find 5 posts:**
```typescript copy
await db.query.posts.findMany({
limit: 5,
});
```
**Find posts and get 3 comments at most:**
```typescript copy
await db.query.posts.findMany({
with: {
comments: {
limit: 3,
},
},
});
```
`offset` now can be used in with tables as well!
```typescript
await db.query.posts.findMany({
limit: 5,
offset: 2, // correct â
with: {
comments: {
offset: 3, // correct â
limit: 3,
},
},
});
```
Find posts with comments from the 6th to the 10th post:
```typescript copy
await db.query.posts.findMany({
with: {
comments: true,
},
limit: 5,
offset: 5,
});
```
### Order By
Drizzle provides API for ordering in the relational query builder.
You can use same ordering **[core API](/docs/select#order-by)** or use
`order by` operator from the callback with no imports.
When you use multiple `orderBy` statements in the same table, they will be included in the query in the same order in which you added them
```typescript copy
await db.query.posts.findMany({
orderBy: {
id: "asc",
},
});
```
**Order by `asc` + `desc`:**
```typescript copy
await db.query.posts.findMany({
orderBy: { id: "asc" },
with: {
comments: {
orderBy: { id: "desc" },
},
},
});
```
You can also use custom `sql` in order by statement:
```typescript copy
await db.query.posts.findMany({
orderBy: (t) => sql`${t.id} asc`,
with: {
comments: {
orderBy: (t, { desc }) => desc(t.id),
},
},
});
```
### Include custom fields
Relational query API lets you add custom additional fields.
It's useful when you need to retrieve data and apply additional functions to it.
As of now aggregations are not supported in `extras`, please use **[`core queries`](/docs/select)** for that.
```typescript copy {5}
import { sql } from 'drizzle-orm';
await db.query.users.findMany({
extras: {
loweredName: (table) => sql`lower(${table.name})`,
},
});
```
```typescript copy {3}
await db.query.users.findMany({
extras: {
loweredName: (users, { sql }) => sql`lower(${users.name})`,
},
})
```
`lowerName` as a key will be included to all fields in returned object.
If you will specify `.as("")` for any extras field - drizzle will ignore it
To retrieve all users with groups, but with the fullName field included (which is a concatenation of firstName and lastName),
you can use the following query with the Drizzle relational query builder.
```typescript copy
const res = await db.query.users.findMany({
extras: {
fullName: (users, { sql }) => sql`concat(${users.name}, " ", ${users.name})`,
},
with: {
groups: true,
},
});
```
```ts
// result type
const res: {
id: number;
name: string;
age: number | null;
invitedBy: number | null;
fullName: string;
groups: {
id: number;
name: string;
description: string | null;
}[];
}[]
```
To retrieve all posts with comments and add an additional field to calculate the size of the post content and the size of each comment content:
```typescript copy
const res = await db.query.posts.findMany({
extras: {
contentLength: (table, { sql }) => sql`length(${table.content})`,
},
with: {
comments: {
extras: {
commentSize: (table, { sql }) => sql`length(${table.content})`,
},
},
},
});
```
```ts
// result type
const res: {
id: number;
content: string;
authorId: number | null;
createdAt: Date;
contentLength: number;
comments: {
id: number;
content: string;
createdAt: Date;
creator: number | null;
postId: number | null;
commentSize: number;
}[];
}[]
```
### Include subqueries
You can also use subqueries within Relational Queries to leverage the power of custom SQL syntax
**Get users with posts and total posts count for each user**
```ts
import { posts } from './schema';
import { eq } from 'drizzle-orm';
await db.query.users.findMany({
with: {
posts: true
},
extras: {
totalPostsCount: (table) => db.$count(posts, eq(posts.authorId, table.id)),
}
});
```
```sql
SELECT
"d0"."id" AS "id",
"d0"."name" AS "name",
"d0"."age" AS "age",
"d0"."invited_by" AS "invitedBy",
(
(
SELECT
count(*)
FROM
"posts"
WHERE
"posts"."author_id" = "d0"."id"
)
) AS "totalPostsCount",
"posts"."r" AS "posts"
FROM
"users" AS "d0"
LEFT JOIN LATERAL (
SELECT
coalesce(json_agg(row_to_json("t".*)), '[]') AS "r"
FROM
(
SELECT
"d1"."id" AS "id",
"d1"."content" AS "content",
"d1"."author_id" AS "authorId",
"d1"."created_at"::text AS "createdAt"
FROM
"posts" AS "d1"
WHERE
"d0"."id" = "d1"."author_id"
) AS "t"
) AS "posts" ON TRUE
```
### Prepared statements
Prepared statements are designed to massively improve query performance â [see here.](/docs/perf-queries)
In this section, you can learn how to define placeholders and execute prepared statements
using the Drizzle relational query builder.
##### **Placeholder in `where`**
```ts copy
const prepared = db.query.users.findMany({
where: { id: { eq: sql.placeholder("id") } },
with: {
posts: {
where: { id: 1 },
},
},
}).prepare("query_name");
const usersWithPosts = await prepared.execute({ id: 1 });
```
##### **Placeholder in `limit`**
```ts copy
const prepared = db.query.users.findMany({
with: {
posts: {
limit: sql.placeholder("limit"),
},
},
}).prepare("query_name");
const usersWithPosts = await prepared.execute({ limit: 1 });
```
##### **Placeholder in `offset`**
```ts copy
const prepared = db.query.users.findMany({
offset: sql.placeholder('offset'),
with: {
posts: true,
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ offset: 1 });
```
##### **Multiple placeholders**
```ts copy
const prepared = db.query.users.findMany({
limit: sql.placeholder("uLimit"),
offset: sql.placeholder("uOffset"),
where: {
OR: [{ id: { eq: sql.placeholder("id") } }, { id: 3 }],
},
with: {
posts: {
where: { id: { eq: sql.placeholder("pid") } },
limit: sql.placeholder("pLimit"),
},
},
}).prepare("query_name");
const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 });
```
Source: https://orm.drizzle.team/docs/pg/schemas
import Section from '@mdx/Section.astro';
import Callout from '@mdx/Callout.astro';
# Table schemas
If you declare an entity within a schema, query builder will prepend schema names in queries:
`select * from "schema"."users"`
```ts copy {3,5,7}
import { serial, text, pgSchema } from "drizzle-orm/pg-core";
export const mySchema = pgSchema("my_schema");
export const colors = mySchema.enum('colors', ['red', 'green', 'blue']);
export const mySchemaUsers = mySchema.table('users', {
id: serial('id').primaryKey(),
name: text('name'),
color: colors('color').default('red'),
});
```
```sql
CREATE SCHEMA "my_schema";
CREATE TYPE "my_schema"."colors" AS ENUM ('red', 'green', 'blue');
CREATE TABLE "my_schema"."users" (
"id" serial PRIMARY KEY,
"name" text,
"color" "my_schema"."colors" DEFAULT 'red'
);
```
{/* TODO: ??? example > **Warning**
> If you will have tables with same names in different schemas then drizzle will respond with `never[]` error in result types and error from database
>
> In this case you may use [alias syntax](./joins#join-aliases-and-self-joins) */}
Source: https://orm.drizzle.team/docs/pg/seed-overview
import Npm from "@mdx/Npm.astro";
import Callout from '@mdx/Callout.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";
# Drizzle Seed
`drizzle-seed` is a TypeScript library that helps you generate deterministic, yet realistic,
fake data to populate your database. By leveraging a seedable pseudorandom number generator (pRNG),
it ensures that the data you generate is consistent and reproducible across different runs.
This is especially useful for testing, development, and debugging purposes.
#### What is Deterministic Data Generation?
Deterministic data generation means that the same input will always produce the same output.
In the context of `drizzle-seed`, when you initialize the library with the same seed number,
it will generate the same sequence of fake data every time. This allows for predictable and repeatable data sets.
#### Pseudorandom Number Generator (pRNG)
A pseudorandom number generator is an algorithm that produces a sequence of numbers
that approximates the properties of random numbers. However, because it's based on an initial value
called a seed, you can control its randomness. By using the same seed, the pRNG will produce the
same sequence of numbers, making your data generation process reproducible.
#### Benefits of Using a pRNG:
- Consistency: Ensures that your tests run on the same data every time.
- Debugging: Makes it easier to reproduce and fix bugs by providing a consistent data set.
- Collaboration: Team members can share seed numbers to work with the same data sets.
With drizzle-seed, you get the best of both worlds: the ability to generate realistic fake data and the control to reproduce it whenever needed.
## Installation
drizzle-seed
## Basic Usage
In this example we will create 10 users with random names and ids
```ts {12}
import { pgTable, integer, text } from "drizzle-orm/pg-core";
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
const users = pgTable("users", {
id: integer().primaryKey(),
name: text().notNull(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users });
}
main();
```
## Options
**`count`**
By default, the `seed` function will create 10 entities.
However, if you need more for your tests, you can specify this in the seed options object
```ts
await seed(db, schema, { count: 1000 });
```
**`seed`**
If you need a seed to generate a different set of values for all subsequent runs, you can define a different number
in the `seed` option. Any new number will generate a unique set of values
```ts
await seed(db, schema, { seed: 12345 });
```
## Reset database
With `drizzle-seed`, you can easily reset your database and seed it with new values, for example, in your test suites
```ts
// path to a file with schema you want to reset
import * as schema from "./schema.ts";
import { reset } from "drizzle-seed";
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await reset(db, schema);
}
main();
```
Different dialects will have different strategies for database resetting
For PostgreSQL, the `drizzle-seed` package will generate `TRUNCATE` statements with the `CASCADE` option to
ensure that all tables are empty after running the reset function
```sql
TRUNCATE tableName1, tableName2, ... CASCADE;
```
## Refinements
In case you need to change the behavior of the seed generator functions that `drizzle-seed` uses by default, you can specify your own implementation and even use your own list of values for the seeding process
`.refine` is a callback that receives a list of all available generator functions from `drizzle-seed`. It should return an object with keys representing the tables you want to refine, defining their behavior as needed.
Each table can specify several properties to simplify seeding your database:
- `columns`: Refine the default behavior of each column by specifying the required generator function, or exclude the column from seeding by specifying `false`.
- `count`: Specify the number of rows to insert into the database. By default, it's 10. If a global count is defined in the `seed()` options, the count defined here will override it for this specific table.
- `with`: Define how many referenced entities to create for each parent table if you want to generate associated entities.
You can also specify a weighted random distribution for the number of referenced values you want to create. For details on this API, you can refer to [Weighted Random docs](#weighted-random) docs section
**API**
```ts
await seed(db, schema).refine((f) => ({
users: {
columns: {},
count: 10,
with: {
posts: 10
}
},
}));
```
Let's check a few examples with an explanation of what will happen:
```ts filename='schema.ts'
import { pgTable, integer, text } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: integer().primaryKey(),
name: text().notNull(),
});
export const posts = pgTable("posts", {
id: integer().primaryKey(),
description: text(),
userId: integer().references(() => users.id),
});
```
**Example 1**: Seed only the `users` table with 20 entities and with refined seed logic for the `name` column
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: f.fullName(),
},
count: 20
}
}));
}
main();
```
**Example 2**: Seed only the `users` table, excluding the `name` column from seeding
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { users: schema.users }).refine((f) => ({
users: {
columns: {
name: false, // the name column will not be seeded, allowing the database to use its default value.
}
}
}));
}
main();
```
**Example 3**: Seed the `users` table with 20 entities and add 10 `posts` for each `user` by seeding the `posts` table and creating a reference from `posts` to `users`
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 20,
with: {
posts: 10
}
}
}));
}
main();
```
**Example 4**: Seed the `users` table with 5 entities and populate the database with 100 `posts` without connecting them to the `users` entities. Refine `id` generation for `users` so
that it will give any int from `10000` to `20000` and remains unique, and refine `posts` to retrieve values from a self-defined array
```ts filename='index.ts'
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
users: {
count: 5,
columns: {
id: f.int({
minValue: 10000,
maxValue: 20000,
isUnique: true,
}),
}
},
posts: {
count: 100,
columns: {
description: f.valuesFromArray({
values: [
"The sun set behind the mountains, painting the sky in hues of orange and purple",
"I can't believe how good this homemade pizza turned out!",
"Sometimes, all you need is a good book and a quiet corner.",
"Who else thinks rainy days are perfect for binge-watching old movies?",
"Tried a new hiking trail today and found the most amazing waterfall!",
// ...
],
})
}
}
}));
}
main();
```
There are many more possibilities that we will define in these docs, but for now, you can explore a few sections in this documentation. Check the [Generators](/docs/seed-functions) section to get familiar with all the available generator functions you can use.
A particularly great feature is the ability to use weighted randomization, both for generator values created for a column and for determining the number of related entities that can be generated by `drizzle-seed`.
Please check [Weighted Random docs](#weighted-random) for more info.
## Weighted Random
There may be cases where you need to use multiple datasets with a different priority that should be inserted into your database during the seed stage. For such cases, drizzle-seed provides an API called weighted random
The Drizzle Seed package has a few places where weighted random can be used:
- Columns inside each table refinements
- The `with` property, determining the amount of related entities to be created
Let's check an example for both:
```ts filename="schema.ts"
import { pgTable, integer, text, varchar, doublePrecision } from "drizzle-orm/pg-core";
export const orders = pgTable(
"orders",
{
id: integer().primaryKey(),
name: text().notNull(),
quantityPerUnit: varchar().notNull(),
unitPrice: doublePrecision().notNull(),
unitsInStock: integer().notNull(),
unitsOnOrder: integer().notNull(),
reorderLevel: integer().notNull(),
discontinued: integer().notNull(),
}
);
export const details = pgTable(
"details",
{
unitPrice: doublePrecision().notNull(),
quantity: integer().notNull(),
discount: doublePrecision().notNull(),
orderId: integer()
.notNull()
.references(() => orders.id, { onDelete: "cascade" }),
}
);
```
**Example 1**: Refine the `unitPrice` generation logic to generate `5000` random prices, with a 30% chance of prices between 10-100 and a 70% chance of prices between 100-300
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
count: 5000,
columns: {
unitPrice: f.weightedRandom(
[
{
weight: 0.3,
value: funcs.int({ minValue: 10, maxValue: 100 })
},
{
weight: 0.7,
value: funcs.number({ minValue: 100, maxValue: 300, precision: 100 })
}
]
),
}
}
}));
}
main();
```
**Example 2**: For each order, generate 1 to 3 details with a 60% chance, 5 to 7 details with a 30% chance, and 8 to 10 details with a 10% chance
```ts filename="index.ts"
import { drizzle } from "drizzle-orm/node-postgres";
import { seed } from "drizzle-seed";
import * as schema from './schema.ts'
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, schema).refine((f) => ({
orders: {
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3] },
{ weight: 0.3, count: [5, 6, 7] },
{ weight: 0.1, count: [8, 9, 10] },
]
}
}
}));
}
main();
```
## Complex example
```ts
import { seed } from "drizzle-seed";
import * as schema from "./schema.ts";
const main = async () => {
const titlesOfCourtesy = ["Ms.", "Mrs.", "Dr."];
const unitsOnOrders = [0, 10, 20, 30, 50, 60, 70, 80, 100];
const reorderLevels = [0, 5, 10, 15, 20, 25, 30];
const quantityPerUnit = [
"100 - 100 g pieces",
"100 - 250 g bags",
"10 - 200 g glasses",
"10 - 4 oz boxes",
"10 - 500 g pkgs.",
"10 - 500 g pkgs."
];
const discounts = [0.05, 0.15, 0.2, 0.25];
await seed(db, schema).refine((funcs) => ({
customers: {
count: 10000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" }),
fax: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
employees: {
count: 200,
columns: {
firstName: funcs.firstName(),
lastName: funcs.lastName(),
title: funcs.jobTitle(),
titleOfCourtesy: funcs.valuesFromArray({ values: titlesOfCourtesy }),
birthDate: funcs.date({ minDate: "2010-12-31", maxDate: "2010-12-31" }),
hireDate: funcs.date({ minDate: "2010-12-31", maxDate: "2024-08-26" }),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
country: funcs.country(),
homePhone: funcs.phoneNumber({ template: "(###) ###-####" }),
extension: funcs.int({ minValue: 428, maxValue: 5467 }),
notes: funcs.loremIpsum()
}
},
orders: {
count: 50000,
columns: {
shipVia: funcs.int({ minValue: 1, maxValue: 3 }),
freight: funcs.number({ minValue: 0, maxValue: 1000, precision: 100 }),
shipName: funcs.streetAddress(),
shipCity: funcs.city(),
shipRegion: funcs.state(),
shipPostalCode: funcs.postcode(),
shipCountry: funcs.country()
},
with: {
details:
[
{ weight: 0.6, count: [1, 2, 3, 4] },
{ weight: 0.2, count: [5, 6, 7, 8, 9, 10] },
{ weight: 0.15, count: [11, 12, 13, 14, 15, 16, 17] },
{ weight: 0.05, count: [18, 19, 20, 21, 22, 23, 24, 25] },
]
}
},
suppliers: {
count: 1000,
columns: {
companyName: funcs.companyName(),
contactName: funcs.fullName(),
contactTitle: funcs.jobTitle(),
address: funcs.streetAddress(),
city: funcs.city(),
postalCode: funcs.postcode(),
region: funcs.state(),
country: funcs.country(),
phone: funcs.phoneNumber({ template: "(###) ###-####" })
}
},
products: {
count: 5000,
columns: {
name: funcs.companyName(),
quantityPerUnit: funcs.valuesFromArray({ values: quantityPerUnit }),
unitPrice: funcs.weightedRandom(
[
{
weight: 0.5,
value: funcs.int({ minValue: 3, maxValue: 300 })
},
{
weight: 0.5,
value: funcs.number({ minValue: 3, maxValue: 300, precision: 100 })
}
]
),
unitsInStock: funcs.int({ minValue: 0, maxValue: 125 }),
unitsOnOrder: funcs.valuesFromArray({ values: unitsOnOrders }),
reorderLevel: funcs.valuesFromArray({ values: reorderLevels }),
discontinued: funcs.int({ minValue: 0, maxValue: 1 })
}
},
details: {
columns: {
unitPrice: funcs.number({ minValue: 10, maxValue: 130 }),
quantity: funcs.int({ minValue: 1, maxValue: 130 }),
discount: funcs.weightedRandom(
[
{ weight: 0.5, value: funcs.valuesFromArray({ values: discounts }) },
{ weight: 0.5, value: funcs.default({ defaultValue: 0 }) }
]
)
}
}
}));
}
main();
```
```ts
import type { AnyPgColumn } from "drizzle-orm/pg-core";
import { integer, numeric, pgTable, text, timestamp, varchar } from "drizzle-orm/pg-core";
export const customers = pgTable('customer', {
id: varchar({ length: 256 }).primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text(),
region: text(),
country: text().notNull(),
phone: text().notNull(),
fax: text(),
});
export const employees = pgTable(
'employee',
{
id: integer().primaryKey(),
lastName: text().notNull(),
firstName: text(),
title: text().notNull(),
titleOfCourtesy: text().notNull(),
birthDate: timestamp().notNull(),
hireDate: timestamp().notNull(),
address: text().notNull(),
city: text().notNull(),
postalCode: text().notNull(),
country: text().notNull(),
homePhone: text().notNull(),
extension: integer().notNull(),
notes: text().notNull(),
reportsTo: integer().references((): AnyPgColumn => employees.id),
photoPath: text(),
},
);
export const orders = pgTable('order', {
id: integer().primaryKey(),
orderDate: timestamp().notNull(),
requiredDate: timestamp().notNull(),
shippedDate: timestamp(),
shipVia: integer().notNull(),
freight: numeric().notNull(),
shipName: text().notNull(),
shipCity: text().notNull(),
shipRegion: text(),
shipPostalCode: text(),
shipCountry: text().notNull(),
customerId: text().notNull().references(() => customers.id, { onDelete: 'cascade' }),
employeeId: integer().notNull().references(() => employees.id, { onDelete: 'cascade' }),
});
export const suppliers = pgTable('supplier', {
id: integer().primaryKey(),
companyName: text().notNull(),
contactName: text().notNull(),
contactTitle: text().notNull(),
address: text().notNull(),
city: text().notNull(),
region: text(),
postalCode: text().notNull(),
country: text().notNull(),
phone: text().notNull(),
});
export const products = pgTable('product', {
id: integer().primaryKey(),
name: text().notNull(),
quantityPerUnit: text().notNull(),
unitPrice: numeric().notNull(),
unitsInStock: integer().notNull(),
unitsOnOrder: integer().notNull(),
reorderLevel: integer().notNull(),
discontinued: integer().notNull(),
supplierId: integer().notNull().references(() => suppliers.id, { onDelete: 'cascade' }),
});
export const details = pgTable('order_detail', {
unitPrice: numeric().notNull(),
quantity: integer().notNull(),
discount: numeric().notNull(),
orderId: integer().notNull().references(() => orders.id, { onDelete: 'cascade' }),
productId: integer().notNull().references(() => products.id, { onDelete: 'cascade' }),
});
```
## Limitations
#### Types limitations for `with`
Due to certain TypeScript limitations and the current API in Drizzle, it is not possible to properly infer references between tables, especially when circular dependencies between tables exist.
This means the `with` option will display all tables in the schema, and you will need to manually select the one that has a one-to-many relationship
The `with` option works for one-to-many relationships. For example, if you have one `user` and many `posts`, you can use users `with` posts, but you cannot use posts `with` users
#### Composite unique constraints
- Seeding is not supported when two composite unique constraints share a column:
```ts
const composite = pgTable(
"composite_example",
{
id: integer("id").notNull(),
name: text("name").notNull(),
slug: text("slug").notNull(),
},
(t) => [
unique("custom_name").on(t.id, t.name),
unique("custom_name1").on(t.name, t.slug),
]
);
```
This is allowed, however, if one of the constraints is a single-column unique constraint:
```ts
unique("custom_name1").on(t.name);
```
- You canât use a generator that doesnât expose an `isUnique` option in its config, unless itâs one of the always-unique generators: `intPrimaryKey`, `email`, `phoneNumber`, or `uuid`.
Source: https://orm.drizzle.team/docs/pg/seed-versioning
import Callout from "@mdx/Callout.astro";
import TableWrapper from "@mdx/TableWrapper.astro";
# Versioning
`drizzle-seed` uses versioning to manage outputs for static and dynamic data. To ensure true
determinism, ensure that values remain unchanged when using the same `seed` number. If changes are made to
static data sources or dynamic data generation logic, the version will be updated, allowing
you to choose between sticking with the previous version or using the latest.
You can upgrade to the latest `drizzle-seed` version for new features, such as additional
generators, while maintaining deterministic outputs with a previous version if needed. This
is particularly useful when you need to rely on existing deterministic data while accessing new functionality.
```ts
await seed(db, schema, { version: '2' });
```
## History
| api version | npm version | Changed generators |
| :-------------- | :-------------- | :------------- |
| `v1` | `0.1.1` | |
| `v2` | `0.2.1` |`string()`, `interval({ isUnique: true })` |
| `v3` | `0.4.0` |`hash generating function` |
| `v4 (LTS) ` | `1.0.0-beta.8` |`uuid` |
> This is not an actual API change; it is just an example of how we will proceed with `drizzle-seed` versioning.
For example, `lastName` generator was changed, and new version, `V2`, of this generator became available.
Later, `firstName` generator was changed, making `V3` version of this generator available.
| | `V1` | `V2` | `V3(latest)` |
| :--------------: | :--------------: | :-------------: | :--------------: |
| **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | |
| **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` |
##### Use the `firstName` generator of version 3 and the `lastName` generator of version 2
```ts
await seed(db, schema);
```
If you are not ready to use latest generator version right away, you can specify max version to use
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 2
```ts
await seed(db, schema, { version: '2' });
```
##### Use the `firstName` generator of version 1 and the `lastName` generator of version 1.
```ts
await seed(db, schema, { version: '1' });
```
## Version 2
#### Unique `interval` generator was changed
An older version of the generator could produce intervals like `1 minute 60 seconds` and `2 minutes 0 seconds`, treating them as distinct intervals.
However, when the `1 minute 60 seconds` interval is inserted into a PostgreSQL database, it is automatically converted to `2 minutes 0 seconds`. As a result, attempting to insert the `2 minutes 0 seconds` interval into a unique column afterwards will cause an error
You will be affected, if your table includes a unique column of type `interval`:
```ts
import { drizzle } from "drizzle-orm/node-postgres";
import { pgTable, interval } from "drizzle-orm/pg-core";
import { seed } from "drizzle-seed";
const intervals = pgTable("intervals", {
interval: interval().unique()
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals });
}
main();
```
You will be affected, if you use the unique `interval` generator in your seeding script, as shown in the script below:
```ts
import { drizzle } from "drizzle-orm/node-postgres";
import { pgTable, interval, char, varchar, text } from "drizzle-orm/pg-core";
import { seed } from "drizzle-seed";
const intervals = pgTable("intervals", {
interval: interval().unique(),
interval1: interval(),
interval2: char({ length: 256 }).unique(),
interval3: char({ length: 256 }),
interval4: varchar().unique(),
interval5: varchar(),
interval6: text().unique(),
interval7: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { intervals }).refine((f) => ({
intervals: {
columns: {
interval: f.interval({ isUnique: true }),
interval1: f.interval({ isUnique: true }),
interval2: f.interval({ isUnique: true }),
interval3: f.interval({ isUnique: true }),
interval4: f.interval({ isUnique: true }),
interval5: f.interval({ isUnique: true }),
interval6: f.interval({ isUnique: true }),
interval7: f.interval({ isUnique: true }),
}
}
}));
}
main();
```
#### `string` generators were changed: both non-unique and unique
Ability to generate a unique string based on the length of the text column (e.g., `varchar(20)`)
You will be affected, if your table includes a column of a text-like type with a maximum length parameter or a unique column of a text-like type:
```ts
import { drizzle } from "drizzle-orm/node-postgres";
import { pgTable, char, varchar, text } from "drizzle-orm/pg-core";
import { seed } from "drizzle-seed";
const strings = pgTable("strings", {
string2: char({ length: 256 }).unique(),
string3: char({ length: 256 }),
string4: varchar().unique(),
string5: varchar({ length: 256 }).unique(),
string6: varchar({ length: 256 }),
string7: text().unique(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings });
}
main();
```
You will be affected, if you use the `string` generator in your seeding script, as shown in the script below:
```ts
import { drizzle } from "drizzle-orm/node-postgres";
import { pgTable, char, varchar, text } from "drizzle-orm/pg-core";
import { seed } from "drizzle-seed";
const strings = pgTable("strings", {
string1: char({ length: 256 }).unique(),
string2: char({ length: 256 }),
string3: char({ length: 256 }),
string4: varchar(),
string5: varchar().unique(),
string6: varchar({ length: 256 }).unique(),
string7: varchar({ length: 256 }),
string8: varchar({ length: 256 }),
string9: text().unique(),
string10: text(),
});
async function main() {
const db = drizzle(process.env.DATABASE_URL!);
await seed(db, { strings }).refine((f) => ({
strings: {
columns: {
string1: f.string({ isUnique: true }),
string2: f.string(),
string3: f.string({ isUnique: true }),
string4: f.string({ isUnique: true }),
string5: f.string({ isUnique: true }),
string6: f.string({ isUnique: true }),
string7: f.string(),
string8: f.string({ isUnique: true }),
string9: f.string({ isUnique: true }),
string10: f.string({ isUnique: true }),
}
}
}));
}
main();
```
## Version 3
#### `hash generating function was changed`
The previous version of the hash generating function generated different hashes depending on whether Bun or Node.js was used, and hashes also varied across versions of Node.js.
The new hash generating function will generate the same hash regardless of the version of Node.js or Bun, resulting in deterministic data generation across all versions.
All generators will output different values compared to the previous version, even with the same seed number.
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '3' });
```
**Switch to the old version**
The previous version of hash generating function is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 hash generating function:
```ts
await seed(db, schema, { version: '2' });
```
## Version 4
#### `uuid` generator was changed
UUID values generated by the old version of the `uuid` generator fail Zodâs v4 UUID validation.
example
```ts
import { createSelectSchema } from 'drizzle-zod';
import { seed } from 'drizzle-seed';
await seed(db, { uuidTest: schema.uuidTest }, { count: 1 }).refine((funcs) => ({
uuidTest: {
columns: {
col1: funcs.uuid()
}
}
})
);
const uuidSelectSchema = createSelectSchema(schema.uuidTest);
const res = await db.select().from(schema.uuidTest);
// the line below will throw an error when using old version of uuid generator
uuidSelectSchema.parse(res[0]);
```
**Usage**
```ts
await seed(db, schema);
// or explicit
await seed(db, schema, { version: '4' });
```
**Switch to the old version**
The previous version of `uuid` generator is v1.
```ts
await seed(db, schema, { version: '1' });
```
To use the v2 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '2' });
```
To use the v3 generators while maintaining the v1 `uuid` generator:
```ts
await seed(db, schema, { version: '3' });
```
Source: https://orm.drizzle.team/docs/pg/select
import CodeTabs from '@mdx/CodeTabs.astro';
import Callout from '@mdx/Callout.astro';
import Section from '@mdx/Section.astro';
import $count from '@mdx/$count-pg.mdx';
# SQL Select
Drizzle provides you the most SQL-like way to fetch data from your database, while remaining type-safe and composable.
It natively supports mostly every query feature and capability of every dialect,
and whatever it doesn't support yet, can be added by the user with the powerful [sql](/docs/sql) operator.
For the following examples, let's assume you have a `users` table defined like this:
```typescript
import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial().primaryKey(),
name: text().notNull(),
age: integer(),
});
```
### Basic select
Select all rows from a table including all columns:
```typescript
const result = await db.select().from(users);
/*
{
id: number;
name: string;
age: number | null;
}[]
*/
```
```sql
select "id", "name", "age" from "users";
```
Notice that the result type is inferred automatically based on the table definition, including columns nullability.
Drizzle always explicitly lists columns in the `select` clause instead of using `select *`.
This is required internally to guarantee the fields order in the query result, and is also generally considered a good practice.
### Partial select
In some cases, you might want to select only a subset of columns from a table.
You can do that by providing a selection object to the `.select()` method:
```typescript copy
const result = await db.select({
field1: users.id,
field2: users.name,
}).from(users);
const { field1, field2 } = result[0];
```
```sql
select "id", "name" from "users";
```
Like in SQL, you can use arbitrary expressions as selection fields, not just table columns:
```typescript
const result = await db.select({
id: users.id,
lowerName: sql`lower(${users.name})`,
}).from(users);
```
```sql
select "id", lower("name") from "users";
```
By specifying `sql`, you are telling Drizzle that the **expected** type of the field is `string`.
If you specify it incorrectly (e.g. use `sql` for a field that will be returned as a string), the runtime value won't match the expected type.
Drizzle cannot perform any type casts based on the provided type generic, because that information is not available at runtime.
If you need to apply runtime transformations to the returned value, you can use the [`.mapWith()`](/docs/sql#sqlmapwith) method.
### Conditional select
You can have a dynamic selection object based on some condition:
```typescript
async function selectUsers(withName: boolean) {
return db
.select({
id: users.id,
...(withName ? { name: users.name } : {}),
})
.from(users);
}
const result = await selectUsers(true);
```
### Distinct select
You can use `.selectDistinct()` instead of `.select()` to retrieve only unique rows from a dataset:
```ts
await db.selectDistinct().from(users).orderBy(users.id, users.name);
await db.selectDistinct({ id: users.id }).from(users).orderBy(users.id);
```
```sql
select distinct "id", "name", "age" from "users" order by "users"."id", "users"."name";
select distinct "id" from "users" order by "users"."id";
```
In PostgreSQL, you can also use the `distinct on` clause to specify how the unique rows are determined:
```ts
await db.selectDistinctOn([users.id]).from(users).orderBy(users.id);
await db.selectDistinctOn([users.name], { name: users.name }).from(users).orderBy(users.name);
```
```sql
select distinct on ("users"."id") "id", "name", "age" from "users" order by "users"."id";
select distinct on ("users"."name") "name" from "users" order by "users"."name";
```
### Advanced select
Powered by TypeScript, Drizzle APIs let you build your select queries in a variety of flexible ways.
Sneak peek of advanced partial select, for more detailed advanced usage examples - see our [dedicated guide](/docs/guides/include-or-exclude-columns).
```ts
import { getColumns, sql } from 'drizzle-orm';
await db.select({
...getColumns(posts),
titleLength: sql`length(${posts.title})`,
}).from(posts);
```
```ts
import { getColumns } from 'drizzle-orm';
const { content, ...rest } = getColumns(posts); // exclude "content" column
await db.select({ ...rest }).from(posts); // select all other columns
```
```ts
await db.query.posts.findMany({
columns: {
title: true,
},
});
```
```ts
await db.query.posts.findMany({
columns: {
content: false,
},
});
```
## ---
### Filters
You can filter the query results using the [filter operators](/docs/operators) in the `.where()` method:
```typescript copy
import { eq, lt, gte, ne } from 'drizzle-orm';
await db.select().from(users).where(eq(users.id, 42));
await db.select().from(users).where(lt(users.id, 42));
await db.select().from(users).where(gte(users.id, 42));
await db.select().from(users).where(ne(users.id, 42));
...
```
```sql
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" < 42;
select "id", "name", "age" from "users" where "users"."id" >= 42;
select "id", "name", "age" from "users" where "users"."id" <> 42;
```
All filter operators are implemented using the [`sql`](/docs/sql) function.
You can use it yourself to write arbitrary SQL filters, or build your own operators.
For inspiration, you can check how the operators provided by Drizzle are [implemented](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/sql/expressions/conditions.ts).
```typescript copy
import { sql } from 'drizzle-orm';
import type { PgColumn } from "drizzle-orm/pg-core";
function equals42(col: PgColumn) {
return sql`${col} = 42`;
}
await db.select().from(users).where(sql`${users.id} < 42`);
await db.select().from(users).where(sql`${users.id} = 42`);
await db.select().from(users).where(equals42(users.id));
await db.select().from(users).where(sql`${users.id} >= 42`);
await db.select().from(users).where(sql`${users.id} <> 42`);
await db.select().from(users).where(sql`lower(${users.name}) = 'aaron'`);
```
```sql
select "id", "name", "age" from "users" where "users"."id" < 42;
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" = 42;
select "id", "name", "age" from "users" where "users"."id" >= 42;
select "id", "name", "age" from "users" where "users"."id" <> 42;
select "id", "name", "age" from "users" where lower("users"."name") = 'aaron';
```
All the values provided to filter operators and to the `sql` function are parameterized automatically.
For example, this query:
```ts
await db.select().from(users).where(eq(users.id, 42));
```
will be translated to:
```sql
select "id", "name", "age" from "users" where "users"."id" = $1; -- params: [42]
```
Inverting condition with a `not` operator:
```typescript copy
import { eq, not, sql } from 'drizzle-orm';
await db.select().from(users).where(not(eq(users.id, 42)));
await db.select().from(users).where(sql`not ${users.id} = 42`);
```
```sql
select "id", "name", "age" from "users" where not ("users"."id" = 42);
select "id", "name", "age" from "users" where not "users"."id" = 42;
```
You can safely alter schema, rename tables and columns
and it will be automatically reflected in your queries because of template interpolation,
as opposed to hardcoding column or table names when writing raw SQL.
### Combining filters
You can logically combine filter operators with `and()` and `or()` operators:
```typescript copy
import { eq, and, sql } from 'drizzle-orm';
await db.select().from(users).where(
and(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 and ${users.name} = 'Dan'`);
```
```sql
select "id", "name", "age" from "users" where (("users"."id" = 42) and ("users"."name" = 'Dan'));
select "id", "name", "age" from "users" where "users"."id" = 42 and "users"."name" = 'Dan';
```
```typescript copy
import { eq, or, sql } from 'drizzle-orm';
await db.select().from(users).where(
or(
eq(users.id, 42),
eq(users.name, 'Dan')
)
);
await db.select().from(users).where(sql`${users.id} = 42 or ${users.name} = 'Dan'`);
```
```sql
select "id", "name", "age" from "users" where (("users"."id" = 42) or ("users"."name" = 'Dan'));
select "id", "name", "age" from "users" where "users"."id" = 42 or "users"."name" = 'Dan';
```
### Advanced filters
In combination with TypeScript, Drizzle APIs provide you powerful and flexible ways to combine filters in queries.
Sneak peek of conditional filtering, for more detailed advanced usage examples - see our [dedicated guide](/docs/guides/conditional-filters-in-query).
```ts
const searchPosts = async (term?: string) => {
await db
.select()
.from(posts)
.where(term ? ilike(posts.title, term) : undefined);
};
await searchPosts();
await searchPosts('AI');
```
```ts
const searchPosts = async (filters: SQL[]) => {
await db
.select()
.from(posts)
.where(and(...filters));
};
const filters: SQL[] = [];
filters.push(ilike(posts.title, 'AI'));
filters.push(inArray(posts.category, ['Tech', 'Art', 'Science']));
filters.push(gt(posts.views, 200));
await searchPosts(filters);
```
## ---
### Limit & offset
Use `.limit()` and `.offset()` to add limit and offset clauses to the query - for example, to implement pagination:
```ts
await db.select().from(users).limit(10);
await db.select().from(users).limit(10).offset(10);
```
```sql
select "id", "name", "age" from "users" limit 10;
select "id", "name", "age" from "users" limit 10 offset 10;
```
### Order By
Use `.orderBy()` to add `order by` clause to the query, sorting the results by the specified fields:
```typescript
import { asc, desc } from 'drizzle-orm';
await db.select().from(users).orderBy(users.name);
await db.select().from(users).orderBy(desc(users.name));
// order by multiple fields
await db.select().from(users).orderBy(users.name, users.name2);
await db.select().from(users).orderBy(asc(users.name), desc(users.name2));
```
```sql
select "id", "name", "name2", "age" from "users" order by "users"."name";
select "id", "name", "name2", "age" from "users" order by "users"."name" desc;
select "id", "name", "name2", "age" from "users" order by "users"."name", "users"."name";
select "id", "name", "name2", "age" from "users" order by "users"."name" asc, "users"."name2" desc;
```
### Advanced pagination
Powered by TypeScript, Drizzle APIs let you implement all possible SQL pagination and sorting approaches.
Sneak peek of advanced pagination, for more detailed advanced
usage examples - see our dedicated [limit offset pagination](/docs/guides/limit-offset-pagination)
and [cursor pagination](/docs/guides/cursor-based-pagination) guides.
```ts
await db
.select()
.from(users)
.orderBy(asc(users.id)) // order by is mandatory
.limit(4) // the number of rows to return
.offset(4); // the number of rows to skip
```
```ts
const getUsers = async (page = 1, pageSize = 3) => {
return await db.query.users.findMany({
orderBy: (users, { asc }) => asc(users.id),
limit: pageSize,
offset: (page - 1) * pageSize,
});
};
await getUsers();
```
```ts
const getUsers = async (page = 1, pageSize = 10) => {
const sq = db
.select({ id: users.id })
.from(users)
.orderBy(users.id)
.limit(pageSize)
.offset((page - 1) * pageSize)
.as('subquery');
return await db.select().from(users).innerJoin(sq, eq(users.id, sq.id)).orderBy(users.id);
};
```
```ts
const nextUserPage = async (cursor?: number, pageSize = 3) => {
return await db
.select()
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined) // if cursor is provided, get rows after it
.limit(pageSize) // the number of rows to return
.orderBy(asc(users.id)); // ordering
};
// pass the cursor of the last row of the previous page (id)
await nextUserPage(3);
```
## ---
### WITH clause
Check how to use WITH statement with [insert](/docs/insert#with-insert-clause), [update](/docs/update#with-update-clause), [delete](/docs/delete#with-delete-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 sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
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";
```
You can also provide `insert`, `update` and `delete` statements inside `with`
```typescript copy
const sq = db.$with('sq').as(
db.insert(users).values({ name: 'John' }).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (insert into "users" ("id", "name", "age") values (default, 'John', default) returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
```typescript copy
const sq = db.$with('sq').as(
db.update(users).set({ age: 25 }).where(eq(users.name, 'John')).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (update "users" set "age" = 25 where "users"."name" = 'John' returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
```typescript copy
const sq = db.$with('sq').as(
db.delete(users).where(eq(users.name, 'John')).returning(),
);
const result = await db.with(sq).select().from(sq);
```
```sql
with "sq" as (delete from "users" where "users"."name" = $1 returning "id", "name", "age")
select "id", "name", "age" from "sq";
```
To select arbitrary SQL values as fields in a CTE and reference them in other CTEs or in the main query,
you need to add aliases to them:
```typescript copy
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);
```
If you don't provide an alias, the field type will become `DrizzleTypeError` and you won't be able to reference it in other queries.
If you ignore the type error and still try to use the field,
you will get a runtime error, since there's no way to reference that field without an alias.
### Select from subquery
Just like in SQL, you can embed queries into other queries by using the subquery API:
```typescript copy
const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');
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 be used in any place where a table can be used, for example in joins:
```typescript copy
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));
```
```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";
```
## ---
### Aggregations
With Drizzle, you can do aggregations using functions like `sum`, `count`, `avg`, etc. by
grouping and filtering with `.groupBy()` and `.having()` respectfully, same as you would do in raw SQL:
```typescript
import { gt, sql } from "drizzle-orm";
await db.select({
age: users.age,
count: sql`cast(count(${users.id}) as int)`,
})
.from(users)
.groupBy(users.age);
await db.select({
age: users.age,
count: sql