【Next.js】Drizzle ORMを導入してマイグレーションするまで

0. 前提

  • Next.js 14(App Router)
  • DBはVercel Postgresを利用する

1. プロジェクトを作成

Terminal window
npx create-next-app@latest

2. ライブラリをインストール

Terminal window
npm i drizzle-orm pg @vercel/postgres
npm i -D drizzle-kit eslint-plugin-drizzle @types/pg

3. drizzleの下準備

.eslintrc.json
{
"extends": [
...
"plugin:drizzle/recommended"
]
}
tsconfig.json
{
"compilerOptions": {
...
"target": "es5"
"target": "es6"
}
}

Drizzle Kitのコマンド実行のために、ECMAScriptのバージョンを切り替える。

4. DBの接続情報を追加

.env.local
POSTGRES_URL="postgres://aaa:bbb@ccc/ddd?sslmode=require"

利用するDBのURLを環境変数に設定する(今回はVercel Postgresを利用するので、管理画面から取得した)。

5. configファイルの実装

Terminal window
mkdir -p src/infra/db/schemas
mkdir -p src/infra/db/migrations

スキーマを設定するschemasディレクトリと、マイグレーションファイルを出力するmigrationsディレクトリを作成する。

src/infra/db/drizzle.config.ts
import type { Config } from 'drizzle-kit';
import { cwd } from 'node:process';
import { loadEnvConfig } from '@next/env';
loadEnvConfig(cwd());
export default {
schema: './src/infra/db/schemas/*',
out: './src/infra/db/migrations',
driver: 'pg',
dbCredentials: {
connectionString: process.env.POSTGRES_URL ?? '',
},
} satisfies Config;

各種設定のためのdrizzle.config.tsを作成し、先ほどのディレクトリ達を指定する。

またDrizzle Kitコマンド実行時にも環境変数を参照したいので、loadEnvConfigを呼び出しておく。

6. スキーマの宣言

src/infra/db/schemas/user.ts
import {pgTable, text, uuid} from 'drizzle-orm/pg-core';
export const userSchema = pgTable('users', {
id: uuid('id').defaultRandom().notNull().primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
});

今回はサンプルとして簡易なusersテーブルを作成する。

7. DBに接続

src/infra/db/index.ts
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { VercelPool } from '@vercel/postgres';
import drizzleConfig from '@/infra/db/drizzle.config';
const connection = new VercelPool(drizzleConfig.dbCredentials);
export const db = drizzle(connection);

8. マイグレーション

8-1. ファイルの作成

package.json
{
"scripts": {
"db:generate": "drizzle-kit generate:pg --config ./src/infra/db/drizzle.config.ts",
"db:push": "drizzle-kit push:pg --config ./src/infra/db/drizzle.config.ts",
"db:preview": "drizzle-kit studio --config ./src/infra/db/drizzle.config.ts"
}
}

Drizzle Kitのコマンドをnpm scriptsに設定しておく。今回はconfigファイルをルートに作成していないので、オプションで明示的にパスを指定する。

Terminal window
npm run db:generate

叩く。

└── db
├── schemas
└── migrations
└── meta
└── 0000_snapshot.json
└── _journal.json
└── 0000_lovely_iron_man.sql
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"email" text NOT NULL
);

するとmigrations下に、宣言したスキーマにマイグレートするためのSQLファイルが作成されている。

8-2. 実行

Terminal window
npm run db:push

これでDBにスキーマの変更が反映された。

9. CRUDを試す

Terminal window
npm run db:preview

Drizzle Studioを起動し、usersテーブルに適当なレコードを追加する。

import { db } from '@/infra/db';
import { userSchema } from '@/infra/db/schemas/user';
export default async function Home(): Promise<JSX.Element> {
const users = await db.select().from(userSchema);
// => [
// {
// id: '451a3667-8f34-4796-9b24-6cdfeade0877',
// name: 'user',
// email: 'test@example.com',
// }
// ]
return <>...</>;
}

任意のコンポーネント内でdrizzleのクエリを用いてusersをSELECTしてみると、先程追加したレコードが取得できた。

12

参考
  1. Drizzle ORM - Overview

  2. [BUG]: drizzle-kit push:mysql throws error with only connectionString · Issue #654 · drizzle-team/drizzle-orm