Files
epess-web-backend/src/Graphql/graphql.builder.ts
Ly Tuan Kiet 9e6d62e4be chore: update package dependencies and improve pub/sub iterator usage
- Updated various package dependencies in package-lock.json and package.json for enhanced stability and performance.
- Changed pub/sub iterator methods from `asyncIterator` to `asyncIterableIterator` in multiple schema files for better compatibility with the latest GraphQL subscriptions.
- Added `moduleResolution` option in tsconfig.json to support Node.js-style module resolution.
2024-12-08 20:38:09 +07:00

182 lines
5.5 KiB
TypeScript

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'
// @ts-expect-error
import type { FileUpload } from 'graphql-upload/processRequest.mjs'
// @ts-expect-error
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'
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, ValueNode } from 'graphql'
import { DateTimeUtils } from '../common/utils/datetime.utils'
import { JsonValue } from '@prisma/client/runtime/library'
import Delta from 'quill-delta'
export type SchemaContext =
| {
isSubscription: true
websocket: {
req: Request
pubSub: PubSub
sessionId: string
me: User
generator: PrismaCrudGenerator<BuilderTypes>
}
}
| {
isSubscription: false
http: {
req: Request
res: Response
me: User | null
pubSub: PubSub
invalidateCache: () => Promise<void>
generator: PrismaCrudGenerator<BuilderTypes>
}
}
// extend prisma types to contain string type
export interface SchemaBuilderOption {
Context: SchemaContext
PrismaTypes: PrismaTypes
DataModel: PothosPrismaDatamodel
Connection: {
totalCount: number | (() => number | Promise<number>)
}
// 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
}
Delta: {
Input: Delta
Output: Delta
}
}
}
@Injectable()
export class Builder extends SchemaBuilder<SchemaBuilderOption> {
public generator: PrismaCrudGenerator<BuilderTypes>
constructor(private readonly prisma: PrismaClient) {
super({
plugins: [
PrismaPlugin,
PrismaUtils,
SimpleObjectPlugin,
SmartSubscriptionPlugin,
RelayPlugin,
ErrorsPlugin,
AuthzPlugin,
ZodPlugin,
],
smartSubscriptions: {
debounceDelay: 1000,
...subscribeOptionsFromIterator((name, context) => {
return context.isSubscription
? context.websocket.pubSub.asyncIterableIterator(name)
: context.http.pubSub.asyncIterableIterator(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: (info) => {
Logger.log(`Unused query: ${info.fieldName}`, 'GraphQL')
},
dmmf: getDatamodel(),
},
errors: {
defaultTypes: [],
},
})
this.generator = new PrismaCrudGenerator<BuilderTypes>(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.scalarType('Delta', {
serialize: (value) => JSON.stringify(value),
parseValue: (value: unknown) => JSON.parse(value as string) as Delta,
parseLiteral: (ast: ValueNode) => ast as unknown as Delta,
})
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<SchemaBuilderOption>