Skip to content

Serverless Apollo GraphQL with Next.js

In this tutorial, we will learn how to create a serverless Apollo GraphQL server using TypeScript and Next.js. By leveraging the power of Apollo and Next.js, we can build a robust serverless GraphQL API.

Step 1: Set up the Next.js project

First, let’s set up a new Next.js project.

npx create-next-app my-apollo-app
cd my-apollo-app

Step 2: Install Dependencies

Next, let’s install the required dependencies for our project. In the terminal, navigate to the project root directory and run:

npm install @apollo/server

Step 3: Create the Apollo Server

In the project directory, create a new file named startServerAndCreateNextHandler.ts. This file will contain the code for creating the Apollo server and the Next.js request handler. Copy the following code into the file

import {
  ApolloServer,
  BaseContext,
  ContextFunction,
  HeaderMap,
} from "@apollo/server";
import type { WithRequired } from "@apollo/utils.withrequired";
import { NextApiHandler } from "next";
import { parse } from "url";

interface Options<Context extends BaseContext> {
  context?: ContextFunction<Parameters<NextApiHandler>, Context>;
}

const defaultContext: ContextFunction<[], any> = async () => ({});

const allowCors =
  <A extends Function>(fn: A): NextApiHandler =>
  async (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader(
      "Access-Control-Allow-Methods",
      "GET,OPTIONS,PATCH,DELETE,POST,PUT"
    );
    res.setHeader(
      "Access-Control-Allow-Headers",
      "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, authorization"
    );
    if (req.method === "OPTIONS") {
      res.status(200).end();
      return;
    }
    return await fn(req, res);
  };

function startServerAndCreateNextHandler(
  server: ApolloServer<BaseContext>,
  options?: Options<BaseContext>
): NextApiHandler;
function startServerAndCreateNextHandler<Context extends BaseContext>(
  server: ApolloServer<Context>,
  options: WithRequired<Options<Context>, "context">
): NextApiHandler;
function startServerAndCreateNextHandler<Context extends BaseContext>(
  server: ApolloServer<Context>,
  options?: Options<Context>
) {
  server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();

  const contextFunction = options?.context || defaultContext;

  const handler: NextApiHandler = async (req, res) => {
    const headers = new HeaderMap();

    for (const [key, value] of Object.entries(req.headers)) {
      if (typeof value === "string") headers.set(key, value);
    }

    const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
      context: () => contextFunction(req, res),
      httpGraphQLRequest: {
        body: req.body,
        headers,
        method: req.method || "POST",
        search: req.url ? parse(req.url).search || "" : "",
      },
    });

    for (const [key, value] of httpGraphQLResponse.headers) {
      res.setHeader(key, value);
    }

    res.statusCode = httpGraphQLResponse.status || 200;

    if (httpGraphQLResponse.body.kind === "complete") {
      res.send(httpGraphQLResponse.body.string);
    } else {
      for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
        res.write(chunk);
      }
      res.end();
    }
  };

  return allowCors(handler);
}

export { startServerAndCreateNextHandler };

Step 4: Create the GraphQL Endpoint

Next, let’s create the GraphQL endpoint in the api/graphql.ts file. Create a new file named api/graphql.ts in the project directory and copy the following code into it:

import { ApolloServer, BaseContext } from "@apollo/server";
import { startServerAndCreateNextHandler } from "./startServerAndCreateNextHandler";
import jwt from "jsonwebtoken";

import { resolvers } from "graphql/resolvers";
import { typeDefs } from "graphql/schema/schema";

export interface ApolloContext extends BaseContext {}

const server = new ApolloServer<ApolloContext>({
  resolvers,
  typeDefs,
  introspection: true,
});

export default startServerAndCreateNextHandler(server, {
  context: async (req, res) => ({
    req,
    res,
  }),
});

Step 5: Define GraphQL Schema and Resolvers

Now, let’s define the GraphQL schema and resolvers. Create a new directory named graphql in the project directory, and inside it, create two files: resolvers.ts and schema.ts. In the resolvers.ts file, define your GraphQL resolvers as per your application’s requirements. In the schema.ts file, define your GraphQL schema using the GraphQL Schema Definition Language (SDL). Here’s an example of how these files could look:

// graphql/resolvers.ts
const resolvers = {
  Query: {
    hello: () => "Hello, World!",
  },
};

export { resolvers };

// graphql/schema.ts
const typeDefs = `
  type Query {
    hello: String!
  }
`;

export { typeDefs };

Step 6: Start the Development Server

Finally, let’s start the development server and test our serverless Apollo GraphQL API. In the terminal, run the following command from the project root directory:

npm run dev

The Next.js development server will start, and you can access the GraphQL API at http://localhost:3000/api/graphql.

Conclusion

In this tutorial, we learned how to create a serverless Apollo GraphQL server using Next.js. We set up the Apollo server, defined the GraphQL schema and resolvers, and created the GraphQL endpoint in Next.js.

You can now extend this setup to add authentication and build powerful serverless GraphQL APIs for your applications.

Happy hacking!