GraphQL 101: From Basics to Advanced


GraphQL is a query language for APIs and a runtime for executing those queries. It provides a complete and understandable description of the data in your API, empowering clients to request only the data they need and nothing more.
Why Use GraphQL?
GraphQL was created to solve common issues with REST APIs, such as over-fetching, under-fetching, and multiple network requests. Unlike REST, which requires multiple API calls to obtain related data, GraphQL can fetch exactly what is needed in a single request.
Key Benefits:
Efficient Data Fetching: Clients can request only the required fields, reducing unnecessary data transfer.
Single Endpoint: Unlike REST APIs, which often have multiple endpoints, GraphQL provides a single endpoint (
/graphql
).Strongly Typed Schema: GraphQL APIs are schema-driven, reducing the chances of errors.
Real-time Updates: GraphQL supports subscriptions, allowing real-time data fetching.
Better Developer Experience: The self-documenting schema makes it easier to understand and use APIs.
GraphQL vs. REST API
GraphQL is often compared to REST, but it offers more flexibility. Here's a comparison:
Feature | GraphQL | REST |
Data Fetching | Fetches only required data | Often over-fetches or under-fetches |
Endpoints | Single endpoint (/graphql ) | Multiple endpoints per resource |
Typing | Strongly typed schema | No enforced schema |
Real-time Support | Built-in subscriptions | Requires WebSockets or polling |
Performance | Less network overhead | Higher overhead with multiple requests |
Example:
Fetching user and their posts:
REST API (Multiple Calls)
GET /user/1
โ Fetch user detailsGET /user/1/posts
โ Fetch posts separately
GraphQL (Single Query)
query {
user(id: "1") {
name
posts {
title
content
}
}
}
This makes GraphQL faster and more efficient.
Setting Up a GraphQL Server
To build a GraphQL server, we need a GraphQL schema and resolvers to handle queries.
Step 1: Install Dependencies
Using Apollo Server (Node.js example):
npm install apollo-server graphql
Step 2: Define the Schema
The schema defines the structure of the API.
type Query {
hello: String
}
Step 3: Create Resolvers
Resolvers return the data requested in the query.
const resolvers = {
Query: {
hello: () => 'Hello, GraphQL!',
},
};
Step 4: Start the Server
const { ApolloServer, gql } = require('apollo-server');
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
This starts a GraphQL API that can be queried at http://localhost:4000
.
Queries
A query is a request to fetch data from a GraphQL server.
Example Query
query {
user(id: "1") {
name
email
}
}
Schema Definition:
type Query {
user(id: ID!): User
}
type User {
id: ID!
name: String
email: String
}
Resolver:
const resolvers = {
Query: {
user: (_, { id }) => users.find(user => user.id === id),
},
};
This returns only the requested fields, making it efficient.
Mutations
A mutation is used to modify data (create, update, delete records).
Example Mutation
mutation {
createUser(name: "Alice", email: "alice@example.com") {
id
name
}
}
Schema:
type Mutation {
createUser(name: String!, email: String!): User
}
Resolver:
Mutation: {
createUser: (_, { name, email }) => {
const newUser = { id: users.length + 1, name, email };
users.push(newUser);
return newUser;
},
};
This allows clients to modify data easily.
Subscriptions
Subscriptions allow real-time updates by pushing data to clients when an event occurs.
Example Subscription
subscription {
userCreated {
id
name
}
}
Schema:
type Subscription {
userCreated: User
}
Resolver with PubSub:
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
const resolvers = {
Subscription: {
userCreated: {
subscribe: () => pubsub.asyncIterator(['USER_CREATED']),
},
},
Mutation: {
createUser: (_, { name, email }) => {
const newUser = { id: users.length + 1, name, email };
users.push(newUser);
pubsub.publish('USER_CREATED', { userCreated: newUser });
return newUser;
},
},
};
When a new user is created, all subscribed clients get updated automatically.
Directives
Directives are special annotations in GraphQL that modify query execution.
Example: @deprecated Directive
type Query {
oldField: String @deprecated(reason: "Use newField instead")
newField: String
}
This warns developers when they use oldField
.
Caching in GraphQL
GraphQL doesnโt provide built-in caching, but tools like Apollo Server offer caching mechanisms.
Example: Apollo Cache
const server = new ApolloServer({
typeDefs,
resolvers,
cache: "bounded",
});
This optimizes performance by reducing repeated database queries.
GraphQL Status Codes
GraphQL responses always return a 200 OK
status code at the HTTP level, even for errors. Instead of relying on HTTP status codes for error handling, GraphQL includes a errors
field in the response body.
Example Response with Errors:
{
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
],
"data": {
"user": null
}
}
This approach ensures consistent response formats, making it easier for clients to handle errors.
For handling authentication or other specific cases, middleware can be used to return appropriate HTTP status codes before executing the GraphQL query.
GraphQL Playground
GraphQL Playground is an interactive tool for testing queries.
Key Features
Auto-completion: Suggests queries based on schema.
Schema Documentation: Helps explore API structure.
Query History: Stores previously executed queries.
Real-time Subscriptions: Allows subscription testing.
Enable Playground in Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: true,
});
Available at http://localhost:4000
.
GraphQL Code Generation
GraphQL Codegen automates the creation of strongly typed GraphQL code.
Installation
npm install @graphql-codegen/cli
Configuration
schema: "http://localhost:4000/graphql"
generates:
src/generated/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-resolvers"
Run Codegen
npx graphql-codegen
This helps in generating types and queries automatically.
Advanced Concepts
Batching and Caching with DataLoader: DataLoader optimizes queries by batching and caching requests.
Custom Scalars: Define custom types like
Date
.Authentication and Authorization: Use middleware to handle authentication.
GraphQL provides unparalleled flexibility and power for building APIs. Developers can build robust, scalable, and efficient applications by understanding their fundamentals and leveraging advanced features. It is a powerful alternative to REST, offering flexibility, efficiency, and real-time capabilities.
๐ Enjoyed this blog?
Reach out in the comments below or on LinkedIn to let me know what you think of it.
For more updates, do follow me here :)
Subscribe to my newsletter
Read articles from Aakanksha Bhende directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Aakanksha Bhende
Aakanksha Bhende
Software Engineer | Open Source Enthusiast | Mentor | Learner I love documenting stuff that I come across and find interesting. Hoping that you will love reading it and get to know something new :)