关于 encore-database
encore-database 面向使用 Encore.ts SQL 数据库的开发者,介绍查询、迁移、跨服务共享数据库及 Drizzle ORM,并说明模板查询如何参数化转义值。
主要功能
- 01异步迭代与数组查询方法
- 02单行查询和写入操作
- 03使用位置参数执行原始 SQL
- 04顺序 SQL 迁移指南
- 05Drizzle ORM 的配置与使用
- 06参数化模板查询
适用场景
- 01在 Encore 服务中读取或更新数据行
- 02设置顺序数据库迁移
- 03在多个服务间共享数据库
- 04在 Encore SQLDatabase 中使用 Drizzle ORM
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-database安装后在 Claude Code 中输入/encore-database确认已加载。
详细介绍
Encore 数据库操作
说明
数据库设置
import { SQLDatabase } from "encore.dev/storage/sqldb";
const db = new SQLDatabase("mydb", {
migrations: "./migrations",
});
查询方法
Encore 提供多种查询方法:
query - 多行结果
返回用于遍历多行结果的异步迭代器:
interface User {
id: string;
email: string;
name: string;
}
const rows = await db.query<User>`
SELECT id, email, name FROM users WHERE active = true
`;
const users: User[] = [];
for await (const row of rows) {
users.push(row);
}
queryAll - 将所有行作为数组返回
将所有行作为数组返回(query 的便捷封装):
const users = await db.queryAll<User>`
SELECT id, email, name FROM users WHERE active = true
`;
// users is User[]
queryRow - 单行结果
返回一行或 null:
const user = await db.queryRow<User>`
SELECT id, email, name FROM users WHERE id = ${userId}
`;
if (!user) {
throw APIError.notFound("user not found");
}
exec - 无返回值
用于 INSERT、UPDATE、DELETE 操作:
await db.exec`
INSERT INTO users (id, email, name)
VALUES (${id}, ${email}, ${name})
`;
await db.exec`
UPDATE users SET name = ${newName} WHERE id = ${id}
`;
await db.exec`
DELETE FROM users WHERE id = ${id}
`;
原始查询方法
使用带位置参数($1、$2 等)的原始 SQL 字符串,而不是模板字面量:
// Raw query returning multiple rows
const rows = await db.rawQuery<User>("SELECT * FROM users WHERE active = $1", true);
// Raw query returning single row
const user = await db.rawQueryRow<User>("SELECT * FROM users WHERE id = $1", userId);
// Raw query returning all rows as array
const users = await db.rawQueryAll<User>("SELECT * FROM users WHERE role = $1", "admin");
// Raw exec for INSERT/UPDATE/DELETE
await db.rawExec("INSERT INTO users (id, email) VALUES ($1, $2)", id, email);
在多个服务之间共享数据库
使用 SQLDatabase.named() 引用由其他服务拥有的数据库:
import { SQLDatabase } from "encore.dev/storage/sqldb";
// In the service that owns the database
const db = new SQLDatabase("shared-db", {
migrations: "./migrations",
});
// In another service that needs access
const sharedDb = SQLDatabase.named("shared-db");
// Now you can query the shared database
const user = await sharedDb.queryRow<User>`SELECT * FROM users WHERE id = ${id}`;
迁移
文件结构
service/
└── migrations/
├── 001_create_users.up.sql
├── 002_add_posts.up.sql
└── 003_add_indexes.up.sql
命名约定
- 以数字开头(001、002 等)
- 后接下划线和描述
- 以
.up.sql结尾 - 数字必须连续
迁移示例
-- migrations/001_create_users.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
Drizzle ORM 集成
设置
// db.ts
import { SQLDatabase } from "encore.dev/storage/sqldb";
import { drizzle } from "drizzle-orm/node-postgres";
const db = new SQLDatabase("mydb", {
migrations: {
path: "migrations",
source: "drizzle",
},
});
export const orm = drizzle(db.connectionString);
模式
// schema.ts
import * as p from "drizzle-orm/pg-core";
export const users = p.pgTable("users", {
id: p.uuid().primaryKey().defaultRandom(),
email: p.text().unique().notNull(),
name: p.text().notNull(),
createdAt: p.timestamp().defaultNow(),
});
Drizzle 配置
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "migrations",
schema: "schema.ts",
dialect: "postgresql",
});
生成迁移:drizzle-kit generate
使用 Drizzle
import { orm } from "./db";
import { users } from "./schema";
import { eq } from "drizzle-orm";
// Select
const allUsers = await orm.select().from(users);
const user = await orm.select().from(users).where(eq(users.id, id));
// Insert
await orm.insert(users).values({ email, name });
// Update
await orm.update(users).set({ name }).where(eq(users.id, id));
// Delete
await orm.delete(users).where(eq(users.id, id));
SQL 注入防护
Encore 的模板字面量会自动转义值:
// SAFE - values are parameterized
const email = "[email protected]";
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
// WRONG - SQL injection risk
await db.queryRow(`SELECT * FROM users WHERE email = '${email}'`);
指南
- 查询始终使用模板字面量(自动转义)
- 使用泛型指定类型:
query<User>、queryRow<User> - 启动时会自动应用迁移
- 预期返回 0 或 1 条结果时使用
queryRow - 多行结果使用
query和异步迭代 - 数据库名称应使用小写且具有描述性
- 每个服务通常都有自己的数据库
评论
支持 Markdown