Oteto Blogのロゴ

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

0. 前提

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

1. プロジェクトを作成

npx create-next-app@latest

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

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.localPOSTGRES_URL="postgres://aaa:bbb@ccc/ddd?sslmode=require"

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

5. configファイルの実装

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

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

src/infra/db/drizzle.config.tsimport 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.tsimport {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.tsimport { 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ファイルをルートに作成していないので、オプションで明示的にパスを指定する。

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. 実行

npm run db:push

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

9. CRUDを試す

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してみると、先程追加したレコードが取得できた。