[[📒Articles]] > [[📒2021 Articles]] ![[2021-06-30.jpg|cover-picture]] ## はじめに [[TypeScript]]の[[ORM]]として近年注目されている[[Prisma]]を少し使ってみた。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://www.prisma.io/images/favicon-32x32.png" /> <span class="link-card-v2-site-name">Prisma</span> </div> <div class="link-card-v2-title"> Prisma | Instant Postgres plus an ORM for simpler db workflows </div> <div class="link-card-v2-content"> Build, fortify, and grow your application easily with an intuitive data model, type-safety, automated migrations ... </div> <img class="link-card-v2-image" src="https://cdn.sanity.io/images/p2zxqf70/production/384386cb3c2c21b3ad27c6b6758547fe18b08ac1-1200x630.png" /> <a href="https://www.prisma.io/"></a> </div> 何かを作ったわけではなく軽くいじった程度だ。雰囲気を軽く知りたいという方向けであり、[[Prisma]]の知見を知りたい方に有用な情報はないと思う。 ## プロジェクトの取得 [Download the starter project]のコマンドを使い、以下の構成を作る。 [Download the starter project]: https://www.prisma.io/docs/getting-started/quickstart#1-download-the-starter-project ```ls  . ├──  package.json ├──  prisma │ ├──  dev.db │ └──  schema.prisma ├──  script.ts └──  tsconfig.json ``` ## 依存関係のインストール 中に入って`npm i`すればOK。 ## ソースコードを書く `script.ts`を開く。 ```ts:script.ts import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() // A `main` function so that you can use async/await async function main() { // ... you will write your Prisma Client queries here } main() .catch(e => { throw e }) .finally(async () => { await prisma.$disconnect() }) ``` ### すべてのユーザーを取得 `main`関数の中に実装を追加する。 ```ts:script.ts#main() async function main() { const users = await prisma.user.findMany(); console.dir(users, { depth: null }); } ``` 実行すると`User`テーブルの全レコードを取得/表示できる。 ```bash $ npm run dev [ { id: 1, email: '[email protected]', name: 'Sarah' }, { id: 2, email: '[email protected]', name: 'Maria' } ] ``` ### Where句の指定 `where`に条件句を指定する。 ```ts:script.ts#main() async function main() { const users = await prisma.user.findMany({ where: { name: "Maria" }, }); console.dir(users, { depth: null }); } ``` 実行すると`User`テーブルで`name = 'Maria'`なレコードのみ取得/表示できる。 ```bash $ npm run dev [ { id: 2, email: '[email protected]', name: 'Maria' } ] ``` ### 関連テーブルのプロパティを含めて取得 `include`に`posts`を指定すると、関連テーブルの`posts`を含めた結果が取得できる。よしなに補完もされる。 ```ts:script.ts#main() async function main() { // usersの型は (User & {posts: Post[]})[] const users = await prisma.user.findMany({ include: { posts: true }, }); console.dir(users, { depth: null }); } ``` ```bash $ npm run dev [ { id: 1, email: '[email protected]', name: 'Sarah', posts: [] }, { id: 2, email: '[email protected]', name: 'Maria', posts: [ { id: 1, title: 'Hello World', content: null, published: false, authorId: 2 } ] } ] ``` ## テーブル定義を変える [Using Prisma Migrate TypeScript PostgreSQL]に従ってテーブル定義を変えてみる。 [Using Prisma Migrate TypeScript PostgreSQL]: https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/using-prisma-migrate-typescript-postgres データベースを直接いじるのではなく`schema.prisma`ファイルを変更する。ここからデータベースとClientが自動生成される。以下のようにしてみた。 ```schema:schema.prisma datasource db { provider = "sqlite" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model Human { id Int @id @default(autoincrement()) name String address String? } ``` データベースから元々あったテーブルを消してから、[[Prisma Migrate]]を実行する。 ```bash $ 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" The following migration(s) have been created and applied from new schema changes: migrations/ └─ 20210630135457_init/ └─ migration.sql Your database is now in sync with your schema. ✔ Generated Prisma Client (2.26.0) to .\node_modules\@prisma\client in 86ms ``` [[ジャーナルファイル]]や実行したsqlファイルなど作成される。 ```ls  ./prisma ├──  dev.db ├──  dev.db-journal ├──  migrations │ ├──  20210630135457_init │ │ └──  migration.sql │ └──  migration_lock.toml └──  schema.prisma ``` `migration.sql`は実行済みなので、データベースに`Human`テーブルが作成されている。 ## クライアントコードの自動生成 [Install Prisma Client]を参考に[[Prisma Client]]を作成する。既にインストールはされているため`npm install`はスキップする。 [Install Prisma Client]: https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/install-prisma-client-typescript-postgres/ `prisma generate`コマンドを実行する。 ````bash $ npx prisma generate Environment variables loaded from .env Prisma schema loaded from prisma\schema.prisma ✔ Generated Prisma Client (2.26.0) to .\node_modules\@prisma\client in 83ms You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client ``` import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() ``` ```` `node_modules/@prisma`配下に作成されるのが実にユニークだ。`schema.prisma`さえバージョン管理しておけば、作成された[[Prisma Client]]を管理する必要はない。 なお、`npm install @prisma/client`を実行したあとにも`prisma generate`は自動実行される。そのため、初回インストール時に個別のコマンドを打つ必要はない。 ## ソースコードの修正 先ほど実装した`script.ts`が早速エラーとなっている。[[Prisma Client]]の定義が変わったためだ。なんという安心感だろうか。 ![[Pasted image 20210630231051.png]] ### データの登録 データベースがカラッポになったので一度登録処理を書く。 ```ts:script.ts#main async function main() { const records = [ { name: "Ichiro", address: "America" }, { name: "Jiro", address: "Japan" }, { name: "Saburo" }, ]; await Promise.all(records.map((r) => prisma.human.create({ data: r }))); } ``` 実行すると`Human`テーブルに3つのレコードが登録される。 ```bash npm run dev ``` 必須のプロパティ(`name`など)がない場合は型エラーとなる。冗長なプロパティがある場合は実行エラーにならないが、データベースにはデータが挿入されなかった。設定で調整できるのかもしれない。 ### データの取得 先ほどと同じようなコードを書く。 ```ts:script.ts#main async function main() { const humans = await prisma.human.findMany(); console.dir(humans); } ``` 先ほど登録したデータを取得できる。 ```bash $ npm run dev [ { id: 1, name: 'Jiro', address: 'Japan' }, { id: 2, name: 'Saburo', address: null }, { id: 3, name: 'Ichiro', address: 'America' } ] ``` ## まとめ [[Prisma]]を使って以下の流れを一通り体験してみた。 - サンプルプロジェクトを動かす - `schema.prisma`ファイルでテーブル定義を変更する - `prisma migrate`コマンドでDBテーブルを自動生成する - `prisma generate`コマンドで[[Prisma Client]]を自動生成する 特筆すべきはSchemaファイルの定義さえ管理すれば、DBもClientコードも同期できることだろう。使い始める前に少し学習が必要だが、慣れれば安全で気持ち良い開発ができそうだ。 パフォーマンスが求められる要件だとORMは諸刃の剣になり得る。だが、そうでない場合なら、[[TypeScript]]や[[Node.js]]を使うのであれば[[Prisma]]は[[ORM]]として有力な選択肢になるだろう。