pothos icon indicating copy to clipboard operation
pothos copied to clipboard

Separate files on building.

Open shtse8 opened this issue 2 years ago • 2 comments

Is there any recommendation on how to split the building codes into separate files with Pothos?

|- mutations | |- readArticle.ts | |- updateUser.ts |- querys | |- getUser.ts | |- me.ts

As the definition is long and complicated, I want to split it into multiple files for better maintenance. But I don't have any good idea on how to split it and preserve the typing at the same time.

Here is my current proposal. But I don't like it much. I put all building code and define function into a file so that they are working on the same builder type.

// builder.ts

import SchemaBuilder from '@pothos/core'
import PrismaPlugin from '@pothos/plugin-prisma'
import RelayPlugin from '@pothos/plugin-relay'
import { Prisma } from '@prisma/client'
import type { Locale } from '@prisma/client'
import type PrismaTypes from '~/prisma/pothos-index'
import type { Context } from './type'

export function createBuilder() {
  return new SchemaBuilder<{
    Context: Context
    PrismaTypes: PrismaTypes
    Scalars: {
      Date: {
        Input: Date
        Output: Date
      }
      JSON: {
        Input: object
        Output: object
      }
    }
    Objects: {
      // Me: Me
      Locale: Locale
    }
  }>({
    plugins: [RelayPlugin, PrismaPlugin],
    prisma: {
      client: context => context.prisma,
      // Because the prisma client is loaded dynamically, we need to explicitly provide the some information about the prisma schema
      dmmf: Prisma.dmmf,
    },
    relayOptions: {
      idFieldName: '_id',
      // These will become the defaults in the next major version
      clientMutationId: 'omit',
      cursorType: 'ID',
    },
  })
}

type Definition<T extends PothosSchemaTypes.SchemaBuilder<any> = ReturnType<typeof createBuilder>> = (builder: T) => void

export function defineMutationField(...args: Parameters<ReturnType<typeof createBuilder>['mutationField']>): Definition {
  return (builder: ReturnType<typeof createBuilder>) => builder.mutationField(...args)
}

export function defineQueryField(...args: Parameters<ReturnType<typeof createBuilder>['queryField']>): Definition {
  return (builder: ReturnType<typeof createBuilder>) => builder.queryField(...args)
}

export function addDefinitions(builder: ReturnType<typeof createBuilder>, ...definitions: Definition[]) {
  for (const definition of definitions)
    definition(builder)
}

for each definition file, for example,

mutations/viewArticle.ts

import { defineMutationField } from '~/server/graphql/builder'

export default defineMutationField('viewArticle', t => t.prismaField({
  type: 'Article',
  args: {
    id: t.arg.int({ required: true }),
  },
  resolve: (query, parent, args, context) => {
    return context.prisma.article.update({
      where: { id: args.id },
      data: {
        viewCount: { increment: 1 },
        viewedAt: new Date(),
      },
      ...query,
    })
  },
}))

finally, in the entry file

schema.ts

import viewArticle from './mutations/viewArticle'
import me from './querys/me'
import { addDefinitions, createBuilder } from './builder'

const builder = createBuilder()
addDefinitions(builder, [
  viewArticle,
  me,
])
export const schema = builder.toSchema({
  directives: [],
})

Seems working, but I don't know if there is any better solution.

shtse8 avatar Aug 26 '22 07:08 shtse8

Yes there is: https://pothos-graphql.dev/docs/guide/app-layout

Also this guide is helpful on how to mitigate circular references: https://pothos-graphql.dev/docs/guide/circular-references

glennreyes avatar Aug 26 '22 09:08 glennreyes

You can see an example of the pattern mentioned in the guide above here: https://github.com/hayes/pothos/blob/main/examples/complex-app/src/schema/game.ts

hayes avatar Aug 26 '22 13:08 hayes

going to close this, but let me know if you are still having issues

hayes avatar Sep 03 '22 19:09 hayes

Experimenting with this myself, I feel like this could be a clean approach.

export const db = new PrismaClient();

interface ISchema {
  PrismaTypes: PrismaTypes;
  Scalars: {
    DateTime: {
      Output: Date;
      Input: Date;
    };
    Json: {
      Output: Prisma.JsonValue;
      Input: Prisma.JsonValue;
    };
  };
}

export type IBuilder = PothosSchemaTypes.SchemaBuilder<
  PothosSchemaTypes.ExtendDefaultTypes<ISchema>
>;

const builder: IBuilder = new SchemaBuilder<ISchema>({
  plugins: [PrismaPlugin],
  prisma: {
    client: db
  }
});

// base
models(builder);
queries(db, builder);
mutations(db, builder);

// not really sure yet
builder.queryType({});
builder.mutationType({});

// not really sure yet
builder.addScalarType("Json", JSONObjectResolver, {});
builder.addScalarType("DateTime", DateTimeResolver, {});

export const schema = builder.toSchema({});

bluematter avatar Dec 15 '22 01:12 bluematter