How to setup a GraphQL server in Next.js with API endpoints

Mahieyin Rahmun
6 min readJun 23, 2021

using TypeScript, type-graphql and apollo-server-micro

Photo by James Harrison on Unsplash

I have been learning GraphQL for a while now, and I love Next.js for the benefits it comes with. So, one morning, I decided to try and combine the two and see if I can make it work. The journey was not as smooth as I expected it to be, so I decided to document my approach. Specifically, I had two issues:

  • You need to get your configurations correct to make decorators play along nicely.
  • You need to have a specific way of creating database connections to not clash with Next.js’ Hot Module Reloading (HMR).

The aim of this article is to give you a boilerplate that will work for Next.js and GraphQL combined without any issues.

Disclaimer: This article assumes some knowledge about TypeScript, Next.js and API routes, GraphQL, PostgreSQL database and TypeORM.

Creating a new Next.js App

To start things off, we create a new Next.js app. I will be using yarn , but the same should be achievable with npm as well.

mkdir nextjs-graphql
cd nextjs-graphql
yarn create next-app . --typescript

The above series of commands will create a basic Next.js template app with TypeScript in the nextjs-graphql directory.

I will make the folder structure a bit more manageable by deleting a few files, but this is optional. This is how my folder structure looks like after deleting some of the files:

tree . -L 2 -I node_modules.
├── next.config.js
├── next-env.d.ts
├── package.json
├── pages
│ └── _app.tsx
├── public
│ ├── favicon.ico
│ └── vercel.svg
├── README.md
├── tsconfig.json
└── yarn.lock

Installing dependencies

Let’s start off by installing the dependencies that we need.

yarn add graphql apollo-server-micro reflect-metadata type-graphql class-validator
  • graphql is self-explanatory.
  • apollo-server-micro is the package that will allow us to instantiate an ApolloServer instance.
  • type-graphql is a framework for building GraphQL API using TypeScript. It has a dependency on reflect-metadata and class-validator.

Setting up a resolver and API endpoint

If you are not familiar with type-graphql , I suggest reading up on their amazing documentation. If you want to learn more about type-graphql , I recommend Ben Awad’s tutorial series on type-graphql .

We will create a hello world resolver to test things out with our GraphQL API. type-graphql includes some decorators to annotate classes which will let GraphQL know about the schema and will enable type safety in the queries. Create a file HelloWorldResolver.ts under /lib/serverless/graphql/resolvers .

As you can see, we are importing Resolver and Query decorators to annotate our resolver class. And for now, we are simply returning a string from the resolver.

At this point, your IDE should be complaining about decorators and how you need to enable a certain feature to resolve the warnings. We will deal with that later, because that is only a fragment of the issues we need to deal with. For now, we want to setup our API handler for GraphQL. Create a file index.ts under /pages/api/graphql .

We are doing a few things here:

  • Import reflect-metadata package at the very top. If you have used TypeORM before, you would be familiar with this.
  • We are importing ApolloServer from apollo-server-micro
  • We are importing buildSchema from type-graphql , which will allow us to build schema definitions directly from our annotated resolvers. Hence, we do not need gql template strings for type definitions.
  • We are disabling bodyParser of Next.js API endpoint, so that GraphQL is the one handling responses from the route.
  • Finally, after instantiating our ApolloServer instance, we are exporting, by default, the GraphQL handler for this particular API endpoint. So, when you want to perform queries, you would need to send them to http://localhost:3000/api/graphql.

At this point, you should see another error, saying that you cannot use top-level await. Now, we will deal with resolving those issues.

Resolving the configuration issues

Even if you try to launch the application using yarn dev right now, you won’t be able to reach the GraphQL endpoint. Because, we need to take care of some configuration before our TypeScript files can be properly transpiled and bundled by webpack. Next.js by default doesn’t have support for decorators enabled. There are four steps to this:

  • Modifying tsconfig.json

We need to explicitly say that we want decorator support in our app. So, add the following lines in your tsconfig.json , under compilerOptions :

"target": "es2018",
"lib": ["dom", "dom.iterable", "esnext", "esnext.asynciterable"],
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
  • Installing a few dependencies
yarn add -D @babel/core @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties babel-plugin-transform-typescript-metadata
  • Creating a .babelrc file

Create a .babelrc file at the root directory of your app and enable the support for decorators. Note that the ordering of the plugins matter in this case. This has been mentioned in this particular GitHub issue on Next.js repository.

{
"presets": ["next/babel"],
"plugins": [
"babel-plugin-transform-typescript-metadata",
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
  • Modifying webpack configuration in next.config.js

You also need to enable top-level await in your webpack config, which should be done in your next.config.js

module.exports = {
reactStrictMode: true,
webpack: function (config, options) {
config.experiments = { topLevelAwait: true }
return config;
}
}

After these steps, you should be able to reach the GraphQL API endpoint at http://localhost:3000/api/graphql after running yarn dev, and you should be able to see the GraphQL playground and run queries it.

Functioning GraphQL API endpoint

Practical Example

Let’s extend our endpoint further by using a real world example of user registration and see if it can withstand a real world use-case.

Setting up a database

Let’s setup a postgres database locally. I will be using the commands listed in this medium.com article.

sudo -u postgres psql
create database nextjs_graphql;
create user nextjs with encrypted password 'nextjs';
grant all privileges on database nextjs_graphql to nextjs;

With the database created, you can now create a database connection. Let’s create a file db.ts in /lib/serverless/utils/ folder. I will be referring to this article which explains how to properly create a database connection that doesn’t clash with Next.js’ Hot Module Reloading.

Let’s not forget to install the necessary packages:

yarn add typeorm pg

Note that we will create the User.ts file shortly.

And now, you can modify the index.ts file under /pages/api/graphql/ . You can pass the database connection object to your resolvers through the context . However, we will not be using it in this article, but it’s left here to serve as an example.

Note that we will be creating the UserResolver.ts file shortly.

Let’s create a file User.ts under /lib/serverless/entities/ . We will be using a lot of decorators in this one.

Let’s see what’s going on here:

  • First, we extend from the BaseEntity class of typeorm . This allows us to readily call methods like user.create() or user.find() without requiring to obtain a connection object or a repository.
  • We use the decorators from typeorm to convert the class into a database entity. We use the @Entity and @Column decorators for this.
  • Next, we need to be able to specify that this class will also be used as a GraphQL object type, which is why we are also using the decorators from type-graphql , namely @ObjectType and @Field decorators.

Finally, we need to write the resolver for the User entity, which we will do in the UserResolver.ts file under /lib/serverless/graphql/resolvers/ .

We have a mutation registerUser and a query getUser . They take their corresponding input parameters to save a user to the database and return a user from the database, if any, respectively.

Just to save some time, I will not be hashing the password before saving it to the database, but it is recommended that you hash any sensitive information before storing it in a database.

And with that, we are done setting up. This is the final folder structure of the project after all the modifications above:

.
├── lib
│ └── serverless
│ ├── entities
│ │ └── User.ts
│ ├── graphql
│ │ └── resolvers
│ │ ├── HelloWorldResolver.ts
│ │ └── UserResolver.ts
│ └── utils
│ └── db.ts
├── next.config.js
├── next-env.d.ts
├── package.json
├── pages
│ ├── api
│ │ └── graphql
│ │ └── index.ts
│ └── _app.tsx
├── public
│ ├── favicon.ico
│ └── vercel.svg
├── README.md
├── tsconfig.json
└── yarn.lock

You can hop onto your GraphQL playground at http://localhost:3000/api/graphqland verify that the queries and mutations are working. I have also tested this in production environments using yarn build && yarn start to verify that nothing breaks in production.

The entire code for the article can be found in this repository. Let me know your thoughts and feedback.

Go Beyond!

--

--

Mahieyin Rahmun

Hi, I am an aspiring Computer Science graduate from Bangladesh who takes interest in Web Development, ML and Automation.