mercurius-js / mercurius-typescript

TypeScript usage examples and "mercurius-codegen" for Mercurius
https://mercurius.dev
MIT License
71 stars 18 forks source link

Issue with codegenMercurius Function Triggering fastify-swagger Error #820

Open luthfi-hh opened 8 months ago

luthfi-hh commented 8 months ago

I encountered an error with the codegenMercurius function that seems to be causing a problem with fastify-swagger. When I include the codegenMercurius function in my code, it triggers an error in swagger-ui. However, upon removing the codegenMercurius function, the fastify-swagger functionality resumes working as expected. This issue is affecting my workflow, and I would appreciate any insights or solutions to resolve it. Thank you.

Errors:

ERROR (37310): .swagger() must be called after .ready()
Screenshot 2024-02-19 at 17 56 32 Screenshot 2024-02-19 at 17 51 24

My Code (app.ts):

import { join } from 'path';
import FastifyEnv from '@fastify/env';
import FastifyCors from '@fastify/cors';
import FastifyAutoLoad, { AutoloadPluginOptions } from '@fastify/autoload';
import FastifySwagger from '@fastify/swagger';
import FastifySwaggerUI from '@fastify/swagger-ui';
import { FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from 'fastify';
import Mercurius from 'mercurius';
import { codegenMercurius } from 'mercurius-codegen'
import { resolvers } from './graphql/resolvers';
import { schema } from './graphql/schema';

export interface AppOptions extends FastifyServerOptions, Partial<AutoloadPluginOptions> {
}

// Pass --options via CLI arguments in command to enable these options.
const options: AppOptions = {
}

const buildContext = async (req: FastifyRequest, _reply: FastifyReply) => {
  return {
    authorization: req.headers.authorization
  }
}

type PromiseType<T> = T extends PromiseLike<infer U> ? U : T

declare module 'mercurius' {
  interface MercuriusContext extends PromiseType<ReturnType<typeof buildContext>> { }
}

const app: FastifyPluginAsync<AppOptions> = async (
  fastify,
  opts
): Promise<void> => {
  // This loads the .env file in the root directory.
  void fastify.register(FastifyEnv, {
    dotenv: true,
    schema: {
      type: 'object',
      required: [
        'NODE_ENV',
        'OPENSEARCH_NODE',
        'OPENSEARCH_USER',
        'OPENSEARCH_PASSWORD',
        'MYSQL_HOST',
        'MYSQL_PORT',
        'MYSQL_USER',
        'MYSQL_PASSWORD',
        'MYSQL_DATABASE',
        'MYSQL_CONNECTION_LIMIT',
      ],
    }
  })

  // This loads the CORS plugin.
  void fastify.register(FastifyCors, {
    origin: '*',
    methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'PATCH'],
  })

  // This loads fastify-swagger plugin.
  void fastify.register(FastifySwagger, {
    swagger: {
      info: {
        title: 'HH Search API',
        description: 'API for HH Search using OpenSearch',
        version: '0.1.0'
      },
      externalDocs: {
        url: 'https://swagger.io',
        description: 'Find more info here'
      },
      host: 'localhost:3003',
      schemes: ['http', 'https'],
      consumes: ['application/json'],
      produces: ['application/json'],
      tags: [
        { name: 'client', description: 'Client related end-points' },
        { name: 'server', description: 'Server related end-points' }
      ],
      definitions: {
        User: {
          type: 'object',
          required: ['id', 'email'],
          properties: {
            id: { type: 'string', format: 'uuid' },
            firstName: { type: 'string' },
            lastName: { type: 'string' },
            email: { type: 'string', format: 'email' }
          }
        }
      },
      securityDefinitions: {
        apiKey: {
          type: 'apiKey',
          name: 'apiKey',
          in: 'header'
        }
      }
    }
  })

  // This loads fastify-swagger-ui plugin.
  void fastify.register(FastifySwaggerUI, {
    routePrefix: '/docs',
    uiConfig: {
      docExpansion: 'full',
      deepLinking: false
    },
    uiHooks: {
      onRequest: function (request: any, reply: any, next: any) { next() },
      preHandler: function (request: any, reply: any, next: any) { next() }
    },
    staticCSP: true,
    transformStaticCSP: (header: any) => header,
    transformSpecification: (swaggerObject: any, request: any, reply: any) => { return swaggerObject },
    transformSpecificationClone: true
  })

  // This loads the mercurius plugin (GraphQL).
  void fastify.register(Mercurius, {
    schema,
    resolvers,
    jit: 1,
    graphiql: 'graphiql',
    context: buildContext
  })

  // This code causing fastify-swagger Errror
  codegenMercurius(fastify, {
    targetPath: './src/graphql/generated.ts',
    operationsGlob: './src/graphql/operations/*.gql',
    codegenConfig: {
      loadersCustomParentTypes: {
        Human: 'never',
      },
    },
  }).catch(console.error)

  // Do not touch the following lines

  // This loads all plugins defined in plugins
  // those should be support plugins that are reused
  // through your application
  void fastify.register(FastifyAutoLoad, {
    dir: join(__dirname, 'plugins'),
    options: opts
  })

  // This loads all plugins defined in routes
  // define your routes in one of these
  void fastify.register(FastifyAutoLoad, {
    dir: join(__dirname, 'routes'),
    options: opts
  })
};

export default app;
export { app, options }