📖

Prisma 基礎

2021/01/16に公開3

Prismaのドキュメントを読んでみたので、基本的なことをまとめていきます。

Primsaとは

  • オープンソースのORM(Object-relational mapping)
  • Node上のアプリケーションで直接DBに接続し、クエリー発行が可能
  • RDB周りの処理をより簡単に行えるようにし、開発者の生産性を向上させることを目的に開発
  • Next.jsアプリケーションでDBを扱う際に特に有用
  • Schemaファイルから型情報が生成され、クエリ結果がタイプセーフになる

対応状況

言語

  • JavaScript
  • TypeScript
  • Go (開発途中)

データベース

  • PostgreSQL
  • MySQL
  • SQLite
  • SQL Server (開発途中)

主な構成要素

Prisma Model

アプリケーションで使用するモデルを表現する。
モデル内でテーブルやカラムの定義を行う。
また、Prisma Clientでクエリーを発行するために必要となる。

Prisma schema

メインとなる設定ファイル。
下記の要素を定義し、データベースクライアントやマイグレーションファイルの生成を行う。

  • データソース: データベースへの接続情報
  • ジェネレータ: データモデルを元に生成するデータベースクライアントを指定
  • Prisma Model

Prisma Client

データベースクライアント。
Prisma Modelでの型情報を使用して、クエリーの結果は型安全になる。

Prisma Migrate

Prisma Schemaの情報をベースにマイグレーション周りの処理が可能になる。

Prisma Studio

データベース上のデータを閲覧・編集するGUI。

実際の使用手順

1. Prisma schemaファイルの作成

Prismaを使用する際は必ず作成しなければならない。
独自のデータモデリング言語を使用している。

// データベースの接続情報
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// generateコマンドで生成するものを指定
generator client {
  provider = "prisma-client-js"
}

// アプリケーションで使用するモデル
model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields:  [authorId], references: [id])
  authorId  Int?
  @@unique([authorId, title])
  @@index(fields: [title, content], name: "main_index")
}
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
  role    Role     @default(USER)
}

enum Role {
  USER
  ADMIN
}

2. Prisma Clientの生成

@prisma/clientをインストール。

npm install @prisma/client

生成コマンドを叩くことで、Prisma schemaを読み、Prisma Clientコードを自動で作成できる。

prisma generate

モデルなどを変更した際は、生成コマンドを再度叩くことで、Prisma Clientコードを更新できる。

生成されたコードはnode_modules/@prisma/clientに置かれ、使用する際はここからインポートする。

3. クエリーの発行

@prisma/clientからインポートして、Prisma Clientインスタンスを作成し、クエリーを発行する。

import { PrismaClient } from '@prisma/client'

// Fetch all posts (in /pages/index.tsx)
export async function getStaticProps() {
  const prisma = new PrismaClient()
  const posts = await prisma.post.findMany()
  return {
    props : { posts }
  }
}

// Display list of posts (in /pages/index.tsx)
export default ({posts}) =>
  <ul> 
   {posts.map(post => (
     <li key={post.id}>{post.title}</li>
    ))}
  </ul>
);

クエリーの例

  • 全ユーザーの取得
const allUsers = await prisma.user.findMany()
  • 紐づけられた投稿と一緒に、全ユーザー取得
const allUsers = await prisma.user.findMany({
  include: { posts: true },
})
  • 「prisma」を含む全投稿を取得
const filteredPosts = await prisma.post.findMany({
  where: {
    OR: [
      { title: { contains: "prisma" } },
      { content: { contains: "prisma" } },
    ],
  },
})
  • ユーザーと投稿を一緒に作成する
const user = await prisma.user.create({
  data: {
    name: "Alice",
    email: "alice@prisma.io",
    posts: {
      create: { title: "Join us for Prisma Day 2020" },
    },
  },
})
  • 条件に合った投稿の更新
const post = await prisma.post.update({
  where: { id: 42 },
  data: { published: true },
})

マイグレーション

マイグレーションの実行

このコマンドを実行することで、下記のようなマイグレーションファイルが生成されると同時に実行される。
先述の通り、SchemaファイルのPrisma Modelより生成される。

prisma migrate dev --preview-feature
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL,
    "name" TEXT NOT NULL,
    PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL,
    "title" TEXT NOT NULL,
    "published" BOOLEAN NOT NULL DEFAULT true,
    "authorId" INTEGER NOT NULL,
    PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Post" ADD FOREIGN KEY("authorId")REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

テーブル構造の変更

テーブル構造を変更する際にも、SchemaファイルのPrisma Modelを更新し、同様のマイグレーションコマンドを実行することで、新たなマイグレーションファイルが生成され、実行される。

Discussion

qaynamqaynam

とても参考にできる記事です!
一つprismaに関して質問してよろしいでしょうか?

activeRecordのようなtry catchのtransaction張りたくて、どこ探しても出てこないです。
一応公式には$transactionというヘルパーがあるみたいですが、引数として配列のPromiseを渡さなければいけないみたいです。

公式ではこういう風に👇書いてます。


const prisma = new PrismaClient();

await prisma.$transaction([
   PromiseA,
   PromiseB
]);

やりたいことは、PromiseAで成功して帰ってくるuser_idをPromiseBの引数に入れたいですが、どうやって実現すればいいのかわからないです。

お力貸していただけないでしょうか?

rabitarochanrabitarochan

すでに解決しているかもしれませんが、バージョン 2.29.0 より Preview の機能に「Interactive Transactions」という機能が実装されており、トランザクション内で関数を実行することができるようになっています。

https://www.prisma.io/docs/guides/performance-and-optimization/prisma-client-transactions-guide#interactive-transactions-in-preview

const prisma = new PrismaClient();

const createdProjectData = await prisma.$transaction(async (db) => {
  // 1. ユーザーを取得する。
  const ownerUser = await db.users.findFirst({ where: /* 省略 */ });

  // 2. ユーザーの値を使い、なんらかの処理を実行する。
  const newProjectData = {
    owner_user_id: user.user_id,
    /* 省略 */
  };
  const createdData = await db.projects.create({
    data: newProjectData,
    /* 省略 */
  });

  return createdData;
});

Preview の機能のため、prisma.schema ファイルにて明示的に有効化する必要があります。
詳細は、上記リンクをご参照ください。

qaynamqaynam

ありがとうございます!
ハードコーディングしていたので、試してみます!