.../articles/

Prisma2を使ってREST APIを実装してみる

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 が実行され UserTodoItem というテーブルが作成されます。

$ 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を見るとあらかじめ作成してあった UserTodoItem がモデルとして取り込まれています。

この状態で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.createprisma.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 を使ってスキーマの定義、マイグレーションの実行をするパターンも試してみました。

基本的な使い方は他のマイグレーションツールに近いかと思います。

  1. マイグレーションファイルの生成
    schema.prisma を更新する(モデルの追加、フィールドの追加など)
    prisma2 migrate save --experimental でスキーマの差分をもとに prisma/migrations 以下にマイグレーションファイルを生成される

  2. マイグレーションを適用
    prisma2 migrate up --experimental でマイグレーションの差分が適用される

  3. マイグレーションのロールバック
    prisma2 migrate down --experimental でマイグレーションをロールバック

マイグレーション後は generate コマンドでクライアントを生成して使うだけです。

スキーマの書き方自体はまだあまり見ていませんが、スキーマの表現力が高くなればこのパターンでも運用していけるかもしれません。

.../articles/

Articles

記事

AWS AmplifyにmonorepoのNext.js(App Router)をデプロイする

AWS AmplifyにmonorepoのNext.js(App Router)をデプロイする

monorepo管理しているNext.jsをAmplifyにデプロイしようとした際にいくつか躓く内容があったのでまとめておきます。

リモートワーク・オンライン会議でも、スムーズに制作を進めるために大切なこと[資料編]

リモートワーク・オンライン会議でも、スムーズに制作を進めるために大切なこと[資料編]

コロナ禍の影響により、リモートワークの導入をおこなっている制作会社も多く、実際に弊社でも導入しています。

売れるECサイトデザインを作るために。参考にしたいおしゃれな事例の探し方。

売れるECサイトデザインを作るために。参考にしたいおしゃれな事例の探し方。

売れるECサイトのデザインは、「この形式」という決まりはありません。ECサイトで売り上げを上げるなら、しっかりとしたコンセプトと、コンセプトを決定するまでのリサーチが必要です。

制作会社の考える、業務効率化ツールのおすすめ。個人でも使いやすいサービスなど。

制作会社の考える、業務効率化ツールのおすすめ。個人でも使いやすいサービスなど。

新型コロナウイルス感染拡大の影響で、リモートワークが主流になり、弊社でも週のほとんどは各自宅で作業をしています。

Figmaでデザインのコミット履歴を残せるプラグイン【Thought Recorder】をリリースしました

Figmaでデザインのコミット履歴を残せるプラグイン【Thought Recorder】をリリースしました

Figmaを利用するWebデザイナーの助けになれると嬉しいです。使い方は本記事をご覧ください。

ECの構築方法、おすすめのECサービス。

ECの構築方法、おすすめのECサービス。

ファッションや家電、スーパーの買い物でさえもECサイトを利用することが当たり前になりました。加えて新型コロナウイルスの影響もあり、弊社にも「どんなプラットフォームを利用したら良いか」「どれくらいコストがかかるのか」などECに関するさまざまなご相談を頂きます。

FastAPIのスキーマクラスをOpenAPIから生成する方法

FastAPIのスキーマクラスをOpenAPIから生成する方法

PythonでAPIを構築する要件があり、フレームワークに比較的モダンなFastAPIを採用しました。FastAPIはバックエンドの開発を行えば自動でOepnApi定義を生成する機能が備わっていますが、今回はこれを使わず、事前に用意したOepnApi定義からFastAPIで利用するスキーマクラスを生成する方法を紹介します。

Laravel 日本一解りやすい全文検索のマイグレーション記載方法解説

Laravel 日本一解りやすい全文検索のマイグレーション記載方法解説

Laravel + MySQLで全文検索を実装する

とあるPythonのソースで sys.path.append としたく無かった話

とあるPythonのソースで sys.path.append としたく無かった話

とあるプロジェクトのとあるソースコードのレビューをしてた時、「ソースコードの参照がうまくいってなかったので修正しました」とレビュー依頼がきました。 ディレクトリ構造 ``` module L __init__.py L main.py L tests L __init__.py L test_main.py ``` ソースコード ``` python tests/test_main.py sys.path.append(os.path.abspath("..")) from main import fuga ``` 今まで案件でPythonに触れる機会も結構ありましたが、なんとなく使ってきた部分も多く、この書き方が良いのか悪いのか判別できなかったので、改めてPythonのモジュールのインポートに関して調べてみたのでブログにしました。普段PHPを書いている事が多くPythonに関して何も分からないので初心者向けの内容になっていると思います。

GiFT1号目新卒デザイナーの2021年振り返り

GiFT1号目新卒デザイナーの2021年振り返り

いつの間に、年末ですね。入社してもう、9ヶ月も立っていたようです。2021年の振り返りを記事にしました。

すべての記事

お問い合わせ