Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Prisma ORMを2年運用して培ったノウハウを共有する

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Prisma ORMを2年運用して培ったノウハウを共有する

Avatar for tockn

tockn

May 10, 2024
Tweet

More Decks by tockn

Other Decks in Technology

Transcript

  1. 情報も増えつつある © 2024 Cloudbase Inc. A 入門記3 A 採用事例記3 A

    エラー対処記3 A 解説記事 (いつもお世話になっています!)
  2. Cloudbaseの技術スタック © 2024 Cloudbase Inc. RDB API Server GraphDB Data

    Loader Storage スキャナー お客様のクラウド環境 Web Frontend お客様
  3. Cloudbaseの技術スタック © 2024 Cloudbase Inc. RDB API Server GraphDB Data

    Loader Storage スキャナー お客様のクラウド環境 Web Frontend お客様
  4. 操作例: SELECT const = . . ({ { } })

    users await prisma user where: name: findMany "tockn" /* [ { id: 2, email: '[email protected]', name: 'tockn' }, { id: 3, email: '[email protected]', name: 'tockn' } ] */ © 2024 Cloudbase Inc.
  5. 操作例: SELECT const = . . ({ { } })

    users await prisma user where: name: findMany "tockn" /* [ { id: 2, email: '[email protected]', name: 'tockn' }, { id: 3, email: '[email protected]', name: 'tockn' } ] */ © 2024 Cloudbase Inc. 純粋なObjectを返す
  6. 操作例: INSERT , UPDATE, DELETE... // INSERT // UPDATE //

    DELETE // UPSERT await await await await await await . . ({ { , } }) . . ({ { }, { } }) . . ({ { }, { } }) . . ({ { } }) . . ({ { } }) . . ({ { }, { , }, { } }) prisma user data: name: email: prisma user where: email: data: name: prisma user where: name: data: name: prisma user where: email: prisma user where: name: prisma user where: email: create: name: email: update: name: create update updateMany delete deleteMany upsert "tockn" "[email protected]" "[email protected]" "tockn" "sato" "tockn" "[email protected]" "tockn" "[email protected]" "tockn" "[email protected]" "sato" © 2024 Cloudbase Inc.
  7. find系のincludeについて await . . ({ { , }, }); prisma

    user include: post: findFirst true © 2024 Cloudbase Inc.
  8. find系のincludeについて await . . ({ { , }, }); prisma

    user include: post: findFirst true © 2024 Cloudbase Inc. リレーションを持つテーブルを指定
  9. includeして得られる結果 { , , , [ { , , ,

    , , }, ], }; id: email: name: posts: id: title: content: published: authorId: 1 1 11 '[email protected]' 'tockn' 'Cloudbase' 'Cloudbase is a cloud security platform.' false © 2024 Cloudbase Inc.
  10. includeして得られる結果 { , , , [ { , , ,

    , , }, ], }; id: email: name: posts: id: title: content: published: authorId: 1 1 11 '[email protected]' 'tockn' 'Cloudbase' 'Cloudbase is a cloud security platform.' false © 2024 Cloudbase Inc. リレーション先のデータも取得
  11. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. -- 1. Userの取得 SELECT FROM

    WHERE LIMIT . . , . . , . . . . . = ? ? OFFSET ?; `main` `User` `id` `main` `User` `email` `main` `User` `name` `main` `User` `main` `User` `name` -- 2. 取得したUserが持つPost取得 SELECT FROM WHERE IN LIMIT . . , . . , . . , . . , . . . . . (?, ?, ?) ? OFFSET ?; `main` `Post` `id` `main` `Post` `title` `main` `Post` `content` `main` `Post` `published` `main` `Post` `authorId` `main` `Post` `main` `Post` `authorId`
  12. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. -- 1. Userの取得 SELECT FROM

    WHERE LIMIT . . , . . , . . . . . = ? ? OFFSET ?; `main` `User` `id` `main` `User` `email` `main` `User` `name` `main` `User` `main` `User` `name` -- 2. 取得したUserが持つPost取得 SELECT FROM WHERE IN LIMIT . . , . . , . . , . . , . . . . . (?, ?, ?) ? OFFSET ?; `main` `Post` `id` `main` `Post` `title` `main` `Post` `content` `main` `Post` `published` `main` `Post` `authorId` `main` `Post` `main` `Post` `authorId` 最初にuserを取得
  13. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. -- 1. Userの取得 SELECT FROM

    WHERE LIMIT . . , . . , . . . . . = ? ? OFFSET ?; `main` `User` `id` `main` `User` `email` `main` `User` `name` `main` `User` `main` `User` `name` -- 2. 取得したUserが持つPost取得 SELECT FROM WHERE IN LIMIT . . , . . , . . , . . , . . . . . (?, ?, ?) ? OFFSET ?; `main` `Post` `id` `main` `Post` `title` `main` `Post` `content` `main` `Post` `published` `main` `Post` `authorId` `main` `Post` `main` `Post` `authorId` 取得したuser_idで Postを取得
  14. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. -- 1. Userの取得 SELECT FROM

    WHERE LIMIT . . , . . , . . . . . = ? ? OFFSET ?; `main` `User` `id` `main` `User` `email` `main` `User` `name` `main` `User` `main` `User` `name` -- 2. 取得したUserが持つPost取得 SELECT FROM WHERE IN LIMIT . . , . . , . . , . . , . . . . . (?, ?, ?) ? OFFSET ?; `main` `Post` `id` `main` `Post` `title` `main` `Post` `content` `main` `Post` `published` `main` `Post` `authorId` `main` `Post` `main` `Post` `authorId` 取得したuser_idで Postを取得 WHERE IN
  15. © 2024 Cloudbase Inc. Cloudbaseではど う し ているか g 対象データ量が多い場合はqueryRawを使C

    g 少ない場合はincludeを使う await . <{ : ; : }> ; prisma id name $queryRaw number string ` SELECT u.id, u.name FROM user as u INNER JOIN post as p ON u.id = p."authorId" WHERE u.name = 'tockn'`
  16. © 2024 Cloudbase Inc. Cloudbaseではど う し ているか f 原則updateMany,

    deleteManyを使用すa f xxxManyなら余分なクエリが走らない UPDATE DELETE // ️ 原則使わない // 余分なクエリが走らないmanyを使う await await . . ({ { }, { }, }); . . ({ { }, { }, }); prisma user where: email: data: name: prisma user where: name: data: name: update updateMany '[email protected]' 'tockn' 'sato' 'tockn' // 原則使わない // ️ 余分なクエリが走らないmanyを使う await await . . ({ { }, }); . . ({ { }, }); prisma user where: email: prisma user where: name: delete deleteMany '[email protected]' 'tockn'
  17. previewFeature: relationJoins await . . ({ { }, { },

    }); prisma user include: posts: where: name: findMany true 'tockn' © 2024 Cloudbase Inc.
  18. previewFeature: relationJoins await . . ({ , { }, {

    }, }); prisma user relationLoadStrategy: include: posts: where: name: findMany 'join' 'tockn' true © 2024 Cloudbase Inc. joinかqueryか選べるように!
  19. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. await . . ({ ,

    { }, { }, }); prisma user relationLoadStrategy: include: posts: where: name: findMany 'join' 'tockn' true SELECT AS FROM AS LEFT JOIN SELECT AS FROM SELECT FROM SELECT AS FROM SELECT FROM AS WHERE AS AS AS AS ON WHERE . , . , . . LATERAL ( (JSONB_AGG( ), ) ( . ( JSONB_BUILD_OBJECT ( , . , , . , , . ) ( .* . . = . ) ) ) ) TRUE . = $ ; "t1" "id" "t1" "name" "User_posts" "__prisma_data__" "posts" "public" "User" "t1" "__prisma_data__" '[]' "__prisma_data__" "t4" "__prisma_data__" 'id' "t3" "id" 'content' "t3" "content" 'authorId' "t3" "authorId" "__prisma_data__" "t2" "public" "Post" "t2" "t1" "id" "t2" "authorId" "t3" "t4" "t5" "User_posts" "t1" "name" COALESCE /* root select */ /* inner select */ /* middle select */ /* outer select */ 1 発行されるSQL
  20. find系のincludeで発行されるSQL © 2024 Cloudbase Inc. await . . ({ ,

    { }, { }, }); prisma user relationLoadStrategy: include: posts: where: name: findMany 'join' 'tockn' true SELECT AS FROM AS LEFT JOIN SELECT AS FROM SELECT FROM SELECT AS FROM SELECT FROM AS WHERE AS AS AS AS ON WHERE . , . , . . LATERAL ( (JSONB_AGG( ), ) ( . ( JSONB_BUILD_OBJECT ( , . , , . , , . ) ( .* . . = . ) ) ) ) TRUE . = $ ; "t1" "id" "t1" "name" "User_posts" "__prisma_data__" "posts" "public" "User" "t1" "__prisma_data__" '[]' "__prisma_data__" "t4" "__prisma_data__" 'id' "t3" "id" 'content' "t3" "content" 'authorId' "t3" "authorId" "__prisma_data__" "t2" "public" "Post" "t2" "t1" "id" "t2" "authorId" "t3" "t4" "t5" "User_posts" "t1" "name" COALESCE /* root select */ /* inner select */ /* middle select */ /* outer select */ 1 発行されるSQL JOINが使われている (少し複雑なSQLだが...)
  21. PrismaClientIssuerについて const new async => async => = ( )

    . (..., ( ) { . . ({ ... }) }) . (..., ( ) { . . ({ ... }) }) issuer PrismaClientIssuser primary create readReplica findMany args issuer tx tx user issuer tx tx user // primaryへのアクセス // read replicaへのアクセス await await await return © 2024 Cloudbase Inc.
  22. PrismaClientIssuerについて const new async => async => = ( )

    . (..., ( ) { . . ({ ... }) }) . (..., ( ) { . . ({ ... }) }) issuer PrismaClientIssuser primary create readReplica findMany args issuer tx tx user issuer tx tx user // primaryへのアクセス // read replicaへのアクセス await await await return © 2024 Cloudbase Inc. primaryを指定
  23. PrismaClientIssuerについて const new async => async => = ( )

    . (..., ( ) { . . ({ ... }) }) . (..., ( ) { . . ({ ... }) }) issuer PrismaClientIssuser primary create readReplica findMany args issuer tx tx user issuer tx tx user // primaryへのアクセス // read replicaへのアクセス await await await return © 2024 Cloudbase Inc. callbackの引数に primaryへ張ったTranasctionClientが来る
  24. PrismaClientIssuerについて const new async => async => = ( )

    . (..., ( ) { . . ({ ... }) }) . (..., ( ) { . . ({ ... }) }) issuer PrismaClientIssuser primary create readReplica findMany args issuer tx tx user issuer tx tx user // primaryへのアクセス // read replicaへのアクセス await await await return © 2024 Cloudbase Inc. primaryへクエリを発行
  25. PrismaClientIssuerについて const new async => async => = ( )

    . (..., ( ) { . . ({ ... }) }) . (..., ( ) { . . ({ ... }) }) issuer PrismaClientIssuser primary create readReplica findMany args issuer tx tx user issuer tx tx user // primaryへのアクセス // read replicaへのアクセス await await await return © 2024 Cloudbase Inc. read replicaも同じ
  26. PrismaClientIssuerについて const async => = ( : . , :

    ) { ... }; handleUser tx user Prisma TransactionClient User © 2024 Cloudbase Inc.
  27. PrismaClientIssuerについて const async => = ( : . , :

    ) { ... }; handleUser tx user Prisma TransactionClient User © 2024 Cloudbase Inc. primaryを渡すべき? readReplicaを渡すべき?
  28. PrismaClientIssuerについて const async => = ( : . , :

    ) { . . ({ }); }; handleUser create tx user tx user data: user Prisma TransactionClient User await © 2024 Cloudbase Inc. 実装を見ないとわからない (この例はcreateしてるのでprimaryが必要)
  29. © 2024 Cloudbase Inc. 2つのTransaction型 ・PrismaのTransactionClientの交差型 ・   ・issuerのprimaryメソッドで渡る型  

    ・ に対しても ・   ・issuerのreadReplicaメソッドで渡る型   ・ に対して CBWritableTransaction CBWritableTransaction CBReadableTransaction CBReadableTransaction 渡せる 渡せない
  30. 独自Transaction型を使う const async => const async => = ( :

    , : ) { . . ({ }); }; = ( : ) { . . (); }; registerUser create listUsers findMany tx user tx user data: user tx tx user CBWritableTransaction User CBReadableTransaction await return © 2024 Cloudbase Inc.
  31. 独自Transaction型を使う const async => const async => = ( :

    , : ) { . . ({ }); }; = ( : ) { . . (); }; registerUser create listUsers findMany tx user tx user data: user tx tx user CBWritableTransaction User CBReadableTransaction await return © 2024 Cloudbase Inc. Write系クエリがある場合は CBWritableTranasction
  32. 独自Transaction型を使う const async => const async => = ( :

    , : ) { . . ({ }); }; = ( : ) { . . (); }; registerUser create listUsers findMany tx user tx user data: user tx tx user CBWritableTransaction User CBReadableTransaction await return © 2024 Cloudbase Inc. Read系のみの場合は CBReadableTransaction
  33. PrismaClientIssuerと独自Transaction型(OKパターン) // primary // CBWritableTransaction // CBReadableTransaction // read replica

    // CBReadableTransaction await await return await return . (..., ( ) { ( ) ( ) }) . (..., ( ) { ( ) }) issuer tx tx tx issuer tx tx primary registerUser listUsers readReplica listUsers async => async => © 2024 Cloudbase Inc.
  34. PrismaClientIssuerと独自Transaction型(OKパターン) // primary // CBWritableTransaction // CBReadableTransaction // read replica

    // CBReadableTransaction await await return await return . (..., ( ) { ( ) ( ) }) . (..., ( ) { ( ) }) issuer tx tx tx issuer tx tx primary registerUser listUsers readReplica listUsers async => async => © 2024 Cloudbase Inc. Writable, Readableどちらも呼べる primaryでは CBWritableTransactionが貰える
  35. PrismaClientIssuerと独自Transaction型(OKパターン) // primary // CBWritableTransaction // CBReadableTransaction // read replica

    // CBReadableTransaction await await return await return . (..., ( ) { ( ) ( ) }) . (..., ( ) { ( ) }) issuer tx tx tx issuer tx tx primary registerUser listUsers readReplica listUsers async => async => © 2024 Cloudbase Inc. readReplicaでは CBReadableTransactionが貰える Readableのみ呼べる
  36. PrismaClientIssuerと独自Transaction型(NGパターン) // NG // 型エラー // NG // 型エラー await

    return return . (..., ( ) { ( ) }) = ( : ) { ( ) . . () } issuer tx tx tx tx tx user readReplica registerUser listUsers registerUser findMany async => const async => CBReadableTransaction © 2024 Cloudbase Inc.
  37. PrismaClientIssuerと独自Transaction型(NGパターン) // NG // 型エラー // NG // 型エラー await

    return return . (..., ( ) { ( ) }) = ( : ) { ( ) . . () } issuer tx tx tx tx tx user readReplica registerUser listUsers registerUser findMany async => const async => CBReadableTransaction © 2024 Cloudbase Inc. CBWritableTransactionが必要なメソッドを readReplicaで呼ぶと型エラー
  38. PrismaClientIssuerと独自Transaction型(NGパターン) // NG // 型エラー // NG // 型エラー await

    return return . (..., ( ) { ( ) }) = ( : ) { ( ) . . () } issuer tx tx tx tx tx user readReplica registerUser listUsers registerUser findMany async => const async => CBReadableTransaction © 2024 Cloudbase Inc. メソッド内で渡してしまうことも防止
  39. © 2024 Cloudbase Inc. パフ ォーマンス、 スケーラビリテ ィ ノ ウハウまとめ

    ・発行されるクエリに注意  ・結局queryRawが必要になることも  ・とはいえ、Prismaはクエリ最適化を頑張ってる ・Read Replicaによるスケールアウト  ・PrismaClientをwrapするクラスを作ると便利  ・独自のTransaction型を定義すると便利
  40. whereとundefined const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc.
  41. whereとundefined const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc. これがundefinedを返した場合
  42. whereとundefined const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc. tenant関係なく 全件取得
  43. RLSざっくり解説 © 2024 Cloudbase Inc. どうぞ (SELECT * FROM messagesの結果)

    id 1 こんにちは〜 どうも〜 さようなら〜 1 1 1 6 29 body tenant_id
  44. RLSざっくり解説 © 2024 Cloudbase Inc. どうぞ (SELECT * FROM messagesの結果)

    id 1 こんにちは〜 どうも〜 さようなら〜 1 1 1 6 29 body tenant_id Whereが無くても tenantIdで絞られる
  45. PrismaClientIssuerとRLS await . ( , ( ) { ... })

    issuer accessToken tx primary async => © 2024 Cloudbase Inc.
  46. PrismaClientIssuerとRLS await . ( , ( ) { ... })

    issuer accessToken tx primary async => © 2024 Cloudbase Inc. 認可情報を渡す
  47. PrismaClientIssuerとRLS await . ( , ( ) { ... })

    issuer accessToken tx primary async => © 2024 Cloudbase Inc. RLSが適用された Transaction
  48. © 2024 Cloudbase Inc. このaccessTokenって何者? await . ( , (

    ) { ... }) issuer accessToken tx primary async =>
  49. handlerの実装 async => async => ( , ) { .

    ( . , ( ) { ... }) } req: res: issuer req accessToken tx express.Request express.Response await readReplica © 2024 Cloudbase Inc. reqから取得して 渡すだけ
  50. PrismaClientIssuerの実装 async accessToken callback prisma tx tx accessToken tenantId tx

    ( , ) { ... . ( ( ) { . . ; ( ); }) } readReplica $transaction $executeRaw callback return await return async => ${ } ` SELECT set_config( 'app.rls_config.tenant', , TRUE )` © 2024 Cloudbase Inc.
  51. PrismaClientIssuerの実装 async accessToken callback prisma tx tx accessToken tenantId tx

    ( , ) { ... . ( ( ) { . . ; ( ); }) } readReplica $transaction $executeRaw callback return await return async => ${ } ` SELECT set_config( 'app.rls_config.tenant', , TRUE )` © 2024 Cloudbase Inc. set_configを呼んで callbackに渡してるだけ
  52. © 2024 Cloudbase Inc. 例えば 以下のテーブルがあるとする model User id Int

    name String address String password String { } 個人情報
  53. © 2024 Cloudbase Inc. Responseの型を以下とする type = { : ;

    : ; }[]; ListUsersResponse number string id name
  54. こうなる const async => const = ( , ) {

    : = . . (); . ( ); }; listUsers findMany json req res prisma user res resp resp ListUsersResponse await
  55. こうなる const async => const = ( , ) {

    : = . . (); . ( ); }; listUsers findMany json req res prisma user res resp resp ListUsersResponse await 型的には問題ないが…?
  56. こうなる [ { : , : , : , :

    } ] "id" "name" "address" "password" 1 "tockn" "〒108-0073 東京都港区三田3-2-8" "sugoi-secure-password"
  57. こうなる [ { : , : , : , :

    } ] "id" "name" "address" "password" 1 "tockn" "〒108-0073 東京都港区三田3-2-8" "sugoi-secure-password" 個人情報が丸見えに
  58. © 2024 Cloudbase Inc. zod ・スキーマ定義とバリデーションを行うライブラリ ・z.object() 等を使用してスキーマを定義 ・Schema.parse(obj) で渡したobjのバリデーション

    const type typeof = . ({ . (), . (), }); = . < >; . ( ); User z id: z name: z User User obj object number string parse User z infer // バリデーション
  59. Response型をzodで書き換える const type typeof = . ( . ({ .

    (), . (), }), ); = . < >; ListUsersResponse z z id: z name: z ListUsersResponse array object number string ListUsersResponse z infer
  60. zodのparseを行う const async => const = ( , ) {

    = . ( . . () ); . ( ); }; listUsers parse findMany json req res ListUsersResponse prisma user res resp resp await
  61. zodのparseを行う const async => const = ( , ) {

    = . ( . . () ); . ( ); }; listUsers parse findMany json req res ListUsersResponse prisma user res resp resp await parseメソッドを実行
  62. こうなる [ { : , : } ] "id" "name"

    1 "tockn" 個人情報が切り落とされた! parseは未定義フィールドを切り落とす
  63. © 2024 Cloudbase Inc. セキ ュ リテ ィ ノ ウハウまとめ

    ・マルチテナントにおける行レベルのセキュリティ  ・PrismaでRLSを使う  ・またしてもPrismaClientをwrapするクラスが便利 ・列レベルのセキュリティも忘れてはいけない  ・PrismaはPOJO返し、TypeScriptは構造的型付け  ・ナイーブな実装は情報漏洩になることも  ・zodを使うことで回避
  64. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => =>
  65. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => シードレコードを 宣言的に定義
  66. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => シードレコードを 宣言的に定義 DELETEしてから INSERT
  67. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => シードレコードを 宣言的に定義 外部キー制約も考慮して INSERT DELETEしてから INSERT
  68. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => テスト対象の メソッドを書く
  69. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => テスト対象メソッドの 戻り値を定義
  70. 内製Test Runner全体像 it run method _TEST_ONLY_primaryDBWithRLS registerUserInGroup ( , (

    , { { [{ , }], [{ , , }], }, () ( ( ) ( , )), { }, { [{ , , }] ... '指定したgroupId配下にuserが作成される' 'テナント1' 'グループ1' 'tockn' db recordSet: tenant: id: name: group: id: name: tenantId: : tx tx returns: id: mutates: user: id: name: groupId: 1 1 1 1 1 1 1 => => テスト対象メソッド実行後の レコードの状態を定義
  71. © 2024 Cloudbase Inc. 今から使えます! schema.prisma shell npm @cloudbase-inc/ install

    prisma-generator-integration-test-runner -D generator integration test runner provider - - { = } "prisma-generator-integration-test-runner"
  72. © 2024 Cloudbase Inc. テスタビリテ ィ まとめ ・開発生産性に課題  ・テストレコードの用意、検証など ・内製Test

    Runnerが便利  ・宣言的な状態定義 ・OSSとして公開しました  ・ぜひ使ってみてください
  73. トレースをJaegerで可視化 const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc.
  74. トレースをJaegerで可視化 const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc. どのAPIで
  75. トレースをJaegerで可視化 const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc. どのような処理が どのくらい実行されたか
  76. トレースをJaegerで可視化 const = (); . . ({ { } });

    tenantId await await getMyTenantId findMany prisma message where: tenantId © 2024 Cloudbase Inc. どのようなSQLが発行されたか