Building a NextJS backend with GraphQL and MongoDB
This article covers how to setup a nextjs application with both the client and backend. Our backend will be a graphql server integrated to MongoDB, which we’ll access on the client side as graphql apis. Before we start, let’s review the different stack used on this project
NextJS: serving as the official framework for ReactJS. For this project, we’ll leverage nextjs version 14, representing one of the latest releases in the framework
GraphQL: the querying language for our API, implemented using Apollo-GraphQL. Our backend will operate through a GraphQL server, managing our database’s read and write operations efficiently.
MongoDB: the data storage solution. We’ll utilize Mongoose, a robust MongoDB object modeling tool designed to streamline interactions with the database.
Note: this project assumes you have Node.js and npm installed on your system and a basic understanding of Next.js, GraphQL, and Apollo Server concepts.
Find the github repository here -> https://github.com/Stephany-Doris/NextJS-GraphQL-MongoDB-app
Project definition: We’ll create a backend api to support crud operations from a single page application, this includes create users with their details, fetch and display a list of users and also update and delete a user.
Project Setup
01. Initial setup using nextjs. You can update the app-name to anything you’d prefer.
npx create-next-app@latest nextjs-user-app
02. Install graphql and apollo packages
npm i graphql @apollo/client @apollo/server
03. Install Mongoose
npm i mongoose
04. Install apollo-datasource-mongodb. Official apollo data source for MongoDB. This package uses DataLoader for batching and per-request memoization caching.
npm i apollo-datasource-mongodb
05. Install as-integrations/next: this is an Apollo Server integration for use with Next.js that enables us to run apollo-server on nextjs api routes.
npm i @as-integrations/next
Note: you can clone the project on this url (https://github.com/Stephany-Doris/NextJS-GraphQL-MongoDB-app) and checkout the initial-setup branch to get the UI and packages installed during setup. You will need to run npm install after cloning and checking out to that branch.
Apollo graphql server initialization
Before we setup our apollo server let’s define a couple of things:
typeDefs: this is the schema or structure of the data accessible via our graphql api.
Resolvers: this determines how data requests for specific queries or mutations are resolved and returned.
We’ll begin by creating a route in our nextjs app
directory. Create a new folder api
and in that folder create another folder called graphql
this will resolve to /api/graphql
route on our browser. In the /graphql
directory we’ll add the following route.ts
, resolver.ts
and schema.ts
Route.ts
In this file, we’re setting up Apollo Server to work with Next.js using the @as-integrations/next
package. Here’s a breakdown of what’s happening in the code snippet below:
startServerAndCreateNextHandler
: provided by the@as-integrations/next
package, this is responsible for creating a handler that integrates Apollo Server with Next.js, accepting the Apollo Server instance and a configuration object as props.ApolloServer
: Imported from@apollo/server
, this is used to instantiate an Apollo Server, passing in the definedresolvers
andtypeDefs
NextRequest
: Imported fromnext/server
, representing a Next.js server request.typeDefs
andresolvers
: These contain the GraphQL schema (typeDefs
) and resolver functions (resolvers
) for the GraphQL API.- Handler Functions: Two functions (
GET
andPOST
) are exported, each accepting aNextRequest
. These functions are designed to handle incoming HTTP requests (GET and POST) received by the Next.js server. Both functions utilize thehandler
created earlier, which encapsulates the logic for processing GraphQL requests through Apollo Server within Next.js context.
Schema.ts
Represents the schema definition for our GraphQL API supporting queries and mutations using specific input parameters.
Resolvers.ts
This file contains the functions that correspond to the defined queries and mutations in the schema. These functions will execute the necessary logic to retrieve or manipulate data from MongoDB and return the results as requested by the client’s queries or mutations. For now the code snippet below has not yet been integrated.
Integrate MongoDB
If you’re unfamiliar with how to create a mongodb database, visit the link below for a quick guideline. I find the easiest way to do this is using the mongodb atlas UI -> https://www.mongodb.com/basics/create-database
Once you’ve created your free cluster and created a database called user-db with a collection users
we’ll proceed to connect our application to our database.
In this section, we’ll use your cluster’s connection string from the Atlas UI and connect the backend to your cluster.
For a detailed guideline on how to get your connection string, check out the link -> https://www.mongodb.com/docs/atlas/tutorial/connect-to-your-cluster/
We’ll update the route.ts
file with the code snippet below to connect to the mongodb cluster using the connection string. Once you’ve saved your file restart your server npm run dev
and navigate to the /api/graphql
route on your browser, on your command line you should see the 🎉 connected to database successfully success message.
MongoDB Models:
To synchronize our database with our GraphQL schema, we’ll create a corresponding model, specifically for users. This model will mirror the fields outlined in our GraphQL schema.
MongoDB Data Source:
A MongoDB data source facilitates the interaction between our application and the MongoDB database. Using Mongoose’s create
and find
methods, we'll implement functionalities to create and retrieve all users respectively.
Let’s delve into the code.
Update our resolvers to return data from the mongodb database
Now that we’ve set up our models and the logic to retrieve and manipulate data from our database, we’ll update our resolvers to use this functions when querying data or mutations.
Note: update the following in your
route.ts
file, in order to access datasource via context.
import Users from "./datasources";
import UserModel from "./models";
const handler = startServerAndCreateNextHandler<NextRequest>(server, {
context: async (req, res) => ({
req,
res,
dataSources: {
users: new Users({ modelOrCollection: UserModel }),
},
}),
});
And with that we are able to create users and query our users from our database. You should be able to test this out on the playground on route localhost:3000/api/graphql
If you’d like to review the methods to update and delete a user, checkout the main branch and test the same on your playground url. I’ve also added the client integration that you can interact with on localhost:3000
Conclusion
We’ve laid the groundwork for our Next.js application incorporating both frontend and backend elements. By setting up Next.js, integrating GraphQL with Apollo Server, and connecting to MongoDB using Mongoose, we’ve established a foundation for handling user-related operations within our application.
Should you wish to build on this, you can always expand the GraphQL schema, implement additional resolvers, and enhance database interactions to support additional functionalities.
I hope you learnt a lot. Feel free to connect with me on LinkedIn and shoot me a dm if you have any questions. Am also always willing to collaborate on anything GraphQL :)
Additional resources
Sharing some of the issues I came cross, in case you experience the same and need a reference point:
- https://github.com/GraphQLGuide/apollo-datasource-mongodb/issues/49
- https://stackoverflow.com/questions/19051041/cannot-overwrite-model-once-compiled-mongoose
- https://nesin.io/blog/fix-mongoose-cannot-overwrite-model-once-compiled-error