If you have worked with REST APIs before, you know the pain of over-fetching or under-fetching data. You call one endpoint and get a massive JSON object when all you needed was a username. GraphQL solves that. It gives clients the power to ask for exactly what they need. In 2026, GraphQL is more mature than ever, with excellent tooling and a vibrant community. This tutorial will walk you through building your first GraphQL server from scratch. You will learn the core concepts, write a schema, create resolvers, and avoid the most common beginner mistakes.
By the end of this guide, you will have a working GraphQL server built with Node.js and Apollo Server. You will understand how to define a schema using the Schema Definition Language (SDL), write resolvers that fetch data, and handle queries and mutations. You will also learn common pitfalls like the N+1 problem and how to avoid them. This is a practical, hands-on roadmap for your first GraphQL project.
What You Will Need Before Starting
This tutorial assumes you have a basic understanding of JavaScript and Node.js. You should have Node.js version 18 or later installed on your machine. If you need a refresher on asynchronous programming, check out our guide on
You will also need a code editor. VS Code is a popular choice, but any editor works. The tools we use are free and open source, which makes this a great project for your learning portfolio. If you are interested in contributing to open source projects after this, our article on https://techpresentations.org/getting-started-with-contributing-to-popular-open-source-projects/ can help you take the next step.
Setting Up Your Project
Let us start with a clean slate. Create a new directory for your project and initialize it with a package manager.
- Create a new directory and navigate into it.
- Run
npm init -yto generate apackage.jsonfile. - Install the core dependencies:
npm install @apollo/server graphql graphql-tag. - Install a dev dependency for running your server:
npm install --save-dev nodemon.
Your package.json should look similar to this after installation:
{
"name": "my-first-graphql-server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"dependencies": {
"@apollo/server": "^4.11.0",
"graphql": "^16.9.0",
"graphql-tag": "^2.12.6"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}
The @apollo/server package gives us a production-ready GraphQL server. We use graphql for parsing and executing queries. The graphql-tag library helps us write our schema using template literals, which keeps the code readable.
Writing Your First Schema
The schema is the heart of any GraphQL server. It defines the types, queries, and mutations that your API supports. Think of it as a contract between the client and the server. In 2026, the Schema Definition Language (SDL) is the standard way to write schemas.
Create a file called index.js and add the following code:
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
const gql = require('graphql-tag');
const typeDefs = gql`
type Book {
id: ID!
title: String!
author: String!
publishedYear: Int
}
type Query {
books: [Book!]!
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!, publishedYear: Int): Book!
}
`;
Notice the exclamation marks. They mean the field is non-nullable. The [Book!]! syntax means we return an array of Books, and neither the array nor the items inside can be null. This is a strong guarantee for your clients.
Creating Resolvers
Resolvers are functions that fetch the data for each field in your schema. For a simple in-memory data store, we can use an array. In a real application, you would connect to a database. Our guide on https://techpresentations.org/mastering-nosql-databases-for-modern-web-applications/ can help you choose the right database for your next project.
Add the following code below your type definitions:
const books = [
{ id: '1', title: 'The Pragmatic Programmer', author: 'Andy Hunt', publishedYear: 1999 },
{ id: '2', title: 'Clean Code', author: 'Robert C. Martin', publishedYear: 2008 },
];
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find(book => book.id === args.id),
},
Mutation: {
addBook: (parent, args) => {
const newBook = {
id: String(books.length + 1),
title: args.title,
author: args.author,
publishedYear: args.publishedYear,
};
books.push(newBook);
return newBook;
},
},
};
The resolver functions receive four arguments: parent, args, context, and info. For now, we only need args, which contains the parameters passed to the query or mutation. The parent argument becomes important when you have nested types, but we will keep things simple for this tutorial.
Connecting Everything and Starting the Server
Now we need to create the Apollo Server instance and start it. Add this final block of code:
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
Run your server with npm run dev. Open your browser and go to http://localhost:4000. You should see Apollo Sandbox, a built-in IDE where you can test your queries. Try the following query:
query {
books {
title
author
}
}
You should get back the list of books. Now try a mutation:
mutation {
addBook(title: "Design Patterns", author: "Gang of Four", publishedYear: 1994) {
id
title
}
}
If everything works, you have just built your first GraphQL server.
Common Beginner Mistakes and How to Avoid Them
Even with a simple setup, beginners often run into the same issues. The table below outlines the most frequent mistakes and their solutions.
| Mistake | What happens | How to fix it |
|---|---|---|
| Forgetting to restart the server after schema changes | The server returns stale data or errors | Use nodemon to auto-restart on file changes |
| Using wrong field names in queries | The server returns null for those fields | Double-check your schema spelling and casing |
| Not handling null values in resolvers | The server throws an internal error | Always check for null before accessing properties |
| Writing resolver functions that return arrays instead of objects | The server complains about return types | Make sure your resolvers return the exact shape defined in the schema |
| Ignoring the N+1 problem | The server makes too many database calls | Use data loaders or batch your queries |
The N+1 problem deserves special attention. It happens when you have a list of items, and for each item, your resolver makes a separate database call. For example, if you query 100 books and each book needs to fetch its author separately, that is 101 queries. In a REST API, you might not notice this, but in GraphQL, it can slow down your server dramatically. One way to fix it is to use a tool like DataLoader, which batches and caches requests.
Expert advice: Always think about the data access patterns when designing your resolvers. A single resolver that fetches all the data in one go is often better than multiple small resolvers that each make a database call. Profile your server early to catch performance issues.
A Simple Project Structure for Growth
As your project grows, you should organize your code into separate files. Here is a suggested structure:
schema/contains your type definitions in.graphqlfiles.resolvers/contains your resolver functions, split by domain (e.g.,book.js,author.js).data/contains your data access layer (database models, API clients).index.jsis the entry point that ties everything together.
This separation makes your code easier to test and maintain. If you plan to build a larger application, our article on https://techpresentations.org/master-modern-web-frameworks-for-faster-development/ can help you choose a framework that supports this structure.
Next Steps After Your First Server
You now have a working GraphQL server. The next step is to connect it to a real database. PostgreSQL and MongoDB are popular choices. You can also add authentication, pagination, and subscriptions. Subscriptions allow clients to receive real-time updates when data changes, which is useful for chat applications or live dashboards.
If you want to deepen your understanding of API design, our guide on https://techpresentations.org/how-to-use-graphql-to-streamline-your-api-development-in-2026/ covers advanced topics like federation and error handling.
Your GraphQL Journey Starts Now
Building your first GraphQL server in 2026 is a rewarding experience. You have learned how to set up a project, write a schema, create resolvers, and avoid common pitfalls. The best way to solidify this knowledge is to build something real. Try creating a server for a personal project, like a book collection or a movie watchlist. Experiment with mutations, add validation, and connect it to a database. Every mistake you make will teach you something valuable. The GraphQL ecosystem is mature and welcoming, so do not hesitate to ask for help in community forums. Your portfolio will thank you.