👍

prisma-fabbricaを試す

2024/07/02に公開

まずは、Prisma ORMを軽くおさらい

Prisma ORM

https://www.prisma.io/orm

Prisma Generators

https://www.prisma.io/docs/orm/prisma-schema/overview/generators

必要に応じてジェネレータを追加できる

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

generator dbml {
  provider = "prisma-dbml-generator"
}

generator docs {
  provider = "node node_modules/prisma-docs-generator"
}

prisma-fabbrica

https://github.com/Quramy/prisma-fabbrica

  • Prisma Community generators のひとつ
  • ヘルパーを得られる

Getting started

npm i @quramy/prisma-fabbrica -D
npm list --depth=0
(...抜粋...)
├── next@14.2.4
├── react@18.3.1
├── typescript@5.4.5
├── @trpc/client@10.45.2
├── @trpc/react-query@10.45.2
├── @trpc/server@10.45.2
├── prisma@5.14.0
├── @prisma/client@5.14.0
├── @quramy/prisma-fabbrica@2.2.0
prisma/schema.prisma
+generator fabbrica {
+  provider = "prisma-fabbrica"
+}
npx prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Error: 
✔ Generated Prisma Client (v5.14.0) to .\node_modules\@prisma\client in 64ms
prisma-fabbrica: Failed to TypeScript transpilation. Please try "noTranspile = true"  
prisma/schema.prisma
generator fabbrica {
  provider = "prisma-fabbrica"
+  noTranspile = true
}
npx prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
✔ Generated Prisma Client (v5.14.0) to .\node_modules\@prisma\client in 64ms
✔ Generated DBML Schema to .\node_modules\.prisma\dbml in 12ms
✔ Generated Prisma Docs Generator to .\node_modules\.prisma\docs in 36ms
✔ Generated fabbrica to .\node_modules\.prisma\fabbrica in 89ms

生成されたものを確認してみる

src\__generated__\fabbrica\index.ts
node_modules\.prisma\fabbrica\index.ts
import type { Account } from "@prisma/client";
import type { Session } from "@prisma/client";
(......)
import type { Prisma, PrismaClient } from "@prisma/client";
import { createInitializer, createScreener, getScalarFieldValueGenerator, normalizeResolver, normalizeList, getSequenceCounter, createCallbackChain, destructure } from "@quramy/prisma-fabbrica/lib/internal";
import type { ModelWithFields, Resolver, } from "@quramy/prisma-fabbrica/lib/internal";
export { resetSequence, registerScalarFieldValueGenerator, resetScalarFieldValueGenerator } from "@quramy/prisma-fabbrica/lib/internal";

type BuildDataOptions<TTransients extends Record<string, unknown>> = {
    readonly seq: number;
} & TTransients;

type TraitName = string | symbol;

type CallbackDefineOptions<TCreated, TCreateInput, TTransients extends Record<string, unknown>> = {
    onAfterBuild?: (createInput: TCreateInput, transientFields: TTransients) => void | PromiseLike<void>;
    onBeforeCreate?: (createInput: TCreateInput, transientFields: TTransients) => void | PromiseLike<void>;
    onAfterCreate?: (created: TCreated, transientFields: TTransients) => void | PromiseLike<void>;
};

const initializer = createInitializer();

const { getClient } = initializer;

export const { initialize } = initializer;

const modelFieldDefinitions: ModelWithFields[] = [{
        name: "Account",
        fields: [{
                name: "user",
                type: "User",
                relationName: "AccountToUser"
            }]
    }, {
        name: "Session",
        fields: [{
                name: "user",
                type: "User",
                relationName: "SessionToUser"
            }]
    }, {
(......)

Usage of factories

seed してみる

prisma/seed.ts
import { PrismaClient, Prisma } from '@prisma/client'
import { initialize, defineProductFactory, defineUserFactory, defineRecipeFactory } from "../src/__generated__/fabbrica";

import { productData } from "./seed-sources";

const prisma = new PrismaClient();
initialize({ prisma });

async function seed() {
  // Product
  const ProductFactory = defineProductFactory();
  console.log(await prisma.product.count());
  // 自動入力
  await ProductFactory.create();
  // 指定入力
  await ProductFactory.create({ userId: "TestUserId", sort: 0, category: "TestCategory", name: "TestName", unit: "pc", });
  console.log(await prisma.product.count());

  // User
  const UserFactory = defineUserFactory({
    defaultData: async ({ seq }) => ({
      name: `user${seq.toString().padStart(3, "0")}`,
    }),
  })
  // Recipe >>> User
  const RecipeFactory = defineRecipeFactory({
    defaultData: {
      user: UserFactory,
    },
  });
  await RecipeFactory.create();
  await RecipeFactory.createList(3);

  // User >>> Recipe
  await UserFactory.create({
    recipes: {
      create: await RecipeFactory.buildList(2),
    },
  });
}

seed();
  • 上記のような、いくつかのパターンを試したところ、想定通りの動きをデータベースのレコードとともに確認できた。

テスティング・ライブラリとの連携

  • 後ほど、試してみたい

Discussion