import { JSONObjectResolver } from 'graphql-scalars' import PrismaPlugin, { PothosPrismaDatamodel, PrismaClient, } from '@pothos/plugin-prisma' import { Request, Response } from 'express' import SmartSubscriptionPlugin, { subscribeOptionsFromIterator, } from '@pothos/plugin-smart-subscriptions' import ZodPlugin from '@pothos/plugin-zod' import AuthzPlugin from '@pothos/plugin-authz' import ErrorsPlugin from '@pothos/plugin-errors' import type { FileUpload } from 'graphql-upload/processRequest.js' import GraphQLUpload from 'graphql-upload/GraphQLUpload.js' import { Injectable, Logger } from '@nestjs/common' import { PrismaCrudGenerator } from './graphql.generator' import type PrismaTypes from '../types/pothos.generated' import PrismaUtils from '@pothos/plugin-prisma-utils' import { PubSub } from 'graphql-subscriptions' import RelayPlugin from '@pothos/plugin-relay' import SchemaBuilder from '@pothos/core' import SimpleObjectPlugin from '@pothos/plugin-simple-objects' import { User } from '@prisma/client' import { getDatamodel } from '../types/pothos.generated' import { DateTime } from 'luxon' import { Kind } from 'graphql' import { DateTimeUtils } from '../common/utils/datetime.utils' import { JsonValue } from '@prisma/client/runtime/library' export type SchemaContext = | { isSubscription: true websocket: { req: Request pubSub: PubSub me: User generator: PrismaCrudGenerator } } | { isSubscription: false http: { req: Request res: Response me: User | null pubSub: PubSub invalidateCache: () => Promise generator: PrismaCrudGenerator } } // extend prisma types to contain string type export interface SchemaBuilderOption { Context: SchemaContext PrismaTypes: PrismaTypes DataModel: PothosPrismaDatamodel Connection: { totalCount: number | (() => number | Promise) } // AuthZRule: keyof typeof rules; Scalars: { DateTime: { Input: string | DateTime | Date Output: string | DateTime | Date } Json: { Input: JsonValue Output: JsonValue } Upload: { Input: FileUpload Output: FileUpload } Int: { Input: number Output: number | bigint | string } } } @Injectable() export class Builder extends SchemaBuilder { public generator: PrismaCrudGenerator constructor(private readonly prisma: PrismaClient) { super({ plugins: [ PrismaPlugin, PrismaUtils, SimpleObjectPlugin, SmartSubscriptionPlugin, RelayPlugin, ErrorsPlugin, AuthzPlugin, ZodPlugin, ], smartSubscriptions: { debounceDelay: 1000, ...subscribeOptionsFromIterator((name, ctx) => { if (ctx.isSubscription) { return ctx.websocket.pubSub.asyncIterator(name) } return ctx.http.pubSub.asyncIterator(name) }), }, zod: { // optionally customize how errors are formatted validationError: (zodError, _args, _context, _info) => { // the default behavior is to just throw the zod error directly Logger.error(zodError.message, 'Zod Error') return zodError }, }, relay: {}, prisma: { client: prisma, exposeDescriptions: true, filterConnectionTotalCount: true, onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn', dmmf: getDatamodel(), }, errors: { defaultTypes: [], }, }) this.generator = new PrismaCrudGenerator(this) this.scalarType('DateTime', { serialize: (value) => { // Serialize outgoing DateTime to ISO string if (typeof value === 'string') { return value } if (typeof value === 'object' && value !== null && 'toISO' in value) { return value } // if value = Date, convert to DateTime if (value instanceof Date) { return DateTimeUtils.toIsoString(DateTimeUtils.fromDate(value)) } throw new Error('Invalid DateTime') }, parseValue: (value) => { // Parse incoming ISO string to Luxon DateTime if (typeof value === 'string') { return DateTimeUtils.fromIsoString(value) } throw new Error('Invalid DateTime') }, parseLiteral: (ast) => { // parse string to DateTime if (ast.kind === Kind.STRING) { return DateTimeUtils.fromIsoString(ast.value) } throw new Error('Invalid DateTime') }, }) this.addScalarType('Json', JSONObjectResolver) this.addScalarType('Upload', GraphQLUpload) this.queryType({}) this.mutationType({}) this.subscriptionType({}) this.globalConnectionField('totalCount', (t) => t.int({ nullable: true, resolve: (parent) => typeof parent.totalCount === 'function' ? parent.totalCount() : parent.totalCount, }), ) } } export type BuilderTypes = PothosSchemaTypes.ExtendDefaultTypes