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

Prisma + GraphQLを最高にする Schema Builderをつくってみた

Prisma + GraphQLを最高にする Schema Builderをつくってみた

Avatar for dekimasoon

dekimasoon

August 31, 2022
Tweet

Other Decks in Programming

Transcript

  1. なぜ作ったか その時点でのNode.jsのCode FirstなSchema Builder - GraphQL.js: リファレンス。人間が直接使うものではない。 - TypeGraphQL: お世話になりました。でもClass使いたくないんじゃ(宗教)!

    - typegraphql-prisma: 自動生成すごい!でももっと細かくカスタマイズしたい… - GraphQL Nexus: パイオニア。Prisma公式推し。 - nexus-prisma: 開発がぜんぜん進まないやんけ! - Pothos: 新星!型も考慮したPluginシステムが素晴らしい。 - prisma-plugin: 素晴らしい!これでいいかもしれない - でもPrismaにフォーカスしたらもっと研ぎ澄ませられるかも…
  2. 作ったもの const usersQuery = pg.query({ name: 'users', field: (b) =>

    b .object(() => objects.User) .list() .prismaArgs(() => args.findManyUser.build()) .resolve(({ prismaArgs }) => prisma.user.findMany(prismaArgs) ) }) query { users ( where: { name: { contains: "diego" } posts: { some: { isPublished: true } } } orderBy: [{ name: asc }, { id: asc }] take: 10 ) { id name posts { id title } } } これだけで こんな感じで呼び出し可能に
  3. 作ったもの const usersQuery = pg.query({ name: 'users', field: (b) =>

    b .object(() => objects.User) .list() .prismaArgs(() => args.findManyUser.edit((f) => ({ where: f.where.edit((f) => ({ name: f.name, email: f.email, })), orderBy: f.orderBy.select('UserOrderByWithRelationInputList').edit((f) => ({ name: f.name, id: f.id, })), take: f.take.default(10).validation((z) => z.max(100)), })).build()) .resolve(({ prismaArgs }) => prisma.user.findMany(prismaArgs) ) }) 必要な部分のみに限定したり、 デフォルト値やバリデーションの設定が可能 型補完も効くから楽ちん
  4. 作ったもの const usersQuery = pg.query({ name: 'users', field: (b) =>

    b .object(() => objects.User) .relay() .resolve(({ prismaArgs }) => prisma.user.findMany(prismaArgs) ) }) Relay対応も一発 query { users(first: 10) { edges { node { id name posts { id title } } cursor } pageInfo { hasNextPage hasPreviousPage } } } ばっちり!
  5. 作ったもの const user = pgpc.redefine({ name: 'User', fields: (f, b)

    => ({ ...omit(f, 'name'), postCount: b.int(), }), relations: () => getRelations('User'), }) 各テーブルに対応する オブジェクトタイプの調整も可能 const userWithoutRelations = user.copy({ name: 'UserWithoutRelations', fields: (f) => omit(f, 'post'), }) コピーもできる
  6. 作ったもの const user = pgpc.redefine({ name: 'User', fields: (f, b)

    => ({ ...omit(f, 'name'), postCount: b.int(), }), relations: () => getRelations('User'), }) user.implement((f) => ({ postCount: f.postCount.resolve((params) => { return pg.dataloader(params, async (userList) => { const userIds = userList.map((x) => x.id) const resp = await prisma.post.groupBy({ _count: { _all: true }, by: ['userId'], where: { userId: { in: userIds } }, }) return userIds.map((id) => resp.find((x) => x.userId === id)?._count._all ?? 0) }) }) })) Field Resolverの実装例 型安全なDataLoaderも組み込み
  7. 作ったもの const crateUserMutation = pg.mutation({ name: 'createUser', field: (b) =>

    b .object(() => user) .args((b) => ({ input: b.input(() => createUserInput) })) .auth(({ context }) => context.isAdmin) .resolve(({ args }) => prisma.user.create({ data: args.input }) ) }) フィールドごとの権限制御も可能