🦔

次世代の Node.js ORM 、Prisma をサクッと入門する

2022/10/31に公開

最近tRPCを勉強をしてるところ、公式のNext.jsサンプルリポジトリにPrismaという謎のORMライブラリー使用されてることに気づき🧐、気になって仕方ないので、実際触ってみた内容をまとめます。

Prismaとは

Prisma は、下記のツールで構成される次世代のORMライブラリーです。

  • Prisma Client : Node.js および TypeScript 用の自動生成された型安全なクエリ ビルダー
  • Prisma Migrate : 宣言型データ モデリングおよび移行システム
  • Prisma Studio : データベース内のデータを表示および編集するための GUI

Prisma は、任意のNode.js または TypeScript バックエンド アプリケーション (サーバーレス アプリケーションおよびマイクロサービスを含む) で使用できます。これは、REST API、GraphQL API、gRPC API、またはデータベースを必要とするその他の全てのもの。
https://www.prisma.io/

Prisma使ってマイグレーションする際に、移行ファイルは素のSQLで書かれるところは好印象です😊

クイックスタート

まずはプロジェクトの作成とPrismaのセットアップ、ゼロベースから作ります。

$ mkdir prisma_test
$ cd prisma_test

Prismaを動作するにはNode.js v14.17.0 以降のバージョンが必要です。
npmでinitしてますがyarnでも構いません。

$ npm init -y 
$ yarn add -D typescript ts-node @types/node
$ touch tsconfig.json

tsconfig.jsonに下記の内容を追加します。

tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

次はPrismaをインストールします。

$ yarn add -D prisma

最後にPrisma CLI のコマンドを使用してPrismaをセットアップします。

$ npx prisma init --datasource-provider sqlite

sqlite使用するのは、テスト用のデータベースを用意する手間を省くためです、
この後マイグレーション実行する際に、自動的にsqliteファイル作ってくれます。

ここまで実行したら、ファイル構成は以下の通りになるはずです。

prisma_test
 |-- .env
 |-- .gitignore
 |-- node_modules
 |-- package.json
 |-- prisma
 |-- |-- schema.prisma
 |-- tsconfig.json
 |-- yarn.lock

Prisma スキーマでデータをモデル化する

prismaフォルダ配下にあるschema.prismaファイルにUserPostモデルを追加します。

prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

+ model User {
+  id    Int     @id @default(autoincrement())
+  email String  @unique
+  name  String?
+  posts Post[]
+ }

+ model Post {
+  id        Int     @id @default(autoincrement())
+  title     String
+  content   String?
+  published Boolean @default(false)
+  author    User    @relation(fields: [authorId], references: [id])
+  authorId  Int
+ }

Prismaスキーマのモデルには、主に2つの役目があります。

  • 基礎となるデータベースのテーブルを表す
  • 生成された Prisma Client API の基盤として機能する

次はマイグレーションを実行して、テーブルを作ります。

npx prisma migrate dev --name init

実行後、ターミナルで以下のログが表示されると思います。

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

SQLite database dev.db created at file:./dev.db

Applying migration `20221031025300_init`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20221031025300_init/
    └─ migration.sql

これで、テーブルの作成とマイグレーション履歴ファイルが作られました。
sqliteのデータベースとなるファイルは存在しなかったのですが、実行後、prismaフォルダの配下にdev.dbが作られます。

Prisma Studioを使ってできたテーブルを確認できます。

npx prisma studio

Prisma Client を使用してデータベースにデータを追加する

prismaフォルダの配下にscript.tsファイルを作ります。
userテーブルにデータを追加してみます。

script.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  const user = await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
    },
  })
  console.log(user)
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

下記のコマンドでscript.tsを実行すると、テーブルにデータが追加されます。

$ npx ts-node script.ts 

{ id: 1, email: 'alice@prisma.io', name: 'Alice' }

追加したデータを取得する場合、main関数の中身を修正します。

script.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
+  const users = await prisma.user.findMany()
+  console.log(users)
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

スクリプトを再度実行します。

$ npx ts-node script.ts 

[{ id: 1, email: 'alice@prisma.io', name: 'Alice' }]

prisma studioからも確認できます。

その他のCRUD操作は公式ドキュメントを参考にしてください。
https://www.prisma.io/docs/concepts/components/prisma-client/crud

テーブルの変更

定義されたPostモデルにtestカラムを追加します。

prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String? 
  posts Post[]
}

model Post {
 id        Int     @id @default(autoincrement())
 title     String
 content   String?
+ test     String
 published Boolean @default(false)
 author    User    @relation(fields: [authorId], references: [id])
 authorId  Int
}

下記のコマンドでマイグレーション実行します。

npx prisma migrate dev --name add_post_test

実行後、下記のログが出力されます。

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

Applying migration `20221031055326_add_post_test`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20221031055326_add_post_test/
    └─ migration.sql

prisma/migrations/20221031055326_add_post_testフォルダは以下にmigration.sqlファイル作られました。

中身は以下の通りです、素のsqlで変更内容書いてくれるのはわかりやすくていいですね😊

-- AlterTable
ALTER TABLE "Post" ADD COLUMN "test" TEXT;

次はさきほど追加したカラムを削除してみます。

prisma/schema.prisma
... 一部省略
model Post {
 id        Int     @id @default(autoincrement())
 title     String
 content   String?
- test     String
 published Boolean @default(false)
 author    User    @relation(fields: [authorId], references: [id])
 authorId  Int
}

下記のコマンドでマイグレーション実行します。

npx prisma migrate dev --name delete_post_test

ログは以下の通りです。

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

Applying migration `20221031055549_delete_post_test`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20221031055549_delete_post_test/
    └─ migration.sql

prisma/migrations/20221031055549_delete_post_testフォルダの配下にmigration.sqlファイルが作られました。

中身は以下の通りです。

/*
  Warnings:

  - You are about to drop the column `test` on the `Post` table. All the data in the column will be lost.

*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Post" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "title" TEXT NOT NULL,
    "content" TEXT,
    "published" BOOLEAN NOT NULL DEFAULT false,
    "authorId" INTEGER NOT NULL,
    CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Post" ("authorId", "content", "id", "published", "title") SELECT "authorId", "content", "id", "published", "title" FROM "Post";
DROP TABLE "Post";
ALTER TABLE "new_Post" RENAME TO "Post";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

その他のマイグレーション操作は公式ドキュメントを参考にしてください。
https://www.prisma.io/docs/concepts/components/prisma-migrate

おわりに

まだ深く使った訳ではありませんが、ORMライブラリーとしては優秀なのではと思いましたね、最近作るシステムの構成は疎結合のものが多い中で、ORMのパーツとしてPrismaを採用する価値は十分あると個人的に考えてます。

Discussion