Introduction
GraphQL has revolutionised API development by offering a flexible and efficient way to query data. Unlike REST APIs, where you often fetch unnecessary data or make multiple requests, GraphQL allows clients to request exactly what they need in a single request.
In this tutorial, we’ll build a GraphQL API from scratch using Node.js and Apollo Server. Our API will handle users and posts, providing a structured example of queries and mutations.
What We’ll Learn
- Setting up a Node.js GraphQL server with Apollo Server
- Defining a GraphQL schema (types, queries, and mutations)
- Creating resolvers to handle requests
- Connecting to MongoDB using Mongoose
- Implementing JWT authentication
What is Apollo and why are we using it here? Apollo is a popular GraphQL server and client library. We use Apollo Server in Node.js to handle GraphQL queries, mutations, and subscriptions efficiently, providing features like schema stitching, caching, and middleware integration.
Let’s get started! 🚀
1️⃣ Setting Up the Project
Install Node.js & Initialise the Project
Ensure you have Node.js installed. Then, create a new project:
mkdir graphql-blog-api && cd graphql-blog-api
npm init -y
Install Required Dependencies
We’ll use Apollo Server (for GraphQL), Express (for handling requests), and Mongoose (for MongoDB).
npm install express apollo-server-express graphql mongoose jsonwebtoken bcryptjs dotenv cors
Why are we installing jsonwebtoken, bcryptjs, and cors?
jsonwebtoken — Used for authentication via JWT tokens.
bcryptjs — Used for securely hashing and comparing passwords.
cors — Enables Cross-Origin Resource Sharing (CORS), allowing the API to be accessed from different domains.
For development, install Nodemon:
npm install --save-dev nodemon
Update package.json to enable auto-reloading:
"scripts": {
"dev": "nodemon index.js"
}
2️⃣ Defining the GraphQL Schema
GraphQL relies on a schema to define types and queries. Let’s create a schema.js file:
const { gql } = require("apollo-server-express");
// type defines a GraphQL object structure, and typedefs (short for type definitions) collectively define the GraphQL schema, including queries and mutations.
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
password: String!
}
type Post {
id: ID!
title: String!
content: String!
authorId: ID!
author: User
}
type Query {
users: [User]
posts: [Post]
}
type Mutation {
register(name: String!, email: String!, password: String!): User
login(email: String!, password: String!): String
createPost(title: String!, content: String!): Post
}
`;
module.exports = typeDefs;
What is gql?
gql (GraphQL Tag) is a template literal function from Apollo that parses GraphQL schema definitions into an AST (Abstract Syntax Tree).
What does ! mean in types?
The ! denotes a non-nullable field, meaning the field must have a value and cannot be null.
3️⃣ Setting Up Resolvers
Resolvers define how GraphQL responds to queries and mutations. Create resolvers.js:
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/User");
const Post = require("../models/Post");
const resolvers = {
Query: {
users: async () => await User.find(),
posts: async () => await Post.find().populate("author"),
},
Mutation: {
register: async (_, { name, email, password }) => {
const hashedPassword = await bcrypt.hash(password, 10);
return await User.create({ name, email, password: hashedPassword });
},
login: async (_, { email, password }) => {
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password))) {
throw new Error("Invalid credentials");
}
return jwt.sign({ ...user.toObject() }, process.env.JWT_SECRET, { expiresIn: "1h" });
},
createPost: async (_, { title, content }, { user }) => {
if (!user) throw new Error("Authentication required");
const post = Post({ title, content, authorId: user._id });
await post.save();
return await Post.findById(post._id).populate('author');
},
},
};
module.exports = resolvers;
4️⃣ Connecting to MongoDB
Create a .env
file to store the MongoDB URI:
MONGO_URI=mongodb://localhost:27017/graphql_blog
JWT_SECRET="secretKey"
Now, create models/User.js:
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: String,
email: String,
password: String,
});
module.exports = mongoose.model("User", userSchema);
And models/Post.js:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
title: String,
content: String,
authorId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
});
postSchema.virtual('author', {
localField: 'authorId',
foreignField: '_id',
ref: 'User',
justOne: true
})
module.exports = mongoose.model("Post", postSchema);
5️⃣ Running & Testing the API
Create index.js to set up Apollo Server:
const express = require('express');
require('dotenv').config();
const { ApolloServer } = require('apollo-server-express');
const mongoose = require('mongoose');
const typeDefs = require("./src/graphql/schema");
const resolvers = require("./src/graphql/resolvers");
const app = express();
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('connected to mongodb'))
.catch((err) => console.log('Not able to connect to mongodb', err));
const jwt = require('jsonwebtoken');
const context = ({ req }) => {
const authHeader = req.headers.authorization || '';
const token = authHeader.split(' ')[1]; // Extract token from "Bearer <token>"
if (token) {
try {
const user = jwt.verify(token, process.env.JWT_SECRET); // Decode user
return { user }; // Attach user to context
} catch (err) {
console.log('Invalid Token');
}
}
return {}; // No user attached if token is missing/invalid
};
const server = new ApolloServer({
typeDefs,
resolvers,
context
});
async function startServer() {
await server.start();
server.applyMiddleware({ app });
// Start Express Server
app.listen(4000, () => {
console.log(`Server running at http://localhost:4000${server.graphqlPath}`);
});
}
startServer();
Run the API:
npm run dev
Test queries in GraphQL Playground at http://localhost:4000/graphql
.
Conclusion
🎉 You’ve built a GraphQL API from scratch using Node.js, Apollo Server, and MongoDB. You learned:
✅ How to define a GraphQL schema
✅ How to set up queries and mutations
✅ How to connect to MongoDB
✅ How to authenticate users
Next Steps: Add subscriptions for real-time updates or build a React frontend!
🚀 What would you like to learn next? Drop a comment below!
Top comments (0)