Quering with SQL-like syntax [CRUD]
Drizzle ORM provide you the most SQL-like way to query your relational database.
We natively support mostly every query feature capability of every dialect
and whatever we do not yet support - can be done with our powerful sql
operator
When you declare schema(see docs) in typescript - you can instantly
use our typed query builder to insert, update, select and delete.
All types are infered instantly without any need for code generation.
import { pgTable, serial, text, varchar } from 'drizzle-orm/pg-core';
import { drizzle } from 'drizzle-orm/postgresjs';
import { InferModel } from 'drizzle-orm';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('full_name'),
});
type User = InferModel<typeof users, "select">;
type NewUser = InferModel<typeof users, "insert">;
const db = drizzle(...);
SQL Select
Basic and partial select
Getting a list of all users and you will have a typed result set
TODO: why we return array and there's no .findOne
, .findMany
const result: User[] = await db.select().from(users);
result[0].id;
result[0].name;
select * from 'users';
Whenever you have SQL table with many columns you might not wanna select all of them for either performance or security reasons.
You can omit them by using our partial query syntax which will generate partial SQL select and automatically map results
const result = await db.select({
field1: users.id,
field2: users.name,
}).from(users);
const { field1, field2 } = result[0];
select "users"."id" as "field1", "users"."name" as "field2" from "users";
With partial select you can apply sql transformations with sql
operator
const result = await db.select({
id: users.id,
lowerName: sql`lower(${users.name})`,
}).from(users);
select "users"."id", lower("users"."name") as "lowerName" from "users";
You can also select fields conditionally
async function selectUsers(withName: boolean) {
return db
.select({
id: users.id,
...(withName ? { name: users.name } : {}),
})
.from(users);
}
const users = await selectUsers(true);
Select filters
You can filter SQL results with our list of filter operators
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));
...
select * from 'users' where 'id' = 42;
select * from 'users' where 'id' < 42;
select * from 'users' where 'id' >= 42;
select * from 'users' where 'id' <> 42;
Any filter operator is a sql
operator under the hood, for full SQL potential
you can utilise it directly and build type safe and future safe queries
You can safely alter schema, rename tables and columns and it will automatically reflect in queries,
as opposed to having regular string raw SQL queries
import { sql } from "drizzle-orm";
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`${users.id} >= 42`);
await db.select().from(users).where(sql`${users.id} <> 42`);
await db.select().from(users).where(sql`lower(${users.name}) = "aaron"`);
select * from 'users' where 'id' = 42;
select * from 'users' where 'id' < 42;
select * from 'users' where 'id' <> 42;
select * from 'users' where 'id' >= 42;
select * from 'users' where lower('name') = "aaron";
Inverting condition with a not
operator
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`);
select * from 'users' where not 'id' = 42;
select * from 'users' where not 'id' = 42;
Combining filters
You can logically combine filter operators with conditional and
and or
operators
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"`);
select * from 'users' where 'id' = 42 and 'name' = "Dan";
select * from 'users' where 'id' = 42 and 'name' = "Dan";
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"`);
select * from 'users' where 'id' = 42 or 'name' = "Dan";
select * from 'users' where 'id' = 42 or 'name' = "Dan";
Distinct
You can use the distinct
keyword to retrieve unique or distinct values from a column
or set of columns in a query result. It eliminates duplicate rows, returning only the unique values.
await db.selectDistinct().from(users).orderBy(usersTable.id, usersTable.name);
await db.selectDistinct({ id: users.id }).from(users).orderBy(usersTable.id);
select distinct "id", "name" from "users" order by "users"."id", "users"."name";
select distinct "id" from "users" order by "users"."id";
Drizzle ORM supports DISTINCT ON
PostgreSQL operator too
DISTINCT ON
is only available in PostgreSQL
await db.selectDistinctOn([users.id])
.from(users)
.orderBy(users.id);
await db.selectDistinctOn([users.name], { name: users.name })
.from(users)
.orderBy(users.name);
select distinct on ("users"."id") "id", "name" from "users" order by "users"."id";
select distinct on ("users"."name") "name" from "users" order by "users"."name";
Limit & Offset
You can apply limit
and offset
to the query
await db.select().from(users).limit(10);
await db.select().from(users).limit(10).offset(10);
select * from "users" limit 10;
select * from "users" limit 10 offset 10;
Order By
You can sort results with orderBy
operator
import { asc, desc } from "drizzle-orm";
await db.select().from(users).orderBy(users.name);
await db.select().from(users).orderBy(desc(users.name));
// you can pass multiple order args
await db.select().from(users).orderBy(users.name, users.name2);
await db.select().from(users).orderBy(asc(users.name), desc(users.name2));
select * from "users" order by "name";
select * from "users" order by "name" desc;
select * from "users" order by "name" "name2";
select * from "users" order by "name" asc "name2" desc;
WITH clause
SQL with
clause - is a statement scoped view, helpful to organise complex queries
const sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
const result = await db.with(sq).select().from(sq);
with sq as (select * from "users" where "users"."id" = 42)
select * from sq;
To select raw sql
in a WITH subquery and reference that field in other queries,
you must add an alias to it
const sq = db.$with('sq').as(db.select({
name: sql<string>`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 - 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 reference 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 SQL queries into other SQL queries by using subquery
API
const sq = db.select().from(users).where(eq(users.id, 42)).as('sq');
const result = await db.select().from(sq);
select * from (select * from "user" where "user"."id" = 42) "sq";
You can also use subqueries in joins
const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id));
Aggregations
With Drizzle ORM you can do aggregations with functions like sum
, count
, avg
, etc. by
grouping and filtering with groupBy
and having
respectfully, just like you do in SQL.
With our powerful sql
operator you can infer aggregations functions return types using sql<number>
syntax
import { pgTable, serial, text, doublePrecision } from 'drizzle-orm/pg-core';
import { gte } from 'drizzle-orm';
export const product = pgTable('product', {
id: serial('id').primaryKey(),
name: text('name'),
unitPrice: doublePrecision("unit_price")
});
const result = await db.select({ count: sql<number>`count(*)` }).from(product);
result[0].count // will be number type
await db.select({ count: sql<number>`count(*)` }).from(product).where(gte(product.unitPrice, 4));
select count(*) from "product";
select count(*) from "product" where "unit_price" >= 4;
Lets have a quick look on how to group and filter grouped using a having
import { pgTable, serial, text } from 'drizzle-orm/pg-core';
export const user = pgTable('user', {
id: serial('id').primaryKey(),
name: text('name'),
city: text("city"),
});
await db.select({ count: sql<number>`count(${user.id})`, city: user.city })
.from(user)
.groupBy(({ city }) => city)
await db.select({ count: sql<number>`count(${user.id})`, city: user.city })
.from(user)
.groupBy(({ city }) => city)
.having(({ count }) => count)
select count("id"), "city" from "user" group by "user"."city";
select count("id"), "city" from "user" group by "user"."city" having count("user"."id");
Here's a more advanced example
const orders = sqliteTable('order', {
id: integer('id').primaryKey(),
orderDate: integer('order_date', { mode: 'timestamp' }).notNull(),
requiredDate: integer('required_date', { mode: 'timestamp' }).notNull(),
shippedDate: integer('shipped_date', { mode: 'timestamp' }),
shipVia: integer('ship_via').notNull(),
freight: numeric('freight').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: integer('employee_id').notNull(),
});
const details = sqliteTable('order_detail', {
unitPrice: numeric('unit_price').notNull(),
quantity: integer('quantity').notNull(),
discount: numeric('discount').notNull(),
orderId: integer('order_id').notNull(),
productId: integer('product_id').notNull(),
});
db
.select({
id: orders.id,
shippedDate: orders.shippedDate,
shipName: orders.shipName,
shipCity: orders.shipCity,
shipCountry: orders.shipCountry,
productsCount: sql<number>`count(${details.productId})`,
quantitySum: sql<number>`sum(${details.quantity})`,
totalPrice: sql<number>`sum(${details.quantity} * ${details.unitPrice})`,
})
.from(orders)
.leftJoin(details, eq(orders.id, details.orderId))
.groupBy(orders.id)
.orderBy(asc(orders.id))
.all();
SQL Insert
Drizzle ORM provides you the most SQL-like way to insert rows into the database tables
Insert one row
Inserting data with Drizzle is extremely straightfoward and sql-like
await db.insert(users).values({ name: 'Andrew' });
insert into "users" ("name") values ("Andrew");
If you need insert type for a particular table - you can use InferModel<typeof table, "insert">
syntax
import { InferModel } from "drizzle-orm";
type NewUser = InferModel<typeof users, "insert">;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);
Insert returning
✓ PostgreSQL
✓ SQLite
✕ MySQL
You can insert a row and get it back in PostgreSQL and SQLite
await db.insert(users).values({ name: "Dan" }).returning();
// partial return
await db.insert(users).value({ name: "Partial Dan" }).returning({ insertedId: users.id });
Insert multiple rows
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);
OnConflict and Upsert [insert or update]
You can run insert statements with on conflict clause to do nothing
or update
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 });
This is how you upsert with onConflictDoUpdate
await db.insert(users)
.values({ id: 1, name: 'Dan' })
.onConflictDoUpdate({ target: users.id, set: { name: 'John' } });
Upsert with where statement
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoUpdate({
target: users.id,
set: { name: 'John1' },
where: sql`${users.createdAt} > '2023-01-01'::date`,
});
SQL Update
Drizzle ORM supports SQL-like update syntax
Basic update
await db.update(users)
.set({ name: 'Mr. Dan' })
.where(eq(users.name, 'Dan'));
Update with returning
✓ PostgreSQL
✓ SQLite
✕ MySQL
You can update a row and get it back in PostgreSQL and SQLite
const updatedUserId: { updatedId: number }[] = await db.update(users)
.set({ name: 'Mr. Dan' })
.where(eq(users.name, 'Dan'))
.returning({ updatedId: users.id });
SQL Delete
Drizzle ORM supports SQL-like delete syntax
Basic delete
You can delete all rows in the table
await db.delete(users);
And you can delete with filters and conditions
await db.delete(users).where(eq(users.name, 'Dan'));
Delete with return
✓ PostgreSQL
✓ SQLite
✕ MySQL
You can delete a row and get it back in PostgreSQL and SQLite
const deletedUser = await db.delete(users)
.where(eq(users.name, 'Dan'))
.returning();
// partial return
const deletedUserIds: { deletedId: number }[] = await db.delete(users)
.where(eq(users.name, 'Dan'))
.returning({ deletedId: users.id });