What is GraphQL?
GraphQL is a query language and runtime for APIs, released by Facebook (now Meta) in 2015. Its main feature is that clients can specify exactly what data they need and receive responses with precisely that data, nothing more, nothing less.
Today, many large-scale services including GitHub, Shopify, Twitter (X), and Netflix have adopted GraphQL.
Why was it created: Facebook developed it to solve the problem of “needing to call multiple endpoints with REST APIs, increasing network traffic” that they faced during mobile app development.
GraphQL Fundamentals
Query
Operations for fetching data.
# Fetch user information and posts at once
query {
user(id: "123") {
name
email
posts {
title
createdAt
}
}
}
Mutation
Operations for modifying data.
# Create a new post
mutation {
createPost(input: { title: "Hello", content: "World" }) {
id
title
}
}
Subscription
Operations for subscribing to data changes in real-time.
# Receive new messages in real-time
subscription {
newMessage(roomId: "456") {
content
sender {
name
}
}
}
Schema Definition
In GraphQL, the API shape is defined through a schema.
# Type definitions
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
createdAt: DateTime!
}
# Query and Mutation definitions
type Query {
user(id: ID!): User
posts(limit: Int): [Post!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
}
input CreatePostInput {
title: String!
content: String!
}
Scalar types:
ID,String,Int,Float, andBooleanare built-in types.!means non-null.
Comparison with REST API
| Aspect | REST API | GraphQL |
|---|---|---|
| Endpoints | Multiple per resource | Single (/graphql) |
| Data fetching | Server decides | Client specifies |
| Over-fetching | Likely to occur | Does not occur |
| Under-fetching | Likely to occur | Does not occur |
| Versioning | Managed via URL or headers | Handled through schema evolution |
| Caching | HTTP caching is easy | Requires dedicated mechanisms |
Over-fetching and Under-fetching
REST API case:
GET /users/123 → User info (including unnecessary items)
GET /users/123/posts → Post list (requires separate request)
GraphQL case:
query {
user(id: "123") {
name # Only required items
posts { title } # Fetched in one request
}
}
How Resolvers Work
Resolvers are functions that transform GraphQL queries into actual data.
const resolvers = {
Query: {
user: async (parent, args, context) => {
return await context.db.users.findById(args.id);
},
posts: async (parent, args, context) => {
return await context.db.posts.findMany({ take: args.limit });
},
},
User: {
posts: async (parent, args, context) => {
return await context.db.posts.findMany({
where: { authorId: parent.id }
});
},
},
};
N+1 Problem: When resolving nested fields, database queries can multiply rapidly. Address this with batching and caching mechanisms like DataLoader.
Benefits and Considerations of GraphQL
Benefits
- Efficient data fetching: Get only the data you need in a single request
- Type safety: Strict type definitions through schema
- Developer experience: Auto-generated documentation through introspection
- Easy evolution: Adding fields doesn’t affect existing clients
Considerations
- HTTP caching is difficult: POST requests are the default
- Complex query control: Protection against malicious queries is needed
- Learning cost: New concepts must be learned
- File uploads: Not included in the standard specification
Summary
GraphQL is an API design pattern that enables client-driven data fetching optimization. It’s not a replacement for REST APIs, but should be chosen based on use cases. It’s particularly effective for applications with complex data relationships or when supporting diverse clients.
← Back to list