GraphQLNext.js
Last updated at 2023-08-30

Create an Apollo Server GraphQL in Serverless Typescript Next.js

ClickUp
Note
AI Status
Last Edit By
Last edited time
Aug 30, 2023 07:42 AM
Metatag
Slug
graphql-nextjs-api-apollo-tutorial-typescript
Writer
Published
Published
Date
Aug 25, 2023
Category
GraphQL
Next.js
So, you have a Next.js TypeScript project that needs to expose an API for your clients to consume. You don't want to provide a REST API for some reason. Then, welcome to GraphQL!
🥸
If you are a seasoned developer, you can jump to the code if you want.

Can I Serve GraphQL Endpoint in Next.js?

Expose GraphQL using Next.js API Router

The Next.js API router can provide a good start to building a GraphQL API. To build GraphQL API using Apollo Server in Next.js, you can install @apollo/server, @as-integrations/next, and graphql-tag library and provide schema and type definitions. Wire up those tools, and you can start consuming the API right away using the single /api/graphql endpoint.
In this tutorial article, you will learn how to build a Typescript based Apollo GraphQL server that acts as your API endpoint using the Next.js API Router.

Host GraphQL using Vercel Edge Functions

The best thing about Next.js is that your GraphQL endpoint will be served from Edge Functions by Vercel if you host the application on their platform.
You will learn quickly in this article. So read on!

What Will You Learn in this Tutorial?

In this article, you will learn how to build a GraphQL API in your Next.js project that clients can consume.

What Are You Building?

Nowadays, people still read blog articles, just like you're doing now. There are many reasons for this, of course.
A good blog provides mechanisms for the client app to fetch all blogs, get blog details, query blogs by title, and favorite a blog post. Like Mozzlog 😁.
The post contains a title, cover image, categories, last modification timestamp, number of reads, and content.

List of Topics That You Will Cover

You will cover the following topics:
  1. Prerequisites
  1. Setting up the schema
  1. Fake Initial Data
  1. Preparing Resolvers
  1. Setting up the Server
  1. Exposing GraphQL to the World
  1. Use cURL to Test the Live GraphQL Endpoint
  1. Performing Mutations
  1. Sandbox Test
Yes, you will end up with a sandbox test so that you can test the API via the browser without spinning up another GraphQL client application.
😀
A Sandbox Test makes testing GraphQL endpoints easier. Wouldn’t you want to help your client try your endpoint? It can make adoption faster.
Let's get to work. Are you ready to build a blog's GraphQL API?

Prerequisites

This article assumes that you are seasoned or have experience with Next.js. Therefore, you won't need to read about how to set up a Next.js project.
Of course, you'll need a running Next.js project. Just ensure that you have TypeScript set up because you will use the language to build the GraphQL API.
This project will use Apollo GraphQL. To install the dependencies, you can use this command:
npm install @apollo/server @as-integrations/next graphql-tag
You will use @apollo/server to serve your GraphQL resolver and schema, while @as-integrations/next will provide functions to serve your ApolloServer in Next.js. The @graphql/tag role is to write the schema of your GraphQL query.

Setting Up The Schema

First, you need to build a schema for the Blog GraphQL API. The schema should address the questions you want to answer with the API.
  1. Clients want to see a list of blog posts.
  1. Clients want to query a blog by its title.
  1. Clients want to mark a post as a favorite.
  1. Clients want to view the details of a blog post.
You need to define the schema and resolvers in variables. You can start by defining the Post type.

Post Type GraphQL Schema

type Post { id: ID! slug: String! title: String! categories: [String!]! cover: String lastUpdatedAt: Float! isFavorite: Bool content: String! }
The fields you can include in your Post type are id, slug, title, categories, cover, lastUpdatedAt, and content.

Query Type GraphQL Schema

type Query { getPost(id: ID!): Post getPostsByTitle(query: String): [Post!]! getAllPosts: [Post!]! }
The fields you can include in your Query type are getPost, which accepts the id parameter, and getAllPosts, which returns all Post objects.
That's it. Creating a GraphQL type is easy. Now, let's put those types in a variable called schemaDefinitions.

Create schemaDefinitions

import { gql } from 'graphql-tag'; const schemaDefinitions: DocumentNode = gql` type Post { id: ID! slug: String! title: String! categories: [String!]! cover: String date: String! published: Boolean! lastEditedAt: Int! isFavorite: Bool content: String! } type Query { getPost(id: ID!): Post getPostsByTitle(query: String): [Post!]! getAllPosts: [Post!]! } `;
In the above code, the gql function will convert our string into a DocumentNode instance.
You can omit the explicit type annotation from schemaDefinitions if you want. TypeScript will infer the type from the gql return type.

Fake Initial Data

In this project, you won't be reading from a database. That will be covered in another article, where you'll read from databases like Postgres, MongoDB, or Redis.
You also won't be reading from a .json file, as that would be another topic involving serialization, deserialization, and saving to a file in TypeScript. However, if you need that, you can refer to this article.
To simplify the tutorial, you'll create a constant variable that stores your list of posts in memory.
🥸
Yes, you and I like constant variables. Make everything much easier.
Let's generate our posts using the code below:
const posts: Post[] = [ { id: "1", slug: "golang-files-tutorial-beginner", title: "Quick Golang Files Tutorial for Beginner", cover: null, categories: [ "Golang", "Snippet" ], lastEditedAt: 1692584880000, isFavorite: null, content: "golang content" }, { id: "2", slug: "restapi-requests-in-python", title: "Quick REST API Calls using the Requests Library in Python", cover: null, categories: [ "Python", "RestAPI" ], lastEditedAt: 1692656820000, isFavorite: null, content: "python content" }, { id: "3", slug: "react-pdf-module-parse-failed-next-js", title: "How to Resolve the react-pdf Issue: \"Module parse failed: Unexpected character '�' (1:0)\" in Next.js", cover: null, categories: [ "Next.js" ], lastEditedAt: 1692612960000, isFavorite: null, content: "react-pdf content" } ];

Preparing Query Resolvers

In GraphQL, the function that runs when you query data is called a resolver.
To prepare resolvers, you can use this code:
const resolvers = { Query: { getPost: async (parent: any, args: { id: string }) => { const { id } = args; return posts.find((post: Post) => post.id === id) }, getPostsByTitle: async (parent: any, args: { query: string }) => { const { query } = args; return posts.find((post: Post) => post.title.includes(query)) }, getAllPosts: async () => { return posts }, }, };
In the above code, you'll only need a resolver for the Query as it is the schema that exposes functions to get the list of posts.
  1. In getPost, you match posts by comparing their id to the given argument and returning the matched one.
  1. For getPostsByTitle, you find posts with title containing the provided query and return them.
  1. getAllPosts simply returns all post content without any filtering.

Setting Up GraphQL Server

As a prerequisite, you've installed Apollo GraphQL to act as your GraphQL engine. To set up Apollo Server, you can use this code:
import { ApolloServer } from '@apollo/server'; const resolvers = ... // Your resolvers const schemaDefinitions = ... // Your schema definitions const server = new ApolloServer({ resolvers, schemaDefinitions, });

Exposing GraphQL Next.js Endpoint

GraphQL is a mechanism that allows you to define how the response should look, rather than the server. It can save you a lot of data when you need a small amount of information displayed.
To transport your GraphQL query, you can leverage HTTP POST.
In this project, you won't set up the transport yourself. You'll use the @as-integrations/next package you installed earlier.
import { startServerAndCreateNextHandler } from '@as-integrations/next'; const server = ... // Your ApolloServer export default startServerAndCreateNextHandler(server);
Now that everything is in place, you can run the Next.js project using:
npm run dev
When your application is accessible through localhost:3000, you can access the GraphQL endpoint at http://localhost:3000/api/graphql.

Use cURL to Test the Live GraphQL Endpoint

Get All Posts

Now that everything is in place, you can test the GraphQL endpoint. Use this cURL command to get a list of blog posts:
curl -X POST \ -H "Content-Type: application/json" \ -d '{ "query": "query { getAllPosts { id slug title categories } }" }' \ http://localhost:3000/api/graphql
That will give you response like this:
{ "data": { "getAllPosts": [ { "id": "1", "slug": "golang-files-tutorial-beginner", "title": "Quick Golang Files Tutorial for Beginner", "categories": [ "Golang", "Snippet" ] }, { "id": "2", "slug": "restapi-requests-in-python", "title": "Quick REST API Calls using the Requests Library in Python", "categories": [ "Python", "RestAPI" ] }, { "id": "3", "slug": "react-pdf-module-parse-failed-next-js", "title": "How to Resolve the react-pdf Issue: \"Module parse failed: Unexpected character '�' (1:0)\" in Next.js", "categories": [ "Next.js" ] } } }

Get Specific Post by ID

To retrieve a specific Post, you can use getPost from the Query Schema. You need to pass the Post.id to get the information you want.
Use this cURL command to get a specific post:
curl -X POST \ -H "Content-Type: application/json" \ -d '{ "query": "query { getPost(id: \"1\") { id slug title categories cover content } }" }' \ http://localhost:3000/api/graphql
It will result in this response:
{ "data": { "getPost": { "id": "1", "slug": "golang-files-tutorial-beginner", "title": "Quick Golang Files Tutorial for Beginner", "categories": [ "Golang", "Snippet" ], "cover": null, "content": "golang content" } } }
Isn't that awesome?
But how do you update data using GraphQL? Let's say you want to favorite a post. In that case, let's introduce mutations.

Performing Mutations

When you want to favorite a post, you must update the database via mutation.
GraphQL provides a specific schema to explicitly tell the server that a client wants to perform a mutation.

Mutation Schema

To enable clients to perform mutations, you should update the schemaDefinitions to include the mutation.
Add this to your const schemaDefinitions above:
type Mutation { addPostToFavorite(id: ID!): Post }
In this schema, you specify that when you add a post to your favorites, the server should return a Post object.
Is it too much to return a Post object? Well, yes but actually no. 😀
You will define what the client wants during the mutation. But before making a mutation, you need to provide a resolver.

Mutation Resolver

After you add mutation to the schema, you write the resolver like this:
const resolvers = { Query: { // ... Your existing queries ... }, Mutation: { addPostToFavorite: async (parent: any, args: { id: string }) => { const { id } = args; const post = posts.find((post: Post) => post.id === id) if (post) { post.isFavorite = true return post; } else { throw new Error('Post not found'); } }, }, };

Mutation Query

This is the query that clients should send to the GraphQL endpoint when they want to perform mutation.
mutation AddPostToFavorite { addPostToFavorite(id: ID!) { id title isFavorite } }
And the cURL command should be like this:
curl -X POST \ -H "Content-Type: application/json" \ -d '{ "query": "mutation AddPostToFavorite { addPostToFavorite(id: \"1\") { id title isFavorite } }" }' \ http://localhost:3000/api/graphql
That will result in something like this:
{ "data": { "addPostToFavorite": { "id": "1", "title": "Quick Golang Files Tutorial for Beginner", "isFavorite": true } } }

Providing a Sandbox Test for Your GraphQL Endpoint

If you're familiar with Swagger, this sandbox works in a similar way. It allows you to create a web-based user interface for exploring the GraphQL endpoint just through your browser!
This is going to be fun!
You can provide an awesome experience for consumers of your GraphQL endpoint to explore the blog post API. You can use Apollo Sandbox, which offers the same features as Apollo Explorer.

Sandbox Embedding in the /sandbox Endpoint

You can create a /sandbox endpoint after the GraphQL endpoint. Simply create another API router within the current GraphQL router.
Host some HTML at /sandbox using the embedded Sandbox code provided by Apollo:
import React, { useEffect } from 'react'; const ApolloSandbox: React.FC = () => { const graphQLEndpoint = "http://localhost:3000/api/graphql" useEffect(() => { const sandboxHTML = ` <!DOCTYPE html> <html lang="en"> <body style="margin: 0; overflow-x: hidden; overflow-y: hidden"> <div id="sandbox" style="height:100vh; width:100vw;"></div> <script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js"></script> <script> new window.EmbeddedSandbox({ target: "#sandbox", initialEndpoint: "${graphQLEndpoint}", }); </script> </body> </html> `; const iframe = document.createElement('iframe'); iframe.srcdoc = sandboxHTML; iframe.style.border = 'none'; iframe.style.position = 'fixed'; iframe.style.top = '0'; iframe.style.left = '0'; iframe.style.width = '100%'; iframe.style.height = '100vh'; document.body.appendChild(iframe); return () => { document.body.removeChild(iframe); }; }, []); return null; }; export default ApolloSandbox;
You can now access http://localhost:3000/sandbox.
Using the sandbox, you can explore the schema, make queries, perform mutations from the explorer, and run checks or diffs against your registered schemas.
Access Apollo Explorer from sandbox endpoint
The best thing about the sandbox is that you can save collections of operations in your embedded sandbox to refer back to. The saved collections will persist and show up the next time you visit the /sandbox route. This functionality is stored in your browser's storage.

NPM Package for Embedding Sandbox?

Actually, there are a couple of options you can choose from to embed the sandbox into your site, besides manually creating a component. After all, it's better to use ready-to-use resources, right?
You can use the @apollo/sandbox npm package. Learn more about all the options you have for embedding Sandbox here.

Thanks!

Thank you for reading this article. There is a lot to learn about GraphQL that has yet to be covered in this article. Keep learning!

Discussion (0)

Related Posts