Drizzle のセットアップ¶
Hono REST、Hono GraphQL、Full-Stack、Monorepo の各テンプレートは、SQLite (@libsql/client 経由) + Drizzle ORM を同梱しています。このレシピでは、スキーマ変更、マイグレーション生成、本番環境向けに Turso に向けるといった 2 日目以降のフローを扱います。
提供されるもの¶
src/Schema.res—@module("drizzle-orm/sqlite-core")バインディングによる Drizzle テーブル定義src/Db.res—createClient({url: ...})+drizzle(client)+ クエリヘルパーdrizzle.config.ts— コンパイル済みSchema.res.mjsを指す drizzle-kit の設定ファイルdata/app.db— デフォルトのローカル SQLite ファイル (gitignore 済み)スクリプト —
db:generate、db:migrate
カラムの追加¶
src/Schema.res を編集します:
let users = sqliteTable("users", {
"id": intCol("id", {"primaryKey": true, "autoIncrement": true}),
"name": textCol("name", {"notNull": true}),
"email": textCol("email", {"notNull": true}),
"createdAt": intCol("created_at", {"notNull": true, "default": Date.now()}),
})
再生成して適用します:
pnpm res:build # refresh Schema.res.mjs that drizzle-kit reads
pnpm db:generate # emit migration SQL under ./drizzle/
pnpm db:migrate # apply pending migrations
コラボレーターが同じ変更を再生できるよう、./drizzle/ に生成された SQL をコミットしてください。
テーブルの追加¶
let posts = sqliteTable("posts", {
"id": intCol("id", {"primaryKey": true, "autoIncrement": true}),
"title": textCol("title", {"notNull": true}),
"authorId": intCol("author_id", {"notNull": true}),
})
同じ 2 ステップ: pnpm db:generate の後に pnpm db:migrate。
クエリ¶
生成された src/Db.res は必要な主要動詞をバインドしています。さらに追加する場合は @send を使います:
@send external where: ('builder, 'expr) => 'builder = "where"
@send external eq: ('col, 'value) => 'expr = "eq"
@send external limit: ('q, int) => 'q = "limit"
例 — id で検索する:
let findUser = async id => {
let rows =
await Db.db
->Db.select({
"id": Schema.users["id"],
"name": Schema.users["name"],
})
->Db.from(Schema.users)
->Db.where(Db.eq(Schema.users["id"], id))
->Db.limit(1)
->Db.allAsync
rows->Array.get(0)
}
Turso へ接続する (本番環境)¶
Turso はマネージド libsql ホストです — リモート URL に対しても同じクライアントが動作します。
brew install tursodatabase/tap/tursoturso auth loginturso db create my-appturso db tokens create my-app環境変数を設定してマイグレーションを実行します:
export DATABASE_URL="libsql://<db-name>-<org>.turso.io"
export DATABASE_AUTH_TOKEN="<token-from-step-4>"
pnpm db:migrate
src/Db.res を拡張して認証トークンを読み込みます:
let authToken =
processEnv
->Dict.get("DATABASE_AUTH_TOKEN")
->Option.getOr("")
let client = createClient({"url": dbUrl, "authToken": authToken})
Drizzle 設定に関する注意¶
drizzle.config.ts は コンパイル済み の src/Schema.res.mjs を読み込みます。スキーマ編集後にマイグレーションが空になる場合は、ReScript がコンパイルされていることを確認してください:
pnpm res:build && pnpm db:generate
あるいは別のターミナルで pnpm res:dev を実行して、Schema.res.mjs を常に最新に保ちます。
方言の切り替え¶
PostgreSQL を使用する場合は、以下を置き換えます:
@libsql/client→postgres(またはpg)drizzle-orm/libsql→drizzle-orm/postgres-jsdrizzle-orm/sqlite-core→drizzle-orm/pg-coredrizzle.config.tsでdialect: "sqlite"→dialect: "postgresql"
クエリヘルパーは多相的なため、ルートモジュール (Routes/Users.res など) は変更不要です。
よくある落とし穴¶
スキーマ編集後に "No migrations pending" が出る —
drizzle-kitはコンパイル済みの.mjsを読むため、先にres:buildを実行してください。ウォッチモードとのロック競合 — Windows でファイルロックエラーが出る場合、
db:migrateの前にres:devを停止してください。外部キー制約 — SQLite はデフォルトでは外部キー制約を強制しません。必要な場合は接続時に
PRAGMA foreign_keys = ON;を実行してください。