Detailed Problem Description for Apollo Federation Gateway with NestJS

Detailed Problem Description for Apollo Federation Gateway with NestJS


Context

I am working on a federated GraphQL setup using Apollo Federation 2 with NestJS. I have a supergraph running on a gateway and two subgraphs. I’m encountering a schema generation issue when configuring one of the subgraphs using forRootAsync while the gateway fails to introspect and merge the subgraphs.


Supergraph Configuration

The supergraph (gateway) is configured as follows:

const isProd = process.env.ACTIVE_PROFILE == 'production' || process.env.ACTIVE_PROFILE == 'prod';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
      driver: ApolloGatewayDriver,
      server: {
        playground: false,
        plugins: isProd ? [] : [ApolloServerPluginLandingPageLocalDefault()],
      },
      gateway: {
        supergraphSdl: new IntrospectAndCompose({
          subgraphs: [
            { name: 'hexagon-go', url: 'https://dev.hexagon-graphql.reefos.ai/' },
            { name: 'example', url: 'http://localhost:3000/graphql/' },
          ],
        }),
      },
    }),
  ],
})
export class FedGqlModule {}

Subgraph Configuration

The subgraph has two versions of the configuration:

1. Synchronous (Working) Configuration:

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloFederationDriverConfig>({
      driver: ApolloFederationDriver,
      autoSchemaFile: {
        federation: 2,
      },
      buildSchemaOptions: {
        orphanedTypes: [HexagonExternalUser],
      },
      plugins: [],
      introspection: true,
      resolvers: { ID: BigIntIdScalarType },
    }),
  ],
  providers: [PostsResolver, PostsService],
})
export class AppModule {}

With this configuration, the schema generates successfully, and the supergraph can introspect and merge it correctly.

2. Asynchronous (Failing) Configuration:

When I switch to forRootAsync, the schema does not generate, and the gateway fails to compose:

@Module({
  imports: [
    GraphQLModule.forRootAsync<ApolloFederationDriverConfig>({
      driver: ApolloFederationDriver,
      useFactory: apolloConfigFactory,
    }),
  ],
  providers: [PostsResolver, PostsService],
})
export class AppModule {}

apolloConfigFactory looks like this:

import { ApolloFederationDriver, ApolloFederationDriverConfig } from '@nestjs/apollo';
import { BigIntIdScalarType } from '../scalars/custom-id.scalar';

const isProd = process.env.ACTIVE_PROFILE === 'production' || process.env.ACTIVE_PROFILE === 'prod';

export const apolloConfigFactory = async () =>
  <ApolloFederationDriverConfig>{
    cors: {
      origin: '*',
      credentials: true,
    },
    debug: !isProd,
    introspection: !isProd,
    autoSchemaFile: {
      federation: 2,
    },
    resolvers: { ID: BigIntIdScalarType },
    plugins: [],
    context: ({ req, res }) => {
      return { req, res };
    },
  };

Issue

  1. Schema Not Generated: When using forRootAsync, the subgraph schema does not get generated, leading to a 400: Bad Request error when the supergraph tries to introspect it.

  2. Gateway Errors:

    • The supergraph cannot load the subgraph schema at http://localhost:3000/graphql/ and throws an error:
      Error: Couldn't load service definitions for "example" at http://localhost:3000/graphql/: 400: Bad Request
      
  3. Differences:

    • With the synchronous configuration, everything works as expected.
    • The orphanedTypes parameter in buildSchemaOptions is required in the synchronous version but cannot be passed in forRootAsync.
  4. No Log Errors: In the failing case, there are no visible errors in the logs for the subgraph server.


Questions

  1. Why does the schema fail to generate when using forRootAsync with autoSchemaFile: { federation: 2 }?
  2. Does forRootAsync in NestJS GraphQL not fully support all Federation 2 schema generation options?
  3. Is the buildSchemaOptions required for orphanedTypes explicitly necessary in the synchronous configuration? If so, how can this be passed in forRootAsync?
  4. Are there differences in lifecycle hooks for schema generation when using forRoot vs forRootAsync?
  5. Is there any additional configuration or workaround required to make forRootAsync work with Apollo Federation 2?

Additional Information

  • Environment:

    • Node.js: v18.18.2
    • @nestjs/graphql: latest
    • @apollo/gateway: latest
    • @apollo/federation: 2.x
  • Observations:

    • Switching to synchronous config fixes the issue.
    • The federation supergraph does not recognize the schema for subgraphs running with forRootAsync.

Goal

I need to understand why the schema fails to generate in forRootAsync and how to resolve this to ensure proper Federation 2 compatibility in my subgraph.

Any insights, suggestions, or pointers to documentation would be greatly appreciated!