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. 01Encoreサービスでのデータ行の読み取りや更新
  2. 02順次実行するデータベースマイグレーションの設定
  3. 03複数サービス間でのデータベース共有
  4. 04Encore 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 - 単一行

1 行または 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 を使用する
  • データベース名は小文字で説明的にする
  • 通常、各サービスは独自のデータベースを持つ

コメント