Prisma2を使ってREST APIを実装してみる
2020.01.23
Modern DB toolkitと謳うPrisma2の使い心地を試すべく、Prisma2のツールを使ってREST APIを実装してみました。
Prisma というデータベースに関する便利な機能を提供するツールをずっと触ってみようと思っていたのですが、次期バージョンが鋭意開発中でプレビュー版として使えるようなので簡単なREST APIを開発しながら使い勝手を見てみました。(現行のバージョン1は全く触れたことがありません)
Prisma2について
READMEによると Prisma2 は以下のツールで構成されます。
- Prisma Client JS: Type-safe and auto-generated database client (“ORM replacement”)
- Prisma Migrate: Declarative data modeling and migrations
- Studio: Admin UI to support various database workflows
この中で Prisma Migrate については試してみたもののエラーが多く今回使うのは諦めました。(2.0.0-preview020.1を使った場合)
うまくいくケースもあったのでその時の感触としては、Prismaのスキーマをベースにマイグレーションができるのは便利そうだなという感じでした。
これについてはIssueも積極的に対応されているようなので、もう少し後にまた試してみようかと思います。
- この記事を書いた直後に 2.0.0-preview020.2 がリリースされており、こちらを使うとPrisma Migrateもうまく動くようだったので試しました。(「Prisma Migrateを使う場合」を追記しました)
- 2020年1月時点では頻繁にバグフィックスなどが対応されリリースされているようです。正式リリースの時点でこの内容が有効かどうかはわかりませんが、様子を追っていきたいと思っています。
以下ではPrisma2を試すにあたってよくあるTODOを管理するREST APIを Prisma Clinet JS で生成したクライアントとExpressを使って実装してみました。
最終的なコードは以下のリポジトリにあるので、実際にコマンドを実行する際などに参考にしてください。
データベースのセットアップ(Prisma Migrateを使わない場合)
先述の通りPrisma Migrateを使うのは諦めたので、DockerでPostgreSQLのコンテナを立ててあらかじめテーブルを作っておきます。
(Prisma Migrateを使いたい場合は記事の最後に追記した内容を見てください)
docker-composeでコンテナを立てるとき init.sql
が実行され User
と TodoItem
というテーブルが作成されます。
$ docker-compose up -d
CREATE TABLE "User" (
user_id bigserial PRIMARY KEY,
name varchar(100) NOT NULL
);
CREATE TABLE "TodoItem" (
todo_id bigserial PRIMARY KEY,
user_id bigint NOT NULL REFERENCES "User" (user_id) ON DELETE CASCADE,
text text NOT NULL
);
CREATE INDEX todo_item_user_id_idx ON "TodoItem" (user_id);
これで実行したPostgreSQLコンテナへlocalhost:5432でアクセスできるようになります。
データベースはローカルで立ててもいいですし、MySQLやSQLiteも使えるのでご自分の環境に合わせて読み替えてください。
Prismaプロジェクトのセットアップ
初期化
以下のコマンドでPrismaプロジェクトの初期化を行います。
$ npx prisma2 init .
これはカレントディレクトリに対して実行していますが、新しくディレクトリを作成する場合は init new-project
のように指定できます。
また prisma2 をグローバルインストールしている場合は以下のようにも実行できます。
$ prisma2 init .
schemaの更新とクライアント生成
初期化時に prisma/schema.prisma
が生成されるので、セットアップしたデータベースに接続するために以下のように書き換えます。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = "postgresql://prisma:prisma@localhost:5432/prisma?schema=public"
}
この状態で introspect
コマンドを実行するとデータベースのテーブルからモデルを生成しSchemaが更新されます。
$ prisma2 introspect
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = "postgresql://prisma:prisma@localhost:5432/prisma?schema=public"
}
model TodoItem {
text String
todo_id Int @id
user_id User
}
model User {
name String
user_id Int @id
todoItems TodoItem[]
}
更新されたSchemaを見るとあらかじめ作成してあった User
と TodoItem
がモデルとして取り込まれています。
この状態でschemaを元にクライアントコードを生成します。
$ prisma2 generate
クライアントコードは node_modules
以下に生成され、普通はignoreされていると思うのでgitでは差分はでません。
ここまできたらクライアントコードを使えるようになっているはずです。
API実装
基本的な構成やコードは以下のサンプルプロジェクトを参考にしていますので、実装についてはかいつまんで紹介します。
PRIMARY KEYについて
例としてUserを追加する場合、サンプルプロジェクトでは以下のようなコードが書かれています。
const result = await prisma.users.create({
data: { ...req.body },
})
user_id
は自動的に割り振られてほしいのですが、 name
のみをリクエストで与えると user_id
がありませんというエラーになってしまいます。
これについてはschemaでPRIMARY KEYに対応するフィールドに @default(autoincrement())
を追加することで解決できました。
ツールで生成されたファイルを直接編集するのはマナーとしてよくないのですが、機能として開発される見込みはありそうなのでこの先に期待というところでしょうか。
エンドポイント追加
Userを追加するエンドポイントは以下のように書くことができます。
(エラーハンドリングや認証は実際はもっとちゃんとしたほうがいいですが)
app.post(`/users`, async (req, res) => {
try {
const result = await prisma.users.create({
data: { ...req.body },
})
res.json(result)
} catch (err) {
console.error(err)
res.sendStatus(400)
}
})
基本的に生成したクライアントによってモデルに対して prisma.models.create
や prisma.models.findOne
のようなメソッドが使えるので他のORMを使ったことがあればある程度使い方はイメージできそうです。
リレーションに関する表現は慣れが必要そうですが、全体的には結構使いやすそうだなと思いました。
Prisma Studio
最後にデータベースの管理画面として使える Prisma Studio を試してみました。
これまでの設定ができていればコマンド一発で管理画面を開くことができます。
(現在は --experimental
オプションの指定が必要です)
$ prisma2 studio --experimental
コマンドを実行するとlocalhost:5555で Prisma Studio を開くことができます。
既存のデータを確認したり、新しいレコードを作成したりすることができました。
見たところよくあるGUIツールとそこまで変わらないんじゃないかという感じですが、この先便利な機能が使えるようになるかもしれません。
まとめ
Prisma2を使って簡単でありますがREST APIを実装してみました。
現状プレビュー版なのでバグが残っていたり機能が不足しているような印象はありますが、それぞれのツールが提供する機能はAPI開発のプロセスで役に立ちそうだなという感覚もありました。
過去にExpressを試そうと思った時マイグレーションやORMのツールを選ぶだけで手が止まってしまった記憶がありますが、Prisma2はこのあたりの面倒なところを解決してくれるかもしれません。
一応時期は違いますが2020年中にはそれぞれのツールが正式にリリースされる予定とのことなので、状況を追いつつ小さなプロジェクトで試してみようかと思います。
Prisma Migrateを使う場合
Prisma Migratre を使ってスキーマの定義、マイグレーションの実行をするパターンも試してみました。
基本的な使い方は他のマイグレーションツールに近いかと思います。
マイグレーションファイルの生成
schema.prisma
を更新する(モデルの追加、フィールドの追加など)prisma2 migrate save --experimental
でスキーマの差分をもとにprisma/migrations
以下にマイグレーションファイルを生成されるマイグレーションを適用
prisma2 migrate up --experimental
でマイグレーションの差分が適用されるマイグレーションのロールバック
prisma2 migrate down --experimental
でマイグレーションをロールバック
マイグレーション後は generate
コマンドでクライアントを生成して使うだけです。
スキーマの書き方自体はまだあまり見ていませんが、スキーマの表現力が高くなればこのパターンでも運用していけるかもしれません。