0.16.3 • Published 1 year ago

drizzle-orm-pg v0.16.3

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
1 year ago

DrizzleORM PostgreSQL

DrizzleORM is a TypeScript ORM library with a drizzle-kit CLI companion for automatic SQL migrations generation. Here you can find extensive docs for PostgreSQL module.

Installation

// postgresql
npm install drizzle-orm drizzle-orm-pg
npm install -D drizzle-kit

SQL schema declaration

With drizzle-orm you declare SQL schema in TypeScript. You can have either one schema.ts file with all declarations or you can group them logically in multiple files. We prefer to use single file schema.

📦project
 ├ 📂src
 │ ├ 📂data
 │ │ └ 📜schema.ts
 │ └ ...
 ├ ...
 └ 📜package.json
 
## or multiple schema files
├ 📂data
  ├ 📜users.ts
  ├ 📜countries.ts
  ├ 📜cities.ts
  ├ 📜products.ts
  ├ 📜clients.ts
  ├ 📜enums.ts
  └ 📜etc.ts

Quick start

import { PgConnector, pgTable, serial, text, varchar } from "drizzle-orm-pg";
import { Pool } from "pg";

const users = pgTable("users", {
  id: serial('id').primaryKey(),
  fullName: text('full_name'),
  phone: varchar('phone', { length: 256 }),
})

const pool = new Pool({ connectionString: "postgres://user:password@host:port/db" });
const connector = new PgConnector(pool);
const db = await connector.connect();

const users = await db.select(users);

Connecting to database

import { PgConnector } from "drizzle-orm-pg";
import { Pool } from "pg";

const pool = new Pool({ connectionString: "postgres://postgres:password@127.0.0.1:5432/postgres" });
const pool = new Pool({
  host: "127.0.0.1",
  port: 5432,
  user: "postgres",
  password: "password",
  database: "db_name",
});

const connector = new PgConnector(pool);
const db = await connector.connect();

This is how you declare SQL schema in schema.ts. You can declare tables, indexes and constraints, foreign keys and enums. Please pay attention to export keyword, they are mandatory if you'll be using drizzle-kit SQL migrations generator.

// declaring enum in database
export const popularityEnum = pgEnum("popularity", ["unknown", "known", 
"popular"]);

export const countries = pgTable("countries", {
    id: serial("id").primaryKey(),
    name: varchar("name", 256),
  }, (table) => ({
    nameIndex: index("name_idx", table.name, { unique: true });
  })
);

export const cities = pgTable("cities", {
  id: serial("id").primaryKey(),
  name: varchar("name", 256),
  countryId: integer("country_id").references(() => countries.id),
  popularity: popularityEnum("popularity"),
})

Database and table entity types

import { PgConnector, PgDatabase, pgTable, InferModel, serial, text, varchar } from "drizzle-orm-pg";

const users = pgTable("users", {
  id: serial('id').primaryKey(),
  fullName: text('full_name'),
  phone: varchar('phone', { length: 256 }),
})

export type User = InferModel<typeof users> // return type when queried
export type InsertUser = InferModel<typeof users, "insert"> // insert type
...

const connector = new PgConnector(pool);
const db: PgDatabase = await connector.connect();

const result: User[] = await db.select(users)

const insertUser = (user: InsertUser) => {
  return db.insert(users).values(user)
}

The list of all column types. You can also create custom types - !!see here!!.

export const popularityEnum = pgEnum("popularity", ["unknown", "known", "popular"]);
popularityEnum("column_name") // declare enum column

smallint("...")
integer("...")
bigint("...", { mode: "number" | "bigint" })

boolean("...")
text("...");
text<"one" | "two" | "three">("...");
varchar("...");
varchar<"one" | "two" | "three">("...");
varchar("...", { length: 256 }); // with length limit

serial("...");
bigserial("...", { mode: "number" | "bigint" });

decimal("...", { precision: 100, scale: 2 });
numeric("...", { precision: 100, scale: 2 });

real("...")
doublePrecision("...")

json<...>("...");
json<string[]>("...");
jsonb<...>("...");
jsonb<string[]>("...");

time("...")
time("...", { precision: 6, withTimezone: true })
timestamp("...")
timestamp("...", { mode: "date" | "string", precision: 0..6, withTimezone: true })
timestamp("...").defaultNow()
date("...")
date("...", { mode: "string" | "date" })
interval("...")
interval("...", { fields: "day" | "month" | "..." , precision: 0..6 })

column.primaryKey()
column.notNull()
column.defaultValue(...)

Declaring indexes and foreign keys

import { foreignKey, index, integer, pgTable, serial, varchar } from "drizzle-orm-pg";

export const countries = pgTable("countries", {
    id: serial("id").primaryKey(),
    name: varchar("name", { length: 256 }),
    population: integer("population"),
  }, (table) => ({
    nameIdx: index("name_idx", table.name), // one column
    namePopulationIdx: index("name_population_idx", [table.name, table.population]), // multiple columns
    uniqueIdx: index("unique_idx", table.name, { unique: true }), // unique index
  })
);

export const cities = pgTable("cities", {
  id: serial("id").primaryKey(),
  name: varchar("name", { length: 256 }),
  countryId: integer("country_id").references(() => countries.id), // inline foreign key
  countryName: varchar("country_id"),
}, (table) => ({
  // explicit foreign key with 1 column
  countryFk: foreignKey(() => ({
    columns: [table.countryId],
    foreignColumns: [countries.id],
  })),
  // explicit foreign key with multiple columns
  countryIdNameFk: foreignKey(() => ({
    columns: [table.countryId, table.countryName],
    foreignColumns: [countries.id, countries.name],
  })),
}));

// list of all index params
unique?: boolean;
concurrently?: boolean;
only?: boolean;
using?: sql``; // sql expression
order?: 'asc' | 'desc';
nulls?: 'first' | 'last';
where?: sql``; // sql expression

Create Read Update Delete

Querying, sorting and filtering. We also support partial select.

...
import { PgConnector, pgTable, serial, text, varchar } from "drizzle-orm-pg";
import { and, asc, desc, eq, or } from "drizzle-orm/expressions";

const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("full_name"),
});

const connector = new PgConnector(...);
const db = await connector.connect();

await db.select(users);
await db.select(users).where(eq(users.id, 42));

// you can combine filters with eq(...) or or(...)
await db.select(users)
  .where(and(eq(users.id, 42), eq(users.name, "Dan")));

await db.select(users)
  .where(or(eq(users.id, 42), eq(users.id, 1)));

// partial select
const result = await db.select(users).fields({
    mapped1: users.id,
    mapped2: users.name,
  });
const { mapped1, mapped2 } = result[0];

// limit offset & order by
await db.select(users).limit(10).offset(10);
await db.select(users).orderBy(asc(users.name));
await db.select(users).orderBy(desc(users.name));
// you can pass multiple order args
await db.select(users).orderBy(asc(users.name), desc(users.name));

// list of all filter operators
eq(column, value)
eq(column1, column2)
ne(column, value)
ne(column1, column2)

notEq(column, value)
less(column, value)
lessEq(column, value)

gt(column, value)
gt(column1, column2)
gte(column, value)
gte(column1, column2)
lt(column, value)
lt(column1, column2)
lte(column, value)
lte(column1, column2)

isNull(column)
isNotNull(column)

inArray(column, values[])
inArray(column, sqlSubquery)
notInArray(column, values[])
notInArray(column, sqlSubquery)

exists(sqlSubquery)
notExists(sqlSubquery)

between(column, min, max)
notBetween(column, min, max)

like(column, value)
like(column, value)
ilike(column, value)
notIlike(column, value)

not(sqlExpression)

and(exressions: Expr[])
or(exressions: Expr[])

Inserting

import { PgConnector, pgTable, serial, text, timestamp } from "drizzle-orm-pg";

const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("name"),
  createdAt: timestamp("created_at"),
});

const connector = new PgConnector(...);
const db = await connector.connect();

await db.insert(users
  .values({
    name: "Andrew",
    createdAt: new Date(),
  });

// accepts vararg of items
await db.insert(users)
  .values(
    {
      name: "Andrew",
      createdAt: new Date(),
    },
    {
      name: "Dan",
      createdAt: new Date(),
    },
  ));

await db.insert(users)
  .values(...[
    {
      name: "Andrew",
      createdAt: new Date(),
    },
    {
      name: "Dan",
      createdAt: new Date(),
    },
  ]);

Update and Delete

await db.update(users)
  .set({ name: 'Mr. Dan' })
  .where(eq(usersTable.name, 'Dan'));
	
await db.delete(users)
  .where(eq(usersTable.name, 'Dan'));

Joins

Last but not least. Probably the most powerful feature in the library🚀

Many-to-one

import { PgConnector, pgTable, serial, text, timestamp } from "drizzle-orm-pg";

const cities = pgTable("cities", {
  id: serial("id").primaryKey(),
  name: text("name"),
});

const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("name"),
  cityId: integer("city_id").references(() => cities.id)
});

const connector = new PgConnector(...);
const db = await connector.connect();

const result = db.select(cities).leftJoin(users, eq(cities2.id, users2.cityId))

Many-to-many

const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("name"),
});

const chatGroups = pgTable("chat_groups", {
  id: serial("id").primaryKey(),
  name: text("name"),
});

const usersToChatGroups = pgTable("usersToChatGroups", {
  userId: integer("user_id").notNull().references(() => users.id),
  groupId: integer("group_id").notNull().references(() => chatGroups.id),
});

...
const connector = new PgConnector(...);
const db = await connector.connect();

// querying user group with id 1 and all the participants(users)
db.select(usersToChatGroups)
  .leftJoin(users, eq(usersToChatGroups.userId, users.id))
  .leftJoin(chatGroups, eq(usersToChatGroups.groupId, chatGroups.id))
  .where(eq(chatGroups.id, 1));

Join aliases and selfjoins

import { ..., alias } from "drizzle-orm-pg";

export const files = pgTable("folders", {
  name: text("name").notNull(),
  parent: text("parent_folder")
})

...
const connector = new PgConnector(...);
const db = await connector.connect();

const nestedFiles = alias(files, "nested_files");

await db.select(files)
  .leftJoin(nestedFiles, eq(files.name, nestedFiles.name))
  .where(eq(files.parent, "/"));
// will return files and folers and nested files for each folder at root dir

Join using partial field select

Join Cities with Users getting only needed fields form request

await db.select(cities).fields({
  id: cities.id,
  cityName: cities.name
}).leftJoin(users, eq(users.cityId, cities.id));

Migrations

Automatic SQL migrations generation with drizzle-kit

DrizzleKit - is a CLI migrator tool for DrizzleORM. It is probably one and only tool that lets you completely automatically generate SQL migrations and covers ~95% of the common cases like delitions and renames by prompting user input.\ Check out the docs for DrizzleKit

For schema file:

import { index, integer, pgTable, serial, varchar } from "drizzle-orm-pg";

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  fullName: varchar("full_name", { length: 256 }),
}, (table)=>({
  nameIdx: index("name_idx", table.fullName),
}));

export const authOtps = pgTable("auth_otp", {
  id: serial("id").primaryKey(),
  phone: varchar("phone", { length: 256 }),
  userId: integer("user_id").references(() => users.id),
}

It will generate:

CREATE TABLE IF NOT EXISTS auth_otp (
	"id" SERIAL PRIMARY KEY,
	"phone" character varying(256),
	"user_id" INT
);

CREATE TABLE IF NOT EXISTS users (
	"id" SERIAL PRIMARY KEY,
	"full_name" character varying(256)
);

DO $$ BEGIN
 ALTER TABLE auth_otp ADD CONSTRAINT auth_otp_user_id_fkey FOREIGN KEY ("user_id") REFERENCES users(id);
EXCEPTION
 WHEN duplicate_object THEN null;
END $$;

CREATE INDEX IF NOT EXISTS users_full_name_index ON users (full_name);

And you can run migrations manually or using our embedded migrations module

import { PgConnector } from "drizzle-orm-pg";
import { Pool } from "pg";

const pool = new Pool({ connectionString: "postgres://user:password@host:port/db" });
const connector = new PgConnector(pool);
const db = await connector.connect();

// this will automatically run needed migrations on the database
await connector.migrate({ migrationsFolder: "./drizzle" })

Raw query usage

If you have some complex queries to execute and drizzle-orm can't handle them yet, then you could use rawQuery execution

Execute custom raw query
// it will automatically run a parametrized query!
const res: QueryResult<any> = await db.execute(sql`SELECT * FROM users WHERE user.id = ${userId}`)
0.15.0-2e1e5b2

1 year ago

0.14.4-59f0a3a

1 year ago

0.15.2-2b4d90d

1 year ago

0.15.3-0dcbb16

1 year ago

0.16.3-60d4ce6

1 year ago

0.16.3-78fb61e

1 year ago

0.15.0-60954a3

1 year ago

0.15.3-1e62a3e

1 year ago

0.15.0-7d17618

1 year ago

0.15.1-9728cc6

1 year ago

0.15.2-1766537

1 year ago

0.15.3-844cb31

1 year ago

0.15.3-1f6c448

1 year ago

0.16.2-97f9977

1 year ago

0.15.3-72fb2c0

1 year ago

0.16.1-b9e9ba0

1 year ago

0.16.2-cf5a510

1 year ago

0.16.1-51c77ba

1 year ago

0.15.3-98b2097

1 year ago

0.15.3-adba448

1 year ago

0.16.1-b4b5a09

1 year ago

0.15.1-656bcc3

1 year ago

0.14.4

1 year ago

0.16.0-11f7ff3

1 year ago

0.16.0-af45b90

1 year ago

0.15.3-71be15e

1 year ago

0.15.1-a1b76f5

1 year ago

0.15.2-6879c1d

1 year ago

0.16.2-2c4c5b1

1 year ago

0.15.3-b422bc1

1 year ago

0.15.1-80901ff

1 year ago

0.15.3-1c43305

1 year ago

0.15.1-f67dd9d

1 year ago

0.15.0-88523f9

1 year ago

0.15.0

1 year ago

0.15.1

1 year ago

0.15.2

1 year ago

0.15.3

1 year ago

0.16.2-43776eb

1 year ago

0.16.1-4124cf0

1 year ago

0.15.4-335d188

1 year ago

0.15.3-d8f67aa

1 year ago

0.15.3-c675dde

1 year ago

0.16.0-484a8ae

1 year ago

0.16.1-ddf4681

1 year ago

0.15.3-36368e7

1 year ago

0.16.3

1 year ago

0.15.3-f77c6c2

1 year ago

0.15.3-b3dd40c

1 year ago

0.15.2-52e75f5

1 year ago

0.16.0

1 year ago

0.16.1

1 year ago

0.16.2

1 year ago

0.15.1-19c5c02

1 year ago

0.16.1-56d16f9

1 year ago

0.15.1-3548c99

1 year ago

0.16.0-3e17d6e

1 year ago

0.15.3-30915c1

1 year ago

0.15.3-23baedd

1 year ago

0.16.2-c522f26

1 year ago

0.15.3-bbe577c

1 year ago

0.15.1-4a5b2e3

1 year ago

0.13.4-f115120

1 year ago

0.14.3-4eada6d

1 year ago

0.14.1-f617bb6

1 year ago

0.14.2-3793d78

1 year ago

0.15.0-67805d4

1 year ago

0.14.3-5b14ef4

1 year ago

0.15.0-4e3a5b9

1 year ago

0.13.0

1 year ago

0.13.1

1 year ago

0.13.2

1 year ago

0.14.3-1e42665

1 year ago

0.13.4

1 year ago

0.14.3-314c1fa

1 year ago

0.13.4-d68193c

1 year ago

0.14.1-c5dca58

1 year ago

0.15.0-07f606f

1 year ago

0.14.2-3967f06

1 year ago

0.14.2-cc06fec

1 year ago

0.14.3-f8d0d46

1 year ago

0.13.4-cd3f5e4

1 year ago

0.14.1-7d1d823

1 year ago

0.13.4-7d422f2

1 year ago

0.14.0-beta.1

1 year ago

0.14.0-beta.2

1 year ago

0.14.0-beta.3

1 year ago

0.14.1-bad61c5

1 year ago

0.12.0-beta.29

2 years ago

0.14.1-1c58471

1 year ago

0.12.0-beta.28

2 years ago

0.14.2-5514014

1 year ago

0.14.0

1 year ago

0.14.1

1 year ago

0.12.0-beta.27

2 years ago

0.14.3-9ba6169

1 year ago

0.14.2

1 year ago

0.12.0-beta.26

2 years ago

0.14.3

1 year ago

0.13.3-beta.1

1 year ago

0.12.0-beta.30

2 years ago

0.13.3-beta.2

1 year ago

0.13.4-099a94f

1 year ago

0.12.0-beta.39

1 year ago

0.14.0-beta.4

1 year ago

0.12.0-beta.34

2 years ago

0.12.0-beta.33

2 years ago

0.12.0-beta.32

2 years ago

0.12.0-beta.31

2 years ago

0.12.0-beta.38

1 year ago

0.12.0-beta.37

1 year ago

0.12.0-beta.36

1 year ago

0.12.0-beta.35

2 years ago

0.13.4-a486088

1 year ago

0.14.3-6f226c1

1 year ago

0.12.0-beta.40

1 year ago

0.13.4-9f4af29

1 year ago

0.15.0-a90d3bc

1 year ago

0.14.3-01c3d82

1 year ago

0.14.0-a7bfec3

1 year ago

0.13.4-03f651b

1 year ago

0.14.2-e902ba8

1 year ago

0.13.4-6a923f2

1 year ago

0.15.0-dd27c86

1 year ago

0.14.3-e295848

1 year ago

0.14.3-cce4cab

1 year ago

0.14.2-c7344a5

1 year ago

0.14.2-a52eacc

1 year ago

0.14.1-f92529c

1 year ago

0.14.3-1bcee50

1 year ago

0.15.0-5ce7d69

1 year ago

0.14.3-c153bbf

1 year ago

0.13.4-b8ae68d

1 year ago

0.13.4-efa4dda

1 year ago

0.14.1-1f44b00

1 year ago

0.13.4-671a09e

1 year ago

0.13.4-a61a883

1 year ago

0.14.1-1d98f11

1 year ago

0.12.0-beta.9

2 years ago

0.12.0-beta.8

2 years ago

0.12.0-beta.7

2 years ago

0.12.0-beta.6

2 years ago

0.12.0-beta.22

2 years ago

0.12.0-beta.20

2 years ago

0.12.0-beta.25

2 years ago

0.12.0-beta.24

2 years ago

0.12.0-beta.19

2 years ago

0.12.0-beta.18

2 years ago

0.12.0-beta.17

2 years ago

0.12.0-beta.12

2 years ago

0.12.0-beta.11

2 years ago

0.12.0-beta.10

2 years ago

0.12.0-beta.16

2 years ago

0.12.0-beta.15

2 years ago

0.12.0-beta.14

2 years ago

0.12.0-beta.13

2 years ago

0.12.0-beta.5

2 years ago

0.12.0-beta.4

2 years ago

0.12.0-beta.3

2 years ago

0.12.0-beta.2

2 years ago

0.12.0-beta.1

2 years ago

0.12.0-beta.0

2 years ago