Clement
Clement

Reputation: 4811

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?

Upvotes: 3

Views: 10488

Answers (3)

Andrew Birks
Andrew Birks

Reputation: 890

The following worked out well for me. I spotted this in the docs here:

https://docs.nestjs.com/graphql/quick-start#accessing-generated-schema

I added a check to see if the ENV was production, as the location I wanted the file generating does already exist when in development mode.

import { NestFactory } from '@nestjs/core';
import { GraphQLSchemaHost } from '@nestjs/graphql';
import { writeFileSync } from 'fs';
import { printSchema } from 'graphql';
import { join } from 'path';
import { ServerModule } from './server.module';

async function bootstrap() {
  const app = await NestFactory.create(ServerModule);
  await app.listen(process.env.PORT || 3001);

  if (process.env.NODE_ENV === 'production') {
    const { schema } = app.get(GraphQLSchemaHost);
    writeFileSync(join(process.cwd(), `/src/schema.gql`), printSchema(schema));
  }
}
bootstrap();

Upvotes: 5

Hayden
Hayden

Reputation: 101

A simpler implementation of Generating the SDL manually

Start with nest start --entryFile generate-schema if you're using the graphql cli-plugin

src/generate-schema.ts

import { NestFactory } from '@nestjs/core';
import { GraphQLSchemaBuilderModule, GraphQLSchemaFactory } from '@nestjs/graphql';
import { writeFileSync } from 'fs';
import { printSchema } from 'graphql';
import { join } from 'path';

const resolvers = [
  // Your resolvers here
];

const scalars = [
  // Your scalars here
];

const main = async () => {
  const app = await NestFactory.create(GraphQLSchemaBuilderModule);
  await app.init();

  const gqlSchemaFactory = app.get(GraphQLSchemaFactory);
  const schema = await gqlSchemaFactory.create(resolvers, scalars);

  writeFileSync(join(process.cwd(), '/schema.graphql'), printSchema(schema));
};
main();

Upvotes: 2

Clement
Clement

Reputation: 4811

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))

Upvotes: 3

Related Questions