apollographql / apollo-server

🌍  Spec-compliant and production ready JavaScript GraphQL server that lets you develop in a schema-first way. Built for Express, Connect, Hapi, Koa, and more.
https://www.apollographql.com/docs/apollo-server/
MIT License
13.76k stars 2.03k forks source link

Error in 3.0+ version of apollo-server-micro with Next.js, requires server.start() in server less environment #5547

Closed naranoeur closed 3 years ago

naranoeur commented 3 years ago

The code was working fine with apollo-server-micro@2.25.1, but now with apollo-server-micro@^3.0.2, it breaks.

It should be able to createHandler for Next.js without starting the server, since Next.js api routes run in server less environment. I tried starting the server first, but then the handler it produces, Next.js cannot use and throws errors.

Error with stack trace: Error: You mustawait server.start()before callingserver.createHandler() at ApolloServer.assertStarted (/Users/naranoeur/Desktop/projects/graphql-blog/node_modules/apollo-server-core/dist/ApolloServer.js:242:19) at ApolloServer.createHandler (/Users/naranoeur/Desktop/projects/graphql-blog/node_modules/apollo-server-micro/dist/ApolloServer.js:13:14) at eval (webpack-internal:///./pages/api/graphql.js:72:30) at Object../pages/api/graphql.js (/Users/naranoeur/Desktop/projects/graphql-blog/.next/server/pages/api/graphql.js:22:1) at __webpack_require__ (/Users/naranoeur/Desktop/projects/graphql-blog/.next/server/webpack-runtime.js:33:42) at __webpack_exec__ (/Users/naranoeur/Desktop/projects/graphql-blog/.next/server/pages/api/graphql.js:54:52) at /Users/naranoeur/Desktop/projects/graphql-blog/.next/server/pages/api/graphql.js:55:28 at Object.<anonymous> (/Users/naranoeur/Desktop/projects/graphql-blog/.next/server/pages/api/graphql.js:58:3) at Module._compile (node:internal/modules/cjs/loader:1095:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)

carlosazaustre commented 3 years ago

Same here, I tried with this code, and get another error stack trace:


// file: pages/api/graphql.js
const typeDefs = {...};
const resolvers = {...};

export default (async () => {
  const server = new ApolloServer({ typeDefs, resolvers, playground: true });
  await server.start();
  const handler = server.createHandler();

  return handler;
})();
TypeError: resolver is not a function
    at apiResolver (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/api-utils.js:8:7)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async DevServer.handleApiRequest (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/next-server.js:66:462)
    at async Object.fn (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/next-server.js:58:580)
    at async Router.execute (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/router.js:25:67)
    at async DevServer.run (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/next-server.js:68:1042)
    at async DevServer.handleRequest (/Users/carlosazaustre/dev/recipe-web-app/node_modules/next/dist/next-server/server/next-server.js:32:504)
carlosazaustre commented 3 years ago

🎉 Solved!

You need to install micro along as apollo-server-micro,

And this is the code that works, kudos to William Lyon (@lyonwj)

import { ApolloServer, gql } from "apollo-server-micro";

const typeDefs = {...};
const resolvers = {...};

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
});

const startServer = apolloServer.start();

export default async function handler(req, res) {
  await startServer;
  await apolloServer.createHandler({
    path: "/api/graphql",
  })(req, res);
}

export const config = {
  api: {
    bodyParser: false,
  },
};
marklawlor commented 3 years ago

@carlosazaustre While that does work, it does not address the original issue.

Apollo's documentation state Call start only if you are using a middleware integration for a non-"serverless" environment .

Most Next.JS projects are "serverless" and will have a negative performance impact by calling .start()

Apollo do not count apollo-server-micro as a serverless package, but most users are probably using it in a serverless environment. There should be a mode to allow apollo-server-micro to operate in a serverless mode similar to apollo-server-lambda.

naranoeur commented 3 years ago

When selecting Apollo server package, I could not find documentation for which package was best suited for next.js. Most tutorials use apollo-server-micro, and it did work until now.

It states that apollo-server-lambda is for the AWS Lambda, and then there are specific packages for Azure, Google, Cloudfare. So apollo-server-lamda, along with the other platform specific packages did not seem to be the right package to use for Next.js app.

sachinraja commented 3 years ago

I'm really confused. It's not only apollo that does not count micro as a serverless package, but also vercel. In the micro readme it says this:

Disclaimer: Micro was created for use within containers and is not intended for use in serverless environments. For those using Vercel, this means that there is no requirement to use Micro in your projects as the benefits it provides are not applicable to the platform. Utility features provided by Micro, such as json, are readily available in the form of Serverless Function helpers.

So why is next.js using micro in its examples? Is it the same as any other HTTP microservice package? If so it wouldn't make sense for only apollo-server-micro to have a "serverless mode". If someone could help clear this up for me, that would be great.

nathantqn commented 3 years ago

I came up with a solution like @carlosazaustre, it works fine. However, I'm facing an issue of integrating the Apollo Subscription into next.js. The issue located here https://github.com/apollographql/apollo-server/issues/2639 still cannot resolve the issue. I really want to make the server located in same origin with Next.js, but I'm not sure whether it affects the performance. If anyone has a solution for this, please post it here, many thanks!

ghost commented 3 years ago

ntegrating the Apollo Subscription into next.js. The issue located here #2639 still cannot resolve the issue. I really want to make the server located

But your solution isn't really addressing the original issue, is it? Are you calling .start()?

glasser commented 3 years ago

I don't think we make any claim that Apollo Server works out of the box with Next.js. I honestly am having trouble understanding what the difference between Next.js and micro is exactly — is there a concise explanation in their docs that I'm not finding? I get that they are somehow related and both come from Vercel, but a Google search for site:nextjs.org "micro" turns up nothing, and Next is not mentioned in the minimal micro docs.

It sounds like Next.js is popular and it would be cool if there was an easy way to use Apollo Server with it (though perhaps this could be a community package, as we are still hoping to move in the direction of fewer framework integrations maintained directly by a core team who doesn't actually use most of them), but I don't think we make any claims that it does today so I don't think this is a bug?

glasser commented 3 years ago

I did a bit more research and I found this thread which suggests that by enabling the webpack experiment named topLevelAwait you can just do await server.start() at the top level in your Next.js API route. Does that solve this issue?

sachinraja commented 3 years ago

@glasser I don't think that was the main issue, though many people were definitely struggling with that. The main issue is #5547 (comment):

Most Next.JS projects are "serverless" and will have a negative performance impact by calling .start()

I'm assuming this can only be solved with an apollo-server-nextjs though.

glasser commented 3 years ago

I mean, startup has to happen for Apollo Server to work, one way or the other. The lambda and cloud functions ones put that into the first request handler, which isn't particularly great.

marklawlor commented 3 years ago

Hey @glasser I noticed you've been reaching out to developers for clarification on this issue and I really appreciate that. But I feel issues like this popup due to a surprising lack of information on how to integrate Apollo and Next.JS

@stemmlerjs @kkemple have written blog posts about Apollo Client, but nothing about Apollo Server, and nothing past a getting started.

For example, those posts also don't explain to enable Apollo Client's SSR using getDataFromTree with Next.JS. There is a discussion with 100+ comments but there's no consensus on a solution. The recommended solutions are manually build your own cache by importing every query that will be run. Which doesn't scale past a getting started example.

Maybe this needs a community solution, but I'm hoping someone from Apollo's Developer Advocate team could write blog post could clarify exactly how to use Apollo Server with Next.JS which also show cases how to incorporate advanced Apollo features like getDataFromTree.

hwillson commented 3 years ago

Thanks for this @marklawlor - I'll pass this to our devrel team. Getting more concrete examples put together that show how to use Next.js with Apollo Client and Apollo Server is definitely something we want to do, and enhancing our SSR approach in Apollo Client to make sure it works more seamlessly with Next specifically, is something we're planning.

tettoffensive commented 2 years ago

I'm just finally upgrading my project to the latest NextJS 11 and the latest apollo-server-micro. Is there a best practice for this yet? I see references to topLevelAwait or wrapping the handler. Or maybe some other solution?

glasser commented 2 years ago

@tettoffensive The Apollo Server team continues to not understand what the relationship between NextJS and Micro is and why you would want to use them together, other than the existence of an example that combines them.

tettoffensive commented 2 years ago

@glasser I see. I'm guessing most are using it precisely because of this example. When I learned how to use graphql with NextJS 9 this is what I've found. Now I'm taking this old project which previously just needed the lines:

const handler = apolloServer.createHandler({ path: '/api/graphql' });
export default withSession<NextIronHandler>(handler);

But something changed in the upgrade and now it wants apolloServer.start() to run first

glasser commented 2 years ago

Yes, I asked the author of the example why they chose Micro a couple of months ago and haven't heard back.

marklawlor commented 2 years ago

I'm not sure why its the communities responsibility to explain why the Apollo/NextJS user base is using micro. In lieu of an official example, the community is going to use the best (only?) documented solution.

Continuing this discussion a little bit, if people are interested in using Apollo Server with NextJS, codepen.io recently blogged on how they are using NextJS and Apollo. However the NextJS devs responded that this may have performance impacts, (direct link to comment) make sure you understand those gotchas.

The apollo-server-vercel also exists, but recently updated their README to explain that the project is still under development and has no guarantee of maintenance.

tettoffensive commented 2 years ago

Thanks @marklawlor. I'll just stick with 2.0 for now. Not worth messing with for me.

glasser commented 2 years ago

I filed https://github.com/vercel/next.js/pull/30082 to fix Next.js's examples.

leo-petrucci commented 2 years ago

Stumbled upon this thread while updating some of my code. As much as I like apollo-server-micro I've ended up switching to the new release of graphql-yoga for my serverless API deployed on Vercel.

I was pretty wary of using micro from the beginning as there seems to be a lot of unknowns on the fate of the project, and even then using it for my serverless API didn't seem like it was what the package was made for.

Either way:

// from
const api = new ApolloServer({
  schema,
  context: createContext,
  playground: true,
  introspection: true,
}).createHandler({
  path: "/api",
});

// to
const server = createServer<{
  req: NextApiRequest;
  res: NextApiResponse;
}>({
  endpoint: "/api",
  schema,
  context: createContext,
  maskedErrors: false,
});

The above was all I needed to have the same implementation. Hope this helps someone.

tettoffensive commented 2 years ago

@creativiii Thanks for this. I've been using: https://github.com/apuyou/apollo-server-nextjs, but it does seem like graphql-yoga might be a good solution since they actually mention Next.js integration in their docs: https://www.graphql-yoga.com/docs/integrations/integration-with-nextjs

shadeemerhi commented 2 years ago

@tettoffensive Have you been able to successfully get subscriptions working with graphql-yoga in your Next project?

tettoffensive commented 2 years ago

@tettoffensive Have you been able to successfully get subscriptions working with graphql-yoga in your Next project?

Haven't tried subscriptions. I'm using graphql-yoga and it's been great. Pulled in a different direction for now. If you get it working I'd love to hear about it!

cindyloo commented 1 year ago

this is updated and appears to work https://github.com/vercel/next.js/blob/canary/examples/api-routes-apollo-server-and-client

leo-petrucci commented 1 year ago

this is updated and appears to work https://github.com/vercel/next.js/blob/canary/examples/api-routes-apollo-server-and-client

That's running version 2.x.