mcpField
E
encore-database

encore-database

github.com/encoredev数据与数据库30813 次安装2 天前更新
代码仓库

关于 encore-database

encore-database 面向使用 Encore.ts SQL 数据库的开发者,介绍查询、迁移、跨服务共享数据库及 Drizzle ORM,并说明模板查询如何参数化转义值。

主要功能

  1. 01异步迭代与数组查询方法
  2. 02单行查询和写入操作
  3. 03使用位置参数执行原始 SQL
  4. 04顺序 SQL 迁移指南
  5. 05Drizzle ORM 的配置与使用
  6. 06参数化模板查询

适用场景

  1. 01在 Encore 服务中读取或更新数据行
  2. 02设置顺序数据库迁移
  3. 03在多个服务间共享数据库
  4. 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