🚀 GraphQL
A Query Language for Your API
👋 Welcome to GraphQL!
GraphQL is a modern alternative to REST APIs. It gives clients the power to ask for exactly what they need, making APIs more flexible and efficient.
Learn how to build powerful, flexible APIs with GraphQL!
🤔 What is GraphQL?
GraphQL is a query language for APIs and a runtime for executing those queries. Developed by Facebook in 2012 and open-sourced in 2015, it provides a complete and understandable description of the data in your API.
Key Concept:
With REST, you might need multiple endpoints:
GET /users/1
GET /users/1/posts
GET /users/1/friends
With GraphQL, you make ONE request:
query {
user(id: 1) {
name
posts { title }
friends { name }
}
}
⚖️ GraphQL vs REST
| Feature |
REST |
GraphQL |
| Endpoints |
Multiple endpoints |
Single endpoint |
| Data Fetching |
Fixed data structure |
Client specifies exactly what it needs |
| Over-fetching |
Common problem |
Eliminated |
| Under-fetching |
Requires multiple requests |
Single request gets all data |
| Versioning |
Often needs /v1, /v2 |
No versioning needed |
| Documentation |
Manual (Swagger, etc.) |
Self-documenting |
🎯 Core Concepts
📋
Schema
Defines the structure of your API
Types, queries, mutations
🔍
Queries
Read data from the server
Like GET in REST
✏️
Mutations
Modify data on the server
Like POST, PUT, DELETE in REST
🔔
Subscriptions
Real-time updates
WebSocket-based
📝 GraphQL Schema
Defining Types
# User type definition
type User {
id: ID! # ! means required
name: String!
email: String!
age: Int
posts: [Post!]! # Array of posts
friends: [User!]!
}
# Post type definition
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
createdAt: String!
}
# Comment type definition
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}
# Query type - defines what can be queried
type Query {
user(id: ID!): User
users: [User!]!
post(id: ID!): Post
posts: [Post!]!
}
# Mutation type - defines what can be modified
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String, email: String): User!
deleteUser(id: ID!): Boolean!
createPost(title: String!, content: String!, authorId: ID!): Post!
}
# Subscription type - real-time updates
type Subscription {
postAdded: Post!
commentAdded(postId: ID!): Comment!
}
🔍 Writing Queries
Basic Query
# Get a single user
query {
user(id: "1") {
name
email
}
}
# Response
{
"data": {
"user": {
"name": "John Doe",
"email": "john@example.com"
}
}
}
Nested Query
# Get user with their posts
query {
user(id: "1") {
name
email
posts {
title
content
comments {
text
author {
name
}
}
}
}
}
Query with Variables
# Query definition
query GetUser($userId: ID!) {
user(id: $userId) {
name
email
posts {
title
}
}
}
# Variables (sent separately)
{
"userId": "1"
}
Multiple Queries
query {
user1: user(id: "1") {
name
}
user2: user(id: "2") {
name
}
allPosts: posts {
title
}
}
✏️ Mutations
Create Data
mutation {
createUser(name: "Alice", email: "alice@example.com") {
id
name
email
}
}
# Response
{
"data": {
"createUser": {
"id": "3",
"name": "Alice",
"email": "alice@example.com"
}
}
}
Update Data
mutation {
updateUser(id: "1", name: "John Updated") {
id
name
email
}
}
Delete Data
mutation {
deleteUser(id: "1")
}
# Response
{
"data": {
"deleteUser": true
}
}
🔧 Building a GraphQL Server (Node.js)
Setup with Apollo Server
// Install dependencies
// npm install apollo-server graphql
const { ApolloServer, gql } = require('apollo-server');
// Define schema
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
}
`;
// Sample data
let users = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
];
let posts = [
{ id: '1', title: 'First Post', content: 'Hello World', authorId: '1' },
{ id: '2', title: 'Second Post', content: 'GraphQL is awesome', authorId: '1' }
];
// Define resolvers
const resolvers = {
Query: {
users: () => users,
user: (parent, args) => users.find(user => user.id === args.id),
posts: () => posts,
post: (parent, args) => posts.find(post => post.id === args.id)
},
Mutation: {
createUser: (parent, args) => {
const newUser = {
id: String(users.length + 1),
name: args.name,
email: args.email
};
users.push(newUser);
return newUser;
},
createPost: (parent, args) => {
const newPost = {
id: String(posts.length + 1),
title: args.title,
content: args.content,
authorId: args.authorId
};
posts.push(newPost);
return newPost;
}
},
User: {
posts: (parent) => posts.filter(post => post.authorId === parent.id)
},
Post: {
author: (parent) => users.find(user => user.id === parent.authorId)
}
};
// Create server
const server = new ApolloServer({ typeDefs, resolvers });
// Start server
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
💻 Using GraphQL in Frontend
Using Fetch API
async function fetchGraphQL(query, variables = {}) {
const response = await fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query,
variables
})
});
const { data, errors } = await response.json();
if (errors) {
console.error('GraphQL Errors:', errors);
throw new Error(errors[0].message);
}
return data;
}
// Usage - Query
const GET_USER = `
query GetUser($id: ID!) {
user(id: $id) {
name
email
posts {
title
}
}
}
`;
const userData = await fetchGraphQL(GET_USER, { id: '1' });
console.log(userData.user);
// Usage - Mutation
const CREATE_USER = `
mutation CreateUser($name: String!, $email: String!) {
createUser(name: $name, email: $email) {
id
name
email
}
}
`;
const newUser = await fetchGraphQL(CREATE_USER, {
name: 'Alice',
email: 'alice@example.com'
});
console.log(newUser.createUser);
Using Apollo Client (React)
// Install: npm install @apollo/client graphql
import { ApolloClient, InMemoryCache, ApolloProvider, useQuery, useMutation, gql } from '@apollo/client';
// Setup Apollo Client
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql',
cache: new InMemoryCache()
});
// Wrap app with ApolloProvider
function App() {
return (
);
}
// Define query
const GET_USERS = gql`
query GetUsers {
users {
id
name
email
}
}
`;
// Use query in component
function UserList() {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return
Loading...
;
if (error) return
Error: {error.message}
;
return (
{data.users.map(user => (
-
{user.name} - {user.email}
))}
);
}
// Define mutation
const CREATE_USER = gql`
mutation CreateUser($name: String!, $email: String!) {
createUser(name: $name, email: $email) {
id
name
email
}
}
`;
// Use mutation in component
function CreateUserForm() {
const [createUser, { loading, error }] = useMutation(CREATE_USER, {
refetchQueries: [{ query: GET_USERS }]
});
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
createUser({
variables: {
name: formData.get('name'),
email: formData.get('email')
}
});
};
return (
);
}
🔔 Subscriptions (Real-time)
// Server setup with subscriptions
const { ApolloServer, gql, PubSub } = require('apollo-server');
const pubsub = new PubSub();
const typeDefs = gql`
type Post {
id: ID!
title: String!
content: String!
}
type Query {
posts: [Post!]!
}
type Mutation {
createPost(title: String!, content: String!): Post!
}
type Subscription {
postAdded: Post!
}
`;
const resolvers = {
Mutation: {
createPost: (parent, args) => {
const newPost = {
id: String(posts.length + 1),
title: args.title,
content: args.content
};
posts.push(newPost);
// Publish event
pubsub.publish('POST_ADDED', { postAdded: newPost });
return newPost;
}
},
Subscription: {
postAdded: {
subscribe: () => pubsub.asyncIterator(['POST_ADDED'])
}
}
};
// Client usage (React)
const POST_ADDED = gql`
subscription OnPostAdded {
postAdded {
id
title
content
}
}
`;
function PostFeed() {
const { data, loading } = useSubscription(POST_ADDED);
if (loading) return
Waiting for posts...
;
return (
New Post!
{data.postAdded.title}
);
}
💡 GraphQL Best Practices
Best Practices:
- Design Schema First: Plan your types before coding
- Use Descriptive Names: Clear type and field names
- Implement Pagination: For large datasets
- Handle Errors Properly: Return meaningful error messages
- Use DataLoader: Prevent N+1 query problems
- Implement Authentication: Secure your resolvers
- Add Field-Level Authorization: Control access to specific fields
- Document Your Schema: Use descriptions
- Version Carefully: Deprecate fields instead of removing
- Monitor Performance: Track query complexity
Pagination Example
type Query {
posts(first: Int, after: String): PostConnection!
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge {
node: Post!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
🛠️ GraphQL Tools
GraphQL Playground
Interactive IDE for GraphQL
- Test queries
- Auto-complete
- Documentation explorer
Apollo Studio
Complete GraphQL platform
- Schema management
- Performance monitoring
- Team collaboration
GraphiQL
In-browser IDE
- Query editor
- Schema explorer
- History
Hasura
Instant GraphQL APIs
- Auto-generate from database
- Real-time subscriptions
- Authorization
🎓 Our Training Course
📖 Related Topics