Xata 提供了基于分支的开发,允许你为开发、测试和生产环境创建独立的数据库分支。
使用 Drizzle 连接 Xata
本教程演示如何将 Drizzle ORM 与 Xata 一起使用。Xata 是一个 PostgreSQL 数据库平台,旨在帮助开发者更高效地运维和扩展数据库,具备即时写时复制数据库分支、零停机模式的架构变更、数据匿名化以及 AI 驱动的性能监控等功能。
- 你需要先安装 Drizzle ORM 和 Drizzle Kit。可通过以下命令安装:
npm i drizzle-orm
npm i -D drizzle-kit
- 你需要安装
dotenv包来管理环境变量。可在这里了解更多该包信息:dotenv
npm i dotenv
- 你需要安装
postgres包以连接 Postgres 数据库。详细介绍见:postgres
npm i postgres
- 你需要拥有一个 Xata 账号及已创建数据库。参考 Xata 文档 创建账户及数据库。
查看Xata 文档以了解更多 Drizzle ORM 与 Xata 的使用资讯。
设置 Xata 和 Drizzle ORM
创建一个新的 Xata 数据库
按照以下步骤创建一个新的 Xata 数据库:
- 注册或登录你的 Xata 账号
- 从仪表盘创建一个新的数据库
- 选择你所在区域及数据库名称
- 系统将创建带有 PostgreSQL 端点的数据库
设置连接字符串变量
进入 Xata 仪表盘,复制 PostgreSQL 连接字符串,该信息可在分支总览页找到。
将 DATABASE_URL 变量添加至你的 .env 或 .env.local 文件:
DATABASE_URL=<YOUR_XATA_DATABASE_URL>连接字符串格式示例如下:
postgresql://postgres:<password>@<branch-id>.<region>.xata.tech/<database>?sslmode=require示例:
postgresql://postgres:password@t56hgfp7hd2sjfeiqcn66qpo8s.us-east-1.xata.tech/app?sslmode=require连接 Drizzle ORM 到数据库
在 src/db 目录下创建 index.ts 文件,配置数据库连接:
import { config } from 'dotenv';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
config({ path: '.env' }); // 或者 .env.local
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle({ client });创建表结构
在 src/db 目录下新建 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,包含数据库连接信息、迁移文件夹路径和 schema 文件路径。
在项目根目录下创建 drizzle.config.ts 文件,内容如下:
import { config } from 'dotenv';
import { defineConfig } from 'drizzle-kit';
config({ path: '.env' });
export default defineConfig({
schema: './src/db/schema.ts',
out: './migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});应用数据库变更
你可以使用 drizzle-kit generate 命令生成迁移文件,再用 drizzle-kit migrate 命令来执行迁移。
生成迁移:
npx drizzle-kit generate迁移文件存储于 migrations 目录(由你的 drizzle.config.ts 指定),该目录包含更新数据库架构所需的 SQL 文件,以及一个 meta 文件夹,存储不同迁移阶段的 schema 快照。
示例生成的迁移内容:
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 "users_table"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;执行迁移:
npx drizzle-kit migrate详细了解迁移流程。
另外,你也可以通过 Drizzle kit 的 push 命令 直接推送变更到数据库:
npx drizzle-kit pushXata 分支开发:Xata 允许你为不同环境创建数据库分支,你可以为开发、测试和生产分支使用不同的连接字符串,方便在部署到生产前测试架构更改。
基本文件结构
项目的基本文件结构如下。在 src/db 目录下存放数据库相关文件,包括连接配置的 index.ts 和 schema 定义的 schema.ts。
📦 <项目根目录>
├ 📂 src
│ ├ 📂 db
│ │ ├ 📜 index.ts
│ │ └ 📜 schema.ts
├ 📂 migrations
│ ├ 📂 meta
│ │ ├ 📜 _journal.json
│ │ └ 📜 0000_snapshot.json
│ └ 📜 0000_watery_spencer_smythe.sql
├ 📜 .env
├ 📜 drizzle.config.ts
├ 📜 package.json
└ 📜 tsconfig.json查询示例
例如,我们在 src/db/queries 创建文件夹,并为每个操作分别创建文件:插入、查询、更新、删除。
插入数据
关于插入查询的更多内容请参阅文档。
import { db } from '../index';
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);
}查询数据
关于查询的更多内容请参阅文档。
getColumns available starting from drizzle-orm@1.0.0-beta.2(read more here)
If you are on pre-1 version(like 0.45.1) then use getTableColumns
import { asc, between, count, eq, getColumns, sql } from 'drizzle-orm';
import { db } from '../index';
import { SelectUser, postsTable, usersTable } 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({
...getColumns(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 '../index';
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 { eq } from 'drizzle-orm';
import { db } from '../index';
import { SelectUser, usersTable } from '../schema';
export async function deleteUser(id: SelectUser['id']) {
await db.delete(usersTable).where(eq(usersTable.id, id));
}后续步骤
完成 Drizzle ORM 与 Xata 的搭建后,你可以继续深入学习:
- 了解 Drizzle 关系查询 以实现复杂查询
- 探索 Xata 官方文档
- 实施 数据库迁移 以便生产环境部署