hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2
https://www.npmjs.com/package/next-connect
MIT License
1.64k stars 65 forks source link

Running custom handler from GraphQL #173

Closed gyto closed 2 years ago

gyto commented 2 years ago

I am trying to create a simple handle with Apollo Server where it requests the user auth before serving the specific schemas. However, when I did such an option.

import { ApolloServer } from 'apollo-server-micro';
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core';
import { makeExecutableSchema } from '@graphql-tools/schema';
import typeDefs from "graphql/schema";
import resolvers from "graphql/resolvers";
import nextConnect from "next-connect";
import auth from "database/auth";

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

const apolloServer = new ApolloServer({
  schema,
  introspection: true,
  playground: true,
  plugins: [ ApolloServerPluginLandingPageGraphQLPlayground() ],
  context: async ({ req }) => {
    console.log(req.user);
    return {
      user: req.headers.user || "",
      getUser: () => req.user,
      logout: () => req.logout(),
    }
  }
});

export const config = {
  api: {
    bodyParser: false,
    externalResolver: true
  }
};

const startServer = apolloServer.start();
const handler = nextConnect();

handler
  .use(auth)
  .use(async (req, res) => {
    await startServer;
    await apolloServer.createHandler({
      path: "/api/graphql"
    })(req, res)
  })

export default handler;

However, in server return, I am getting an error with error - unhandledRejection: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I am also using this example as my auth indignation for serverless option

I also saw an example with express-graphql which actually works fine, but I don't want to rewrite my logic to the old version. I am trying to use Apollo Server for this.

So my question is in which scenario nextConnect can server apollo server with creating additional handlers, or do I have to fully charge it to express server to serve this handler so that my auth will work? I did not see any similar examples of nextConnect and apollo server setups that could work together.

heschong commented 2 years ago

I have something similar (I "start" the server earlier, not in the handler), but instead of .use() to attach it, I used .all('/api/graphql', <handler>) and it seems to work well.

gyto commented 2 years ago

So this is the final decision I made to run graphQL as a serverless service and everything looks super easy to setup

import { makeExecutableSchema } from '@graphql-tools/schema';
import typeDefs from "graphql/schema";
import resolvers from "graphql/resolvers";
import { createRouter } from "next-connect";
import { graphqlHTTP } from "express-graphql";
import auth from "database/auth";

const schema = makeExecutableSchema({
    typeDefs,
    resolvers,
});

export const config = {
    api: {
        bodyParser: true,
        externalResolver: true,
        responseLimit: '5mb',
    }
};

const router = createRouter();

router
    .use(async (req, res, next) => {
        res.setHeader('Access-Control-Allow-Credentials', 'true')
        res.setHeader(
            'Access-Control-Allow-Headers',
            'Origin, X-Requested-With, Content-Type, Accept'
        )
        if (req.method === 'OPTIONS') {
            res.end();
            return false;
        }

        await next()
    })
    .use(auth)
    .post(graphqlHTTP((req) => ({
        schema,
        context: {
            user: req.user
        },
        graphiql: process.env.NODE_ENV === "development",
    })))

export default router.handler();