nextjs-nestjs-integration-example icon indicating copy to clipboard operation
nextjs-nestjs-integration-example copied to clipboard

Doesn't work when using with @nest/graphql

Open ArturJS opened this issue 4 years ago • 7 comments

@Skn0tt Thanks for awesome article about nextjs + nestjs integration!

I tried using this approach for passing nextjs requests to nestjs based backend module but encountered into a problem with no server response when sending POST graphql requests to /api/graphql endpoint. My first thought was that it could be related to the issue with race condition but unfortunately the fix didn't solve that problem.

Here's the repo https://github.com/ArturJS/nextjs-nestjs-integration-issue with that issue.

Additional info: when navigating to http://localhost:3000/api/graphql it successfully loads graphql playground, so looks like only POST requests are getting stuck.

ArturJS avatar Oct 18 '21 18:10 ArturJS

Hi @ArturJS! I don't know enough about GraphQL to debug this easily. Feel free to look into the issue - when you find a fix to this repo that helps, I'd love to review a PR!

Skn0tt avatar Oct 19 '21 07:10 Skn0tt

Hey @ArturJS, funny enough, I'm also working on such an integration. The foundation @Skn0tt built with this example is awesome. The actual integration of @nestjs/graphql works for me (playground + interacting with the API). This is what I did:

First of all I added a [...catchAll].ts and a index.ts in pages/api/graphql (both files are basically structured like demonstrated in this example) and booted my NestJS-based GraphQL API via:


@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
      bodyParserConfig: false,
      cors: false,
      path: "/api/graphql",
      playground: true,
      introspection: true,
    })
  ],
  controllers: [],
  providers: [
    ...providers,
    ...resolvers,
  ],
})
export class AppModule {}

let app: NestApplication;

export const createGraphQlApi = async () => {
  if (!app) {
    app = await NestFactory.create(AppModule, {
      bodyParser: false,
      logger: ["error", "warn"],
    });

    app.setGlobalPrefix("api/graphql");

    app = await app.init();
  }

  const server = app.getHttpServer();

  const [requestHandler] = server.listeners("request") as NextApiHandler[];

  return requestHandler;
};

I think adding bodyParser: false when creating the app and also adding bodyParserConfig: false did the trick 🙂 It works like a charm, although I'm currently fighting with injecting my providers into my resolvers. This is currently my problem with the setup as the injected providers are always undefined. I'm wondering if this is related to using app.init() instead of app.listen. Looks like that this results in a different booting behavior of the dependency injection mechanism.

akoenig avatar Oct 27 '21 21:10 akoenig

@akoenig, many thanks! Now it finally works! @Skn0tt, I'll try to prepare PR for this repo soon.

ArturJS avatar Oct 28 '21 09:10 ArturJS

@ArturJS You're welcome 🙂 Glad I could help 🙂 Just wondering: Have you been able to have exactly one instance of the actual NestJS app? My service is complaining about multiple types in my schema (Schema must contain uniquely named types but contains multiple types named Foo). Looks like the @nestjs/graphql module is somehow shared between the actual Next.js api route requests.

akoenig avatar Oct 28 '21 17:10 akoenig

@akoenig Yes, but with using global object as a workaround. Here's my PR https://github.com/ArturJS/nextjs-nestjs-integration-issue/pull/3 Interesting thing here is that after almost every request to /api/graphql happens event - build page: /api/graphql which led to multiple instances of NestJS app. Let me know if you find better way than with using global.

ArturJS avatar Oct 28 '21 17:10 ArturJS

Hey @ArturJS, thanks for your response. global does the trick for the moment. In the end importing ES modules (and defining the app variable within that scope) should behave the same, but it looks like there is some magic going on in NestJS land. Anyways, thanks a lot @ArturJS for tackling the problem 🙂

akoenig avatar Oct 29 '21 07:10 akoenig

Hey @ArturJS, funny enough, I'm also working on such an integration. The foundation @Skn0tt built with this example is awesome. The actual integration of @nestjs/graphql works for me (playground + interacting with the API). This is what I did:

First of all I added a [...catchAll].ts and a index.ts in pages/api/graphql (both files are basically structured like demonstrated in this example) and booted my NestJS-based GraphQL API via:

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
      bodyParserConfig: false,
      cors: false,
      path: "/api/graphql",
      playground: true,
      introspection: true,
    })
  ],
  controllers: [],
  providers: [
    ...providers,
    ...resolvers,
  ],
})
export class AppModule {}

let app: NestApplication;

export const createGraphQlApi = async () => {
  if (!app) {
    app = await NestFactory.create(AppModule, {
      bodyParser: false,
      logger: ["error", "warn"],
    });

    app.setGlobalPrefix("api/graphql");

    app = await app.init();
  }

  const server = app.getHttpServer();

  const [requestHandler] = server.listeners("request") as NextApiHandler[];

  return requestHandler;
};

I think adding bodyParser: false when creating the app and also adding bodyParserConfig: false did the trick 🙂 It works like a charm, although I'm currently fighting with injecting my providers into my resolvers. This is currently my problem with the setup as the injected providers are always undefined. I'm wondering if this is related to using app.init() instead of app.listen. Looks like that this results in a different booting behavior of the dependency injection mechanism.

Hey @akoenig would you be so kind to give us a working github repo? Thanks in advance 🙂

killjoy2013 avatar Nov 08 '21 12:11 killjoy2013