🔺

Prisma ORMを使いこなす ~歴史と対RDB運用の知見を添えて~

2024/02/13に公開

CloudbaseはPostgreSQLのORMとしてPrismaを使用しています

CloudbaseはAWS, Azure, Google Cloud等のパブリッククラウドを対象にしたセキュリティリスクの検出・管理SaaSです。
個人情報の入ったS3バケットを公開してしまうなどの設定ミスや、近年騒がれたLog4Shellなどの脆弱性をエージェントを使用せず検出し、その修正をサポートするプロダクトです。


https://cloudbase.ink/

CloudbaseではAPIサーバとしてNode.js、DBとしてPostgreSQLを使用し、そのORMとしてPrismaを使用しています。本記事では入門を超えて本番環境でPrismaを使いこなすために必要な知識、弊社がPrismaを運用する中で得た知見を共有していきます。

対象読者

  • PrismaをRDBのORMとして使っている人
  • 雰囲気でPrismaを使っている人
  • これからPrismaを用いて開発する予定で、運用の知見や注意すべきポイントを知りたい人

Prismaを少しでも扱ったことがある人を対象としており、インストール方法や使い方などは解説しません。

Prismaとは

概要

PrismaはNode.js, TypeScript向けのORMライブラリです。SQLから一歩抽象化し開発者体験を向上させるAPI、クラスではなく純粋なオブジェクトを扱うことでモデルクラスの肥大化を防止、強力な型付けによる安全性を持たせることなどを思想に開発されています。[1]

この記事を執筆している現在は扱えるデータベースとして以下をサポートしています。[2]

  • PostgreSQL
  • MySQL
  • SQLite
  • MongoDB
  • Microsoft SQL Server
  • PlanetScale
  • CockroachDB
  • Supabase
  • Neon
  • Turso

歴史

実はPrismaは、現在のようなDBのための純粋なORMライブラリとして登場したものではありませんでした。

Prismaの開発を主導している会社は元々GraphcoolというGraphQLのBaaSを開発していたベルリンにあるスタートアップでした。

その後その会社がGraphcool BaaSを支えるコンポーネントの一部だったものをオープンソースライブラリとして切り出してPrisma 1.0が生まれました。最初のPrismaはGraphQLのためのライブラリで、GraphQLとDB間のデータ層のやり取りを簡易化するツールとして位置付けられていました。2018年当時、この内容でシードラウンドとして4.5万ドルを資金調達しています。[3]

それから約1年が経過した2019年6月、Prisma 2.0のプレビュー版がアナウンスされます。Prisma 2.0ではGraphQL色が一気に薄れ、今の形に近い純粋なDBのORMライブラリとして生まれ変わります。ここではPhotonとLiftという名前がついたコンポーネントによって構成されていました。[4]

そこからさらに9ヶ月程経過した2020年3月、Prisma 2.0のβ版が公開され今のPrismaとほぼ同じ形となりました(Photon, Liftという名前も消えてそうでした)。[5]

また、その3ヶ月後に1.2万ドルを調達しPrismaの開発を加速させていきます。(時を同じくしてGraphcoolはサービスの提供を終了していました[6]

その後破壊的変更はあれど1.0~2.0程の大きな思想の変化はなく、対応するDBの拡大やさらなる改善、Prismaを活用した独自のクラウドサービスなども展開し、今の成長したPrismaがあります。(この記事を執筆している2024/02現在は5.9.1がリリースされています)

このようにPrismaは元々GraphQLのエコシステムとして開発されていたものでしたが、数年掛けて思想やアーキテクチャを変化させて今の形にたどり着いたのでした。

アーキテクチャ

現在のPrisma ORMのアーキテクチャは大きく分けるとPrisma ClientとPrisma Engineという2つのコンポーネントに別れています。1つ目のPrisma ClientはNode.js製のコンポーネントであり、普段我々がPrismaを使用する際に触っているものです( prisma.user.findMany(...) など )。もう1つのPrisma EngineはPrisma Clientからのリクエストを受けて実際にデータベースとやりとしりてクエリの発行等を行うコンポーネントです。Prisma EngineはRust製[7]で、Prisma Clientとは全く別のコンポーネントとしてビルドされたバイナリで動いています。つまりPrismaは prisma.user.findMany(...) 等を実行してもNode.jsが直接クエリを発行するわけではなく、全く別の独立したPrisma Engineを介してクエリが発行されているのです。


Prisma Client, Engine, Databaseのやり取りの様子( https://www.prisma.io/docs/orm/more/under-the-hood/engines

ではPrisma ClientとPrisma Engineはどのようにやりとりしているのでしょうか。
実はこの2コンポーネント間のやりとりには2つの方法が提供されています。libraryモードとbinaryモードです。

libraryモード

libraryモードは現在のPrismaの最新バージョンではデフォルトで採用される[8]方法です。これはPrisma EngineをNode.jsのネイティブアドオンとして読み込み、Node-API経由でやりとりする方法です。Node-API経由でやりとりすることで後述するbinaryモードと比べて通信のオーバヘッドが低減されるため、現在推奨されているモードになります。[8:1]

Interfaceがこちらで定義されており、libraryとしてPrisma Engineへリクエストしている様子がこちらから伺えますね。

ちなみにlibraryはPrisma 3.x, 2.20.0から追加された方法になります。[9]

binaryモード

binaryモードはサイドカープロセスとしてPrisma Engineを稼働させ、HTTPで通信する方法です。Prisma 3.x, 2.20.0以前はbinaryモードしか存在しませんでした。


https://www.prisma.io/docs/orm/more/under-the-hood/engines#the-query-engine-at-runtime

HTTP POSTしている様子がこちらから伺えます。HTTP上でGraphQLで通信しているようで、ここにPrismaの歴史が垣間見えます。

これらのモードは schema.prismagenerator client.engineType で選択することが可能です。

generator client {
  provider   = "prisma-client-js"
  engineType = "binary" // または library
}

繰り返しになりますが、binaryモードはHTTPによる通信となるため通信のオーバヘッドがかかります。そのため特殊な理由がない限りはlibraryモードを使うことが望ましいです。

Prisma Engineはどこから来るのか?

では、全く別のバイナリであるPrisma Engineはどこから取得されるのでしょうか?調査したところデフォルトでは https://binaries.prisma.sh/ からダウンロードしているようです。 [10]

また、気になったのでアクセス先を調べてみたところCloudflareのようでした。

❯ dig +short binaries.prisma.sh
binaries-failover.prisma.sh.
binaries.prisma.sh.cdn.cloudflare.net.
104.22.23.109
104.22.22.109
172.67.4.173

ちなみにこれは事前にビルドされたバイナリであるため、Prismaを動かすシステムのOS、アーキテクチャによって使用するバイナリを変える必要があります。そこで darwin-arm64 linux-musl-openssl-3.0.x のような名前で各環境ごとに用意されています。サポートしている環境一覧はこちらから閲覧できます。

どのバイナリを使用可能にするかを schema.prismagenerator client.binaryTargets から選択することができます。

generator client {
  provider        = "prisma-client-js"
  binaryTargets   = ["native", "rhel-openssl-1.0.x"] // ここで選択する
}

binaryTargets を選択していない場合はデフォルトで native が選択されます。これはPrismaが稼働している環境を自動で特定し、動作するバイナリを自動で選択してくれます。

PrismaClientに渡すDatasource URLのパラメータ

Prismaでは以下のようにDatasource URLを渡すことでデータベースの接続情報等を設定します。

datasource db {
  provider = "postgresql"
  url      = "postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"
}

ただしこのURLは純粋に接続情報を渡すだけではなく、様々なパラメータを設定する責務も担っています。

このパラメータは対象のDBの種類によって異なります。DB毎の全てのパラメータについては以下を参照してください。
https://www.prisma.io/docs/orm/overview/databases

ここでは対RDBでPrismaを運用する上で特に重要になるであろう一部のパラメータについて解説していきます。

connection_limit

Connection poolで保持する最大のコネクション数を設定します。デフォルトでは CPU数 * 2 + 1 が設定されています。

Connection poolとは?

Prismaはトランザクション開始の度に毎回DBとのコネクションを新規に張っているのではなく、一度張ったコネクションを設定された数だけ保持し、それを再利用します。この保持しているコネクションの塊をConnection poolと呼びます。これによってDBとの新規コネクション作成時のあらゆるオーバーヘッドを軽減させ、パフォーマンスを向上させることを狙っています。ただしシステムによっては寧ろこのConnection poolが悪い副作用を発生させる場合もあるため、銀の弾丸というわけではありません。

ちなみにConnection poolはPrisma特有の仕組みというわけではなく、昔から言語問わず様々なシステム、ライブラリで採用されている枯れた仕組みです。

ただし、よくあるAPIサーバでPrismaを使用しこの connection_pool をデフォルトの値にしていると、リクエスト数が大量になれば簡単にpoolが枯渇し、後述の pool_timeout の値も相まってコネクション取得のタイムアウトエラーが発生する可能性が高まります。稼働しうるAPIサーバのインスタンス数やDB側の max_connections の値などを考慮して適切な値へ設定することが望ましいです。

pool_timeout

Connection poolが持つコネクション数が connection_limit に達し、そのコネクションが全て使用中の場合に新たにPrisma Clientからトランザクションを開始したい場合、Connection poolに使用していないコネクションが戻ってくるのを待機することになります。この待機時間の上限値を設定するのが pool_timeout です。超過した場合タイムアウトエラーになります。単位は秒で、デフォルトは10秒が設定されています。0を指定するとtimeoutが発生しなくなります。

タイムアウトエラー例

Timed out fetching a new connection from the connection pool. More info: http://pris.ly/d/connection-pool (Current connection pool timeout: x, connection limit: x)

connect_timeout

Connection poolが connection_limit の上限に達していない場合、新たにトランザクションを開始しようとするとPrismaは新たにDBとのコネクションを作成します。この時、例えばDBがhangしている場合などコネクション作成に時間がかかるケースがあり、これを最大何秒待つかを設定するのが connect_timeout です。超過した場合タイムアウトエラーになります。デフォルトは5秒が設定されており、0を指定するとtimeoutが発生しなくなります。

socket_timeout

socket_timeout はPrismaからDBへ1つクエリを投げた時にDBからのレスポンスを待機する最大の時間です。例えばバッチ処理等で実行に時間がかかるクエリを実行した時、 socket_timeout の秒数を超過するとタイムアウトエラーになります。デフォルトでは値は設定されていません。

タイムアウトエラー例

Context: Socket timeout (the database failed to respond to a query within the configured timeout — see https://pris.ly/d/mssql-connector for more details.).

弊社ではPrismaClientの生成をwrapする関数を作っています

上述したようにPrismaはこれらのパラメータをDatasource URLで設定する必要があります。Datasource URLはDBとの接続情報…つまりDBのユーザ名やパスワードなども入る値であるため、コード上からではなく環境変数を通して渡すのが望ましいです。[11]

そうなった場合上記のパラメータについても環境変数を通して渡す必要があるため、カジュアルに値を変更することが難しくなりがちです(Datasource URL全体がシークレット情報になるため、強い権限を持たないと値を編集することができなかったり、編集ミスをした場合の影響範囲が大きい)。

そこで弊社CloudbaseではPrismaClientの生成をwrapする関数を作ることでこの問題を解決しています。以下はその実装の一部です。

// PrismaClientの生成をwrapする関数。PrismaClientのコンストラクタの`datasources.db.url`にDatasource URLを渡せる。
export const buildPrismaClient = (options?: {
  prismaClientOptions?: Prisma.PrismaClientOptions;
  connectionConfigs?: Partial<PrismaConnectionConfigs>;
}) => {
  return new PrismaClient({
    datasources: {
      db: { url: buildDatabaseURL(options?.connectionConfigs) },
    },
    ...options?.prismaClientOptions,
  });
};

// Datasource URLで設定するパラメータ
export type PrismaConnectionConfigs = {
  //https://www.prisma.io/docs/concepts/database-connectors/postgresql#arguments
  connection_limit: number;
  connect_timeout: number;
  pool_timeout: number;
  socket_timeout: number;
};

// Datasource URLを組み立てる関数
export const buildDatabaseURLWithArgs = (
  databaseURL: string,
  options?: {
    stage?: string;
    connectionConfig?: Partial<PrismaConnectionConfigs>;
  }
) => {
  const url = new URL(databaseURL);

  url.search = new URLSearchParams(
    Object.entries({
      ...(options?.stage ? getPrismaClientConfig(options.stage) : undefined),
      ...options?.connectionConfig,
    }).reduce((prev, [k, v]) => {
      return { ...prev, [k]: v.toString() };
    }, {})
  ).toString();
  return url.toString();
};

また、直接new PrismaClient()せずに必ずこの関数を使用するように独自のeslintルールも作成しています。

Prismaから発行される注意すべきSQL

PrismaはSQLから一歩抽象化したAPIを提供するという思想や、まだ発展途上のライブラリであることから一部のAPIから発行されるSQLについては少し感覚的ではなかったり[12]パフォーマンス的に問題がある場合があります。

そこでここでは、筆者がRDBを対象にPrismaを扱う上で注意すべきと感じたAPIとSQLについて紹介していきます。

findXXX

includeはJOINされるわけではない (将来改善されそう)

Prismaでは include を使用してリレーションを持っているレコードを参照し、結果のオブジェクトに結合して取得することができます。しかしこのincludeDB側でJOINしているわけではない(2024/02のGA機能という前提)点に注意が必要です。

ではどういうクエリが走っているかというと、純粋に2つのクエリに別れます。
以下のようなスキーマを例にします。

ここで以下を実行すると、

await prisma.article.findMany({
  include: {
    user: true,
  },
})

以下の2つのクエリが発行されます。

-- articleの取得処理
SELECT
	"public"."article"."id",
	"public"."article"."body",
	"public"."article"."userId"
FROM
	"public"."article"
WHERE
	1 = 1 OFFSET $1;

-- 上記で取得したarticleに関連付いているuserの取得
SELECT
	"public"."user"."id",
	"public"."user"."name"
FROM
	"public"."user"
WHERE
	"public"."user"."id" IN($1)
	OFFSET $2;

(要は、Railsでいうpreloadです)

これが必ずしも悪というわけでは全く無いのですが、SQLレベルでJOINされている前提で実装してしまうとパフォーマンス上の問題になる可能性もあるため注意が必要です。
現状GAしているPrismaのAPIにはJOIN句を発行するものは提供されていないため(ゴニョゴニョすれば発行できるかもしれませんが)、JOINしたい場合は $queryRaw を使用する必要があります。

一方で、割と最近リリースされた5.7.0ではpreviewFeatureとしてrelationJoinsが追加され、includeでJOINが発行されるようになっています。
https://github.com/prisma/prisma/releases/tag/5.7.0

続く5.8.0でJOINとpreloadをAPI単位で選べるようになり、5.9.0でパフォーマンス改善が入るなど、Prismaとしてはこの辺りの改善に力を入れていそうです。relationJoinsがGAになる日も近いかも知れません。
https://github.com/prisma/prisma/releases/tag/5.8.0
https://github.com/prisma/prisma/releases/tag/5.9.0

whereでリレーション先のカラムを指定するとサブクエリが大変なことに(5.4.0で改善済み)

Prismaでは where でリレーション先のカラムを指定してフィルターすることができます。
ここでまた上記のスキーマを例に取ると、"tockn"というユーザー名のuserが書いた全てのarticleを取得するクエリは以下のように書くことができます。

await prisma.article.findMany({
  where: {
    user: {
      name: 'tockn',
    },
  },
})

もし生のSQLを書く場合はarticleとuserをINNER JOINして、user.name = 'tockn'のようなWHERE句を書くのが定石かなと思います。
しかし実際上記のPrismaの実装では、以下のようなクエリが発行されることになります。

SELECT
	"public"."article"."id",
	"public"."article"."body",
	"public"."article"."user_id"
FROM
	"public"."article"
WHERE ("public"."article"."id")
IN(
	SELECT
		"t0"."id" FROM "public"."article" AS "t0"
		INNER JOIN "public"."article" AS "j0" ON ("j0"."id") = ("t0"."user_id")
	WHERE ("j0"."name" = $1
		AND "t0"."id" IS NOT NULL))
OFFSET $2;

INNER JOINして絞るのではなく、このようにサブクエリとして「"tockn"というユーザー名のuserが書いた全てのarticleのid」が取得され、それがWHERE IN句に代入される形になっており、パフォーマンス的にかなり厳しいクエリが発行されてしまいます。

そのためこのようなフィルターをしたい場合で対象レコード数が多い場合は $queryRaw を使用することを検討しましょう…と書くつもりだったのですが、この問題は少し前に以下のPRによって改善されました。

https://github.com/prisma/prisma-engines/pull/4235

5.4.0以降のバージョンであれば改善されていそうです。
https://github.com/prisma/prisma/releases/tag/5.4.0

whereとundefinedに気をつけよう

Prismaでは(当たり前ですが)whereでカラムに対してundefinedを渡すと何もフィルターされません。型定義からしても自然でありこれ自体は何も悪い点はないのですが、普段の開発では注意する必要があります。

上記のschemaを再度例に使用し、「リクエストしたユーザが権限を持っているarticleを1つ返す処理」を考え、以下のような実装をしたとします。

const articleID = await 権限を持っているarticleIDを1つ取得する関数(requestUserID)
return prisma.article.findFirst({
  where: {
    id: articleID
  }
})

ここでもし、最初の「権限を持っているarticleIDを1つ取得する関数」の返り値の型が number | undefined で、権限を持っているarticleが1つもない場合はundefinedを返すという仕様だった場合どうなるでしょう…!
本来であれば何も返したくないところですが、whereにはid: undefinedが渡るためこのフィルターは設定されず、権限があるかもわからないarticleの1行目のレコードが返されることになります…!

このような不具合を発生させないためにwhereに渡す値の型には注意する必要があります。また、このタイプの不具合によっては情報漏えい等の重大なインシデントを起こしてしまう可能性があるので、RLSを導入したりクラスタを分離するなどそもそもの仕組みで守ることが望ましいです。

update

prisma.user.update(...) です。これだけ見ると純粋に UPDATE user SET ... が実行されるだけかなと思いがちですが、実は違います。Prismaの update APIは引数の where で指定した更新対象のレコードが存在しなかった場合エラーを投げるという仕様になっています。この「更新対象のレコードが存在しなかった場合エラーを投げる」という仕様を実現するために、実はSQLのUPDATE文の前にSELECT文が走ります(!)

そのため大量にUPDATEを実行したい場合などパフォーマンス面が気になる場合は、余分なSELECTが走ってしまうことを避けるためにupdateManyを使用することが望ましいです。updateManyであれば前段でSELECTが走ることはありません。

delete

prisma.user.delete(...) ですが、これも update と全く同じでDELETE文を発行する前にSELECT文が走ります。
これが困る場合の対処法としても同じくdeleteManyを使用することが望ましいです。

$queryRaw, $executeRawの活用

Prismaに提供されているAPIだけでは目的のクエリを達成できない場合があります。そこで必要になるのが$queryRaw, $executeRawといった生SQLを実行する関数です。ただし変数を渡したい場合や動的にSQLを組み立てたい場合などは少し工夫が必要になることがあるため、チートシート的に共有しようと思います。

WHEREの値として配列を渡したい時

$queryRawに書いたWHEREの条件に配列を渡したいことが頻繁にあると思います。その場合はPrisma.join()を用いて以下のように書くことができます。

const userIDs = [1, 2, 3]
await prisma.$queryRaw`
SELECT
  *
FROM
  article
WHERE
  user_id IN (${Prisma.join(userIDs)})`

${Prisma.join(...)}を囲っている括弧()を忘れずに!)

もしここでuser_id IN (${userIDs}) のように書いても残念ながらエラーになってしまうのでPrisma.join()を使用する必要があります。

SQLを動的に変えたい時

指定されている場合のみWHEREの条件を追加したい…など、動的にSQLを組み立てたい場合があります。その場合はPrisma.sqlを用いて以下のように書くことができます。

const userID = getUserID()
await prisma.$queryRaw`
SELECT
  *
FROM
  article
WHERE
  ${userID !== null ? Prisma.sql`user_id = ${userID}` : Prisma.sql``}`

もしここで${userID !== null ? 'user_id = ${userID}' : ''}のようにPrisma.sqlを用いずにstringを渡してしまうとstring全体がパラメータとして処理されてしまうためエラーになってしまいます。
また、上手く行かないからといって間違っても $queryRawUnsafeは使用しないでください。 SQLインジェクションの温床になってしまいます。

BULK INSERTしたい時

こちらもよくあるパターンです。ON CONFLICT ... DO UPDATEによるBULK UPSERTなどPrismaでは実現できないINSERT文を発行したい場合に必要になります。
これは上述のPrisma.sqlPrisma.join()の組み合わせによって実現させます。

const params = [
  { id: 1, name: 'tockn' },
  { id: 2, name: 'taro' },
  { id: 3, name: 'jiro' }
]

const values = params.map((p) => Prisma.sql`(${id}, ${name})`)

await prisma.$executeRaw`
INSERT INTO
  user (id, name)
VALUES
  ${Prisma.join(values)}`

合せ技という感じですね。

Prismaのトランザクションについて

概要

Prismaではもちろんトランザクションを使用することができます。実装のパターンとしてSequential operationsとInteractive transactionsの2つのパターンが用意されています。[13]

Sequential operationsは複数のクエリを配列で渡し、その配列内のクエリを1つのトランザクションで実行する形です。

const [userList, updateUser] = await prisma.$transaction([
  prisma.$queryRaw`SELECT 'title' FROM User`,
  prisma.$executeRaw`UPDATE User SET name = 'Hello' WHERE id = 2;`,
])

Interactive transactionsは関数を渡し、その関数内のクエリを1つのトランザクションで実行する形です。

await prisma.$transaction(async (tx) => {
  await prisma.$queryRaw`SELECT 'title' FROM User`
  await prisma.$executeRaw`UPDATE User SET name = 'Hello' WHERE id = 2;`
})

ちなみに弊社Cloudbaseでは現在Sequential operationsが活躍する機会は無く、Interactive transactionsのみ使用しています。

タイムアウトについて

Prismaのトランザクションで気をつけなければいけないのがタイムアウトです。これはトランザクションを実行している際に設定された時間以上経過するとDBとのトランザクションがcloseしてロールバックされ、関数の実行終了後にタイムアウトエラーが投げられるというものです。
エラーメッセージ例

Transaction API error: Transaction already closed: A commit cannot be executed on an expired transaction. The timeout for this transaction was 5000 ms, however 12494 ms passed since the start of the transaction. Consider increasing the interactive transaction timeout or doing less work in the transaction.

これはクエリの実行時間のみを計測しているわけではなく、その処理全体を計測しています。つまり以下の実装のトランザクションの実行時間は10秒となります。

await prisma.$transaction(async (tx) => {
  await new Promise((resolve) => setTimeout(resolve, 10000))
})

このタイムアウトはデフォルトで5秒に設定されているため、上記の実装は必ずタイムアウトエラーが発生します。変更したい場合は$transactionの第二引数に渡します。単位がミリ秒なので注意が必要です。

await prisma.$transaction(async (tx) => {
  await new Promise((resolve) => setTimeout(resolve, 10000))
}, {
  timeout: 1000 * 60 * 5 // 5min
})

厄介なのがこのタイムアウトのデフォルト値を変更する方法が現状どこにも提供されていないということです。必ず5秒以下に抑えたい・5秒以上に設定したいといった場合には$transactionを呼び出す度に都度オプションを渡す必要があります。

宣伝のようで恐縮ですが、実はこの問題を解決するためにPrismaClient生成時に$transactionのオプションのデフォルト値を設定できるようにするというPull Requestを筆者が送っています。この記事を執筆している現在は5.10.0のマイルストーンラベルが貼られているため、5.10.0で入る可能性が高いです。

https://github.com/prisma/prisma/pull/19592

また、タイムアウトに設定した時間が経過すると関数の実行が強制的に終了されるわけではなく、Prisma APIを実行しない限りは後続の処理が実行され続けるという点にも注意が必要です。
例えば以下の実装ではhogeが出力されます。

await prisma.$transaction(async (tx) => {
  await new Promise((resolve) => setTimeout(resolve, 10000)) // この時点でタイムアウトしている
  console.log("hoge") // タイムアウトしても後続の処理が実行されるのでhogeがプリントされる
})

Prisma APIを実行すればそこでエラーが発生します。以下の例ではhogeは出力されますがfugaは出力されません。

await prisma.$transaction(async (tx) => {
  await new Promise((resolve) => setTimeout(resolve, 10000)) // この時点でタイムアウトしている
  console.log("hoge") // タイムアウトしても後続の処理が実行されるのでhogeがプリントされる
  await tx.user.count() // Prisma APIを実行するのでここでタイムアウトエラーが発生する
  console.log("fuga") // 上記でエラーになるのでfugaはプリントされない
})

例えばトランザクション内で外部APIを叩いている場合などは注意が必要です(そもそもDBトランザクション内で外部APIを叩くのはオススメしませんが)。

まとめ

この記事ではPrismaの概要や歴史、アーキテクチャの説明をして基本的な思想や仕組みを紹介しました。また、注目すべきパラメータや注意すべきAPI・トランザクションを紹介することで日々の運用を通して得た知見の共有を行いました。

さいごに

Cloudbaseではエンジニアを募集中です!

クラウドやセキュリティのようなエンジニアが共感しやすい事業ドメインであり、技術的な面白さがあり、技術力が直接プロダクトの成長へと繋がり、事業面・ポテンシャルも素晴らしく、スタートアップならではの夢も見れる日本の会社という、とても稀有な存在だと思っています。

少しでも気になった方はぜひご連絡ください!

https://levetty.notion.site/Cloudbase-Engineer-Entrance-Book-1f51dcaf9edd490e934780f526f833a3

脚注
  1. https://www.prisma.io/docs/orm/more/comparisons ↩︎

  2. https://www.prisma.io/docs/orm/overview/databases ↩︎

  3. https://www.prisma.io/blog/prisma-raises-4-5m-to-build-the-graphql-data-layer-for-all-databases-663484df0f60 ↩︎

  4. https://www.prisma.io/blog/announcing-prisma-2-zq1s745db8i5 ↩︎

  5. https://www.prisma.io/blog/prisma-2-beta-b7bcl0gd8d8e ↩︎

  6. https://www.graph.cool/ ↩︎

  7. https://github.com/prisma/prisma-engines ↩︎

  8. https://www.prisma.io/docs/orm/more/under-the-hood/engines#the-query-engine-at-runtime ↩︎ ↩︎

  9. https://www.prisma.io/docs/orm/more/under-the-hood/engines#defining-the-query-engine-type-for-prisma-client ↩︎

  10. https://www.prisma.io/docs/orm/reference/environment-variables-reference#downloading-engines ↩︎

  11. https://www.prisma.io/docs/orm/reference/connection-urls#env ↩︎

  12. 筆者の主観です ↩︎

  13. https://www.prisma.io/docs/orm/prisma-client/queries/transactions ↩︎

Discussion