graphqlnestjs

Nest.js GraphQL Schema generation during build


I'm using the code-first approach to GraphQL with NestJS and have a monorepo setup using Nx.

The schema.gql is only produced when I run the server, which I can't do during CI. It's impractical for me to copy the whole repository into the docker image and start the server. The schema.gql isn't generated when you build the nest application.

I've also looked at Generating the SDL manually doc on the NestJS website, but not really sure how to integrate that script.

Just wondering if someone has managed to generate the schema without starting the server?


Solution

  • Managed to figure out how to integrate Generating the SDL manually.

    Just executing the generate SDL function before you start your nest server will work.

    Included a full sample below.

    const resolvers = [MyResolver]
    
    /**
     * Generate GraphQL schema manually. NestJS does not generate the GraphQL schema
     * automatically during the build process and it doesn't generate the GraphQL
     * schema when starting the built app. This schema needs to be generated or
     * the GraphQL api would have nothing to use.
     *
     * @param {MyServices} serviceName - Name of the gavel service to generate a unique
     * schema.
     * @param {Function[]} resolvers - List of GraphQL resolvers being used in that app.
     * @returns {Promise<void>} Nothing gets returned. It will just write the schema and
     * throw an error if it fails.
     */
    export async function generateGraphQLSchema(
      serviceName: MyServices,
      resolvers: Function[],
    ): Promise<void> {
      const app = await NestFactory.create(GraphQLSchemaBuilderModule)
      await app.init()
    
      const gqlSchemaFactory = app.get(GraphQLSchemaFactory)
      const schema = await gqlSchemaFactory.create(resolvers)
    
      writeFileSync(join(process.cwd(), `/${serviceName}-schema.gql`), printSchema(schema))
    }
    
    /**
     * Setup nest application.
     *
     * @param {string} port - Port the application should listen to.
     * @param {unknown} appModule - Main app module from a nest application.
     * @param {MyServices} serviceName - Name of the gavel service to generate a unique
     * schema.
     * @returns {Promise<void>}
     */
    async function bootstrap(port: string, appModule: any, serviceName: MyServices): Promise<void> {
      const app = await NestFactory.create(appModule)
    
      // Endpoint prefix
      const globalPrefix = `${config.get(`env`)}/v1/${serviceName}`
      app.setGlobalPrefix(globalPrefix)
    
      // Start Server
      await app.listen(port, () => {
        Logger.log(`Listening at http://localhost:${port}/${globalPrefix}/graphql`)
      })
    }
    
    /**
     * Helper to crate standard Nest JS Server.
     *
     * @param {MyServices} serviceName - Name of service.
     * @param {unknown} appModule - Main app module from a nest application.
     */
    export function initializeServer(serviceName: GavelService, appModule: any): void {
      const environment = config.get(`env`)
      const port = config.get(`port`)
      initializeElasticApm(`service-${serviceName}`, {
        framework: `nest`,
        environment,
        version: `${packageJson.version}`,
      })
      bootstrap(port, appModule, serviceName).catch((error) => {
        const logger = getElasticSearchLogger(serviceName)
        logger.error(
          { error, environment: config.get(`env`), applicationName: serviceName },
          error.message,
        )
      })
    }
    
    generateGraphQLSchema(MyServices.SERVICE_A, resolvers)
      .then(() => initializeServer(MyServices.SERVICE_A, AppModule))
      .catch((e) => console.error(e))