本教程演示了如何使用 Drizzle ORM 与 Neon Postgres 数据库。如果您还没有现有的 Neon 账户,请在这里 注册。
This guide assumes familiarity with:
npm i drizzle-orm
npm i -D drizzle-kit
yarn add drizzle-orm
yarn add -D drizzle-kit
pnpm add drizzle-orm
pnpm add -D drizzle-kit
bun add drizzle-orm
bun add -D drizzle-kit
npm i @neondatabase/serverless
yarn add @neondatabase/serverless
pnpm add @neondatabase/serverless
bun add @neondatabase/serverless
您应该已经安装了 dotenv
包来管理环境变量。
npm i dotenv
yarn add dotenv
pnpm add dotenv
bun add dotenv
设置 Neon 和 Drizzle ORM
创建一个新的 Neon 项目 登录到 Neon 控制台 ,导航到项目部分。选择一个项目或点击 “New Project” 按钮来创建一个新的项目。
您的 Neon 项目有一个名为 neondb
的预配置 Postgres 数据库。我们将在本教程中使用它。
Setup connection string variable 在项目控制台的 Connection Details 部分查找您的数据库连接字符串。它应该类似于:
postgres://username:password@ep-cool-darkness-123456.us-east-2.aws.neon.tech/neondb
将 DATABASE_URL
环境变量添加到您的 .env
或 .env.local
文件中,用来连接到 Neon 数据库。
DATABASE_URL=NEON_DATABASE_CONNECTION_STRING
连接 Drizzle ORM 到你的数据库 创建一个 db.ts
文件并设置数据库配置:
import { drizzle } from "drizzle-orm/neon-http" ;
import { neon } from "@neondatabase/serverless" ;
import { config } from "dotenv" ;
config ({ path : ".env" }); // or .env.local
const sql = neon ( process . env . DATABASE_URL ! );
export const db = drizzle (sql);
创建表格 创建一个 schema.ts
文件并声明你的表格:
import { integer , pgTable , serial , text , timestamp } from 'drizzle-orm/pg-core' ;
export const usersTable = pgTable ( 'users_table' , {
id : serial ( 'id' ) .primaryKey () ,
name : text ( 'name' ) .notNull () ,
age : integer ( 'age' ) .notNull () ,
email : text ( 'email' ) .notNull () .unique () ,
});
export const postsTable = pgTable ( 'posts_table' , {
id : serial ( 'id' ) .primaryKey () ,
title : text ( 'title' ) .notNull () ,
content : text ( 'content' ) .notNull () ,
userId : integer ( 'user_id' )
.notNull ()
.references (() => usersTable .id , { onDelete : 'cascade' }) ,
createdAt : timestamp ( 'created_at' ) .notNull () .defaultNow () ,
updatedAt : timestamp ( 'updated_at' )
.notNull ()
.$onUpdate (() => new Date ()) ,
});
export type InsertUser = typeof usersTable .$inferInsert;
export type SelectUser = typeof usersTable .$inferSelect;
export type InsertPost = typeof postsTable .$inferInsert;
export type SelectPost = typeof postsTable .$inferSelect;
设置 Drizzle 配置文件 Drizzle 配置文件 是由 Drizzle Kit 使用的配置文件,包含有关数据库连接、迁移文件夹和模式文件的所有信息。
在项目根目录下创建一个 drizzle.config.ts
文件并添加以下内容:
import { config } from 'dotenv' ;
import { defineConfig } from "drizzle-kit" ;
config ({ path : '.env' });
export default defineConfig ({
schema : "./src/schema.ts" ,
out : "./migrations" ,
dialect : "postgresql" ,
dbCredentials : {
url : process . env . DATABASE_URL ! ,
} ,
});
Applying changes to the database You can generate migrations using drizzle-kit generate
command and then run them using the drizzle-kit migrate
command.
Generate migrations:
npx drizzle-kit generate
These migrations are stored in the drizzle/migrations
directory, as specified in your drizzle.config.ts
. This directory will contain the SQL files necessary to update your database schema and a meta
folder for storing snapshots of the schema at different migration stages.
Example of a generated migration:
CREATE TABLE IF NOT EXISTS "posts_table" (
"id" serial PRIMARY KEY NOT NULL ,
"title" text NOT NULL ,
"content" text NOT NULL ,
"user_id" integer NOT NULL ,
"created_at" timestamp DEFAULT now () NOT NULL ,
"updated_at" timestamp NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users_table" (
"id" serial PRIMARY KEY NOT NULL ,
"name" text NOT NULL ,
"age" integer NOT NULL ,
"email" text NOT NULL ,
CONSTRAINT "users_table_email_unique" UNIQUE ( "email" )
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "posts_table" ADD CONSTRAINT "posts_table_user_id_users_table_id_fk" FOREIGN KEY ( "user_id" ) REFERENCES "public" . "users_table" ( "id" ) ON DELETE cascade ON UPDATE no action ;
EXCEPTION
WHEN duplicate_object THEN null ;
END $$;
Run migrations:
npx drizzle-kit migrate
Alternatively, you can push changes directly to the database using Drizzle kit push command :
npx drizzle-kit push
💡
push 命令适用于需要在本地开发环境中快速测试新的模式设计或更改的情况,可以实现快速迭代,而无需管理迁移文件的开销。
基本文件结构
这是项目的基本文件结构。在 src/db
目录中,我们有与数据库相关的文件,包括连接在 db.ts
中,模式定义在 schema.ts
中,以及存储在 migrations
目录中的迁移脚本。
📦 <项目根目录>
├ 📂 src
│ ├ 📜 db.ts
│ └ 📜 schema.ts
├ 📂 migrations
│ ├ 📂 meta
│ │ ├ 📜 _journal.json
│ │ └ 📜 0000_snapshot.json
│ └ 📜 0000_dry_richard_fisk.sql
├ 📜 .env
├ 📜 drizzle.config.ts
├ 📜 package.json
└ 📜 tsconfig.json
查询示例
例如,我们创建 src/queries
文件夹并针对每个操作创建单独的文件:insert、select、update、delete。
插入数据
在文档 中了解更多关于插入查询的信息。
import { db } from '../db' ;
import { InsertPost , InsertUser , postsTable , usersTable } from '../schema' ;
export async function createUser (data : InsertUser ) {
await db .insert (usersTable) .values (data);
}
export async function createPost (data : InsertPost ) {
await db .insert (postsTable) .values (data);
}
查询数据
有关查询查询的更多信息,请参阅文档 。
import { asc , between , count , eq , getTableColumns , sql } from 'drizzle-orm' ;
import { db } from '../db' ;
import { SelectUser , usersTable , postsTable } from '../schema' ;
export async function getUserById (id : SelectUser [ 'id' ]) : Promise <
Array <{
id : number ;
name : string ;
age : number ;
email : string ;
}>
> {
return db .select () .from (usersTable) .where ( eq ( usersTable .id , id));
}
export async function getUsersWithPostsCount (
page = 1 ,
pageSize = 5 ,
) : Promise <
Array <{
postsCount : number ;
id : number ;
name : string ;
age : number ;
email : string ;
}>
> {
return db
.select ({
... getTableColumns (usersTable) ,
postsCount : count ( postsTable .id) ,
})
.from (usersTable)
.leftJoin (postsTable , eq ( usersTable .id , postsTable .userId))
.groupBy ( usersTable .id)
.orderBy ( asc ( usersTable .id))
.limit (pageSize)
.offset ((page - 1 ) * pageSize);
}
export async function getPostsForLast24Hours (
page = 1 ,
pageSize = 5 ,
) : Promise <
Array <{
id : number ;
title : string ;
}>
> {
return db
.select ({
id : postsTable .id ,
title : postsTable .title ,
})
.from (postsTable)
.where ( between ( postsTable .createdAt , sql `now() - interval '1 day'` , sql `now()` ))
.orderBy ( asc ( postsTable .title) , asc ( postsTable .id))
.limit (pageSize)
.offset ((page - 1 ) * pageSize);
}
或者,您可以使用关系查询语法 。
更新数据
在文档 中了解更多关于更新查询的信息。
import { eq } from 'drizzle-orm' ;
import { db } from '../db' ;
import { SelectPost , postsTable } from '../schema' ;
export async function updatePost (id : SelectPost [ 'id' ] , data : Partial < Omit < SelectPost , 'id' >>) {
await db .update (postsTable) .set (data) .where ( eq ( postsTable .id , id));
}
删除数据
在文档 中了解有关删除查询的更多信息。
import { db } from '../db' ;
import { eq } from 'drizzle-orm' ;
import { SelectUser , usersTable } from '../schema' ;
export async function deleteUser (id : SelectUser [ 'id' ]) {
await db .delete (usersTable) .where ( eq ( usersTable .id , id));
}