Node.js + MongoDB: Creating a GraphQL API

Sharukhan PatanSharukhan Patan
5 min read

Not too long ago, I was setting up a basic GraphQL server with Node.js. It worked — queries returned responses, and the structure felt clean. But there was one thing missing: real data. Static arrays and hardcoded responses are great for learning, but eventually, I needed my server to talk to an actual database. That’s when MongoDB came into the picture.

In this post, I’m walking through how I connected a GraphQL API to MongoDB using Mongoose. It’s not complicated — once it clicks, it actually feels surprisingly intuitive. The goal here is to build a small but functional API for managing books. Something simple, but real enough to be useful.

Why MongoDB and GraphQL Make Sense Together

One thing I noticed early on with GraphQL is how well it mirrors the shape of the data you want. And MongoDB? It stores data in a format that already looks a lot like the shape of your queries. That’s a big deal — I didn’t have to fight the database to make GraphQL work. The two fit together naturally.

What We're Building

Here’s what this project covers:

  • A GraphQL API that can:

    • Fetch all books

    • Fetch a single book by ID

    • Add a new book

    • Update a book

    • Delete a book

  • MongoDB as the backend database

  • Mongoose to model and work with Mongo documents

By the end, it’ll be a full CRUD API you can query directly in GraphiQL or hook into a frontend app.

Setting Up the Project

I started with a blank Node.js project. After initializing, I installed the usual suspects:

npm init -y
npm install express express-graphql graphql mongoose dotenv

And just like that, the foundation was ready.

Connecting to MongoDB

I chose to use a local MongoDB instance, but MongoDB Atlas works too. I saved my connection string in a .env file to keep things clean:

MONGO_URI=mongodb://localhost:27017/graphql_books

Then I created a simple connection file using Mongoose:

const mongoose = require('mongoose');
require('dotenv').config();

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI);
    console.log('MongoDB connected');
  } catch (error) {
    console.error('Connection failed:', error.message);
    process.exit(1);
  }
};

module.exports = connectDB;

When this ran without error, I knew I was ready to define models.

Creating the Book Model

With the database connected, it was time to define the structure for my book data. Here’s what I added in models/Book.js:

const mongoose = require('mongoose');

const BookSchema = new mongoose.Schema({
  title: String,
  author: String,
  publishedYear: Number
});

module.exports = mongoose.model('Book', BookSchema);

Setting Up the GraphQL Schema

This is where the magic happens — translating Mongo data into something GraphQL can query and mutate.

I created a file called schema.js and started with the BookType, defining the fields my GraphQL API would expose:

const {
  GraphQLObjectType,
  GraphQLString,
  GraphQLInt,
  GraphQLID,
  GraphQLSchema,
  GraphQLList,
  GraphQLNonNull
} = require('graphql');

const Book = require('../models/Book');

const BookType = new GraphQLObjectType({
  name: 'Book',
  fields: () => ({
    id: { type: GraphQLID },
    title: { type: GraphQLString },
    author: { type: GraphQLString },
    publishedYear: { type: GraphQLInt }
  })
});

Then I added queries for fetching all books or just one by ID, and mutations for adding, updating, and deleting a book:

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    books: {
      type: new GraphQLList(BookType),
      resolve() {
        return Book.find();
      }
    },
    book: {
      type: BookType,
      args: { id: { type: GraphQLID } },
      resolve(_, args) {
        return Book.findById(args.id);
      }
    }
  }
});

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    addBook: {
      type: BookType,
      args: {
        title: { type: new GraphQLNonNull(GraphQLString) },
        author: { type: new GraphQLNonNull(GraphQLString) },
        publishedYear: { type: GraphQLInt }
      },
      resolve(_, args) {
        const book = new Book(args);
        return book.save();
      }
    },
    deleteBook: {
      type: BookType,
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(_, args) {
        return Book.findByIdAndDelete(args.id);
      }
    },
    updateBook: {
      type: BookType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLID) },
        title: { type: GraphQLString },
        author: { type: GraphQLString },
        publishedYear: { type: GraphQLInt }
      },
      resolve(_, args) {
        return Book.findByIdAndUpdate(args.id, args, { new: true });
      }
    }
  }
});

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation
});

Spinning Up the Server

Everything came together in index.js:

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const schema = require('./graphql/schema');
const connectDB = require('./db');
require('dotenv').config();

const app = express();
const PORT = 4000;

connectDB();

app.use('/graphql', graphqlHTTP({
  schema,
  graphiql: true
}));

app.listen(PORT, () => {
  console.log(`Server is running at http://localhost:${PORT}/graphql`);
});

Testing the API

With the server running, I opened GraphiQL and tried adding a book:

mutation {
  addBook(title: "GraphQL for Beginners", author: "Jane Doe", publishedYear: 2022) {
    id
    title
  }
}

Then fetched all books:

Node.js + MongoDB: Creating a GraphQL API

{
  books {
    id
    title
    author
    publishedYear
  }
}

Node.js + MongoDB: Creating a GraphQL API

Seeing real data come through from MongoDB was a rewarding moment — much more satisfying than hardcoded mock data.

Wrap-Up

This simple project took me from a static GraphQL server to a live, data-driven API using MongoDB. What stood out was how natural the flow felt — defining types, connecting them to Mongoose models, and instantly being able to query or mutate that data.

Next up, I’m planning to refine this even more by adding authentication, modularizing the schema and resolvers, and maybe even exploring pagination and filters. But for now, I’ve got a working GraphQL API backed by MongoDB — and that’s a solid foundation to build on.

0
Subscribe to my newsletter

Read articles from Sharukhan Patan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sharukhan Patan
Sharukhan Patan