// A GraphQL query to get a user's name and email
{
user(id: "1") {
name
email
}
}
// REST example (multiple endpoints)
GET /users/1
GET /users/1/posts
// GraphQL equivalent
{
user(id: "1") {
name
posts {
title
}
}
}
// First public version released in 2015
// Adopted by GitHub in 2016 with GitHub's GraphQL API v4
// Now widely supported by clients and servers in JavaScript, Python, Java, and more.
// Schema definition
type User {
id: ID!
name: String!
email: String!
}
// Query
{
user(id: "1") {
name
email
}
}
// A React component using Apollo Client to fetch GraphQL data
import { useQuery, gql } from '@apollo/client';
const GET_USER = gql`
query {
user(id: "1") {
name
email
}
}
`;
function UserProfile() {
const { loading, error, data } = useQuery(GET_USER);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :( </p>;
return (
<div>
<h1>{data.user.name}</h1>
<p>{data.user.email}</p>
</div>
);
}
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
type Query {
getUser(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!): User
}
type Subscription {
userCreated: User
}
type Product {
id: ID!
name: String!
price: Float!
available: Boolean!
}
type Book {
title: String!
author: Author
}
type Author {
name: String!
}
type Course {
title: String!
tags: [String!]!
}
{
book(id: "1") {
title
author {
name
age
}
}
}
// Example libraries:
Node.js -> Apollo Server
Python -> Graphene
Java -> graphql-java
npm install apollo-server graphql
const { ApolloServer, gql } = require('apollo-server');
// Schema
const typeDefs = gql`
type Query {
hello: String
}
`;
// Resolver
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
// Server
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
// Access it at http://localhost:4000/ after running Apollo Server
// You can run queries like:
{
hello
}
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
const books = [
{ title: '1984', author: 'George Orwell' },
{ title: 'Brave New World', author: 'Aldous Huxley' },
];
const resolvers = {
Query: {
books: () => books,
},
};
// A GraphQL request that says:
// "Give me just the name of the user with ID 1"
{
user(id: "1") {
name
}
}
// Imagine you have a big book of users.
// You don’t want the whole book — just the title page.
// GraphQL lets you grab just the title page!
{
user(id: "1") {
name
}
}
// REST (multiple requests)
GET /user/1
GET /user/1/posts
// GraphQL (one request)
{
user(id: "1") {
name
posts {
title
}
}
}
// Type this into GraphQL Playground:
{
hello
}
// And get this back:
{
"data": {
"hello": "Hello world!"
}
}
// Step 1: Install the tools
npm install apollo-server graphql
// Step 2: Create index.js
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => "Welcome to GraphQL!",
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
type Mutation {
addUser(name: String!, email: String!): User
}
// Mutation to create a new user
mutation {
addUser(name: "Alice", email: "alice@example.com") {
id
name
email
}
}
// Update user info
mutation {
updateUser(id: "1", name: "Alice Smith") {
id
name
}
}
// Delete a user
mutation {
deleteUser(id: "1") {
success
message
}
}
// Define an input type in the schema
input NewUserInput {
name: String!
email: String!
}
type Mutation {
addUser(input: NewUserInput): User
}
// Using the input in a mutation
mutation {
addUser(input: {
name: "Bob"
email: "bob@example.com"
}) {
id
name
}
}
// Mutation with variables
mutation AddUser($name: String!, $email: String!) {
addUser(name: $name, email: $email) {
id
name
}
}
// Variables
{
"name": "Charlie",
"email": "charlie@example.com"
}
type MutationResponse {
success: Boolean!
message: String!
user: User
}
type Mutation {
addUser(name: String!, email: String!): MutationResponse
}
// Example response:
{
"data": {
"addUser": {
"success": true,
"message": "User created successfully",
"user": {
"id": "5",
"name": "Charlie"
}
}
}
}
type Subscription {
newMessage: Message
}
// Install WebSocket support
npm install graphql-ws
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
const resolvers = {
Subscription: {
newMessage: {
subscribe: () => pubsub.asyncIterator(['NEW_MESSAGE']),
},
},
};
// Somewhere in your mutation:
pubsub.publish('NEW_MESSAGE', { newMessage: { text: "Hi!" } });
// Define typeDefs
type Subscription {
messagePosted: String
}
// Publish an event
pubsub.publish('messagePosted', { messagePosted: "New message!" });
import { gql, useSubscription } from '@apollo/client';
const NEW_MESSAGES = gql`
subscription {
newMessage {
text
}
}
`;
function ChatBox() {
const { data } = useSubscription(NEW_MESSAGES);
return <div>{data?.newMessage?.text}</div>;
}
// Schema
type Query {
hello: String
}
// Resolver
const resolvers = {
Query: {
hello: () => "Hello from the server!"
}
};
const resolvers = {
Query: {
getUser: () => { /* return user */ }
},
Mutation: {
createUser: () => { /* add user */ }
},
User: {
fullName: (parent) => `${parent.firstName} ${parent.lastName}`
}
};
type User {
id: ID!
firstName: String!
lastName: String!
fullName: String!
}
// Resolver
User: {
fullName: (parent) => {
return `${parent.firstName} ${parent.lastName}`;
}
}
type Product {
id: ID!
price: Float!
tax: Float!
totalPrice: Float!
}
// Resolver
Product: {
totalPrice: (parent) => {
return parent.price + parent.tax;
}
}
Query: {
async getUser(_, args) {
const user = await db.findUserById(args.id);
return user;
}
}
// Example: clean structure
Query: {
user: (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUserById(id);
}
}
type Query {
user(id: ID!): User
}
// Query example
{
user(id: "1") {
name
}
}
type Mutation {
createUser(name: String!, age: Int): User
}
type Query {
searchBooks(keyword: String!): [Book]
}
// Query with variable
query GetUser($id: ID!) {
user(id: $id) {
name
}
}
// Variables
{
"id": "5"
}
fragment userFields on User {
name
email
}
{
user(id: "1") {
...userFields
}
admin(id: "2") {
...userFields
}
}
fragment userFields on User {
name
email
}
fragment userFields on User {
name
email
}
{
search(id: "1") {
... on User {
name
}
... on Admin {
role
}
}
}
{
result {
... on Book {
title
}
... on Author {
name
}
}
}
query GetUser($showEmail: Boolean!) {
user(id: "1") {
name
email @include(if: $showEmail)
}
}
// In schema:
type User {
name: String!
oldField: String @deprecated(reason: "Use 'newField' instead.")
}
// In SDL
directive @log on FIELD_DEFINITION
// Example use
type Query {
users: [User] @log
}
// Query with @skip
query GetUser($hideEmail: Boolean!) {
user(id: "2") {
name
email @skip(if: $hideEmail)
}
}
// Variables
{
"hideEmail": true
}
input CreateUserInput {
name: String!
email: String!
}
type Mutation {
createUser(input: CreateUserInput): User
}
type Address {
city: String!
country: String!
}
type User {
name: String!
address: Address
}
enum Role {
ADMIN
USER
GUEST
}
type User {
name: String!
role: Role!
}
// Union example
union SearchResult = Book | Author
type Book {
title: String!
}
type Author {
name: String!
}
type Query {
search(keyword: String!): [SearchResult]
}
// Interface example
interface Animal {
name: String!
}
type Dog implements Animal {
name: String!
breed: String!
}
type Cat implements Animal {
name: String!
color: String!
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
author: User!
}
// userSchema.js
const typeDefs = gql`
type User {
id: ID!
name: String!
}
type Query {
user(id: ID!): User
}
`;
// Using tools like @graphql-tools/stitch
const stitchedSchema = stitchSchemas({
subschemas: [userSchema, postSchema],
});
// Schema-first
typeDefs = gql`
type User {
id: ID!
name: String!
}
`
// Code-first (e.g. with Nexus or TypeGraphQL)
const User = objectType({
name: 'User',
definition(t) {
t.id('id');
t.string('name');
}
});
"""A user who writes blog posts"""
type User {
"""Unique ID for the user"""
id: ID!
"""User's full name"""
name: String!
}
// Sample error response
{
"data": null,
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
]
}
const resolvers = {
Query: {
user: async (_, { id }) => {
try {
const user = await getUser(id);
if (!user) throw new Error("User not found");
return user;
} catch (error) {
throw new Error(error.message);
}
}
}
};
throw new GraphQLError("Invalid email format", {
extensions: {
code: "BAD_USER_INPUT",
field: "email"
}
});
// Basic logging
console.error(error);
// With Sentry
Sentry.captureException(error);
// Middleware to protect server
app.use((req, res, next) => {
const token = req.headers.authorization;
if (!token || token !== "valid_token") {
return res.status(401).send("Unauthorized");
}
next();
});
const resolvers = {
Query: {
privateData: (parent, args, context) => {
if (context.user.role !== "admin") {
throw new Error("Access denied");
}
return "Top Secret Info";
}
}
};
import jwt from 'jsonwebtoken';
const context = ({ req }) => {
const token = req.headers.authorization || "";
const user = jwt.verify(token, "my_secret_key");
return { user };
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const token = req.headers.authorization;
const user = getUserFromToken(token);
return { user };
}
});
type Query {
profile: User @auth(requires: USER)
adminPanel: AdminData @auth(requires: ADMIN)
}
// Prisma user model (schema.prisma)
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
// Resolver using Prisma
const resolvers = {
Query: {
users: async (_, __, { prisma }) => await prisma.user.findMany()
}
};
// MongoDB example with Mongoose
const User = mongoose.model("User", userSchema);
Query: {
users: () => User.find()
}
// Using Prisma in mutation resolver
Mutation: {
createUser: (_, { name, email }, { prisma }) => {
return prisma.user.create({
data: { name, email }
});
}
}
// Schema
type Query {
users(name: String, skip: Int, take: Int): [User]
}
// Resolver with filtering
Query: {
users: (_, { name, skip = 0, take = 10 }, { prisma }) => {
return prisma.user.findMany({
where: { name: { contains: name } },
skip,
take
});
}
}
// Schema
type Query {
posts(skip: Int, limit: Int): [Post]
}
// Resolver
Query: {
posts: (_, { skip = 0, limit = 10 }, { db }) => {
return db.post.find().skip(skip).limit(limit);
}
}
// Schema
type Query {
postsAfter(cursor: ID, limit: Int): [Post]
}
// Resolver
Query: {
postsAfter: async (_, { cursor, limit = 5 }, { prisma }) => {
return prisma.post.findMany({
take: limit,
skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined
});
}
}
// Schema
type Query {
products(category: String, inStock: Boolean): [Product]
}
// Resolver
Query: {
products: (_, { category, inStock }, { db }) => {
return db.product.find({ category, inStock });
}
}
// Schema
type Query {
posts(orderBy: String): [Post]
}
// Resolver (example using Prisma)
Query: {
posts: (_, { orderBy = "createdAt_DESC" }, { prisma }) => {
return prisma.post.findMany({
orderBy: {
createdAt: orderBy === "createdAt_DESC" ? "desc" : "asc"
}
});
}
}
// Install
npm install @graphql-codegen/cli --save-dev
// Example config file (codegen.yml)
schema: http://localhost:4000/graphql
documents: src/**/*.graphql
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
// Example: extend types across services
// In Users service
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post]
}
// Example config
sources:
- name: RESTCountries
handler:
openapi:
source: https://restcountries.com/v3/openapi.json
// Install
npm install graphql-yoga
// Quick setup
import { createServer } from 'graphql-yoga';
const server = createServer({
schema: {
typeDefs: `type Query { hello: String }`,
resolvers: { Query: { hello: () => 'Hi Yoga!' } },
},
});
server.start();
// Use Altair at: https://altair.sirmuel.design/
// Use GraphiQL in-browser if Apollo Server includes it
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const server = new ApolloServer({
validationRules: [createComplexityLimitRule(1000)],
});
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
validationRules: [depthLimit(5)],
});
const DataLoader = require('dataloader');
const userLoader = new DataLoader(async (ids) => {
return getUsersByIds(ids); // Batch DB call
});
// N+1 example:
posts: () => posts.map(post => getUser(post.userId)) // BAD
// Better with DataLoader:
posts: () => posts.map(post => userLoader.load(post.userId)) // GOOD
const client = new ApolloClient({
uri: '/graphql',
cache: new InMemoryCache(),
});
// Jest example: test a resolver
const resolvers = {
Query: {
hello: () => 'Hello world',
},
};
test('hello resolver returns correct string', () => {
expect(resolvers.Query.hello()).toBe('Hello world');
});
import request from 'supertest';
import { server } from '../index'; // your GraphQL server
test('integration test for hello query', async () => {
const response = await request(server)
.post('/graphql')
.send({ query: '{ hello }' });
expect(response.body.data.hello).toBe('Hello world');
});
import { makeExecutableSchema } from '@graphql-tools/schema';
import { addMocksToSchema } from '@graphql-tools/mock';
const schema = makeExecutableSchema({ typeDefs });
const mockedSchema = addMocksToSchema({ schema });
test('mocked hello', async () => {
// Test mocked schema here
});
import { createTestClient } from 'apollo-server-testing';
const { query } = createTestClient(server);
test('test hello query', async () => {
const res = await query({ query: gql`{ hello }` });
expect(res.data.hello).toBe('Hello world');
});
// Instead of one big schema, break it into smaller services
// Each subgraph handles a part of your data
// Gateway setup example
const { ApolloGateway } = require('@apollo/gateway');
const { ApolloServer } = require('apollo-server');
const gateway = new ApolloGateway({
serviceList: [
{ name: 'users', url: 'http://localhost:4001/graphql' },
{ name: 'products', url: 'http://localhost:4002/graphql' },
],
});
const server = new ApolloServer({ gateway, subscriptions: false });
server.listen().then(({ url }) => {
console.log(`Gateway ready at ${url}`);
});
# In users service
type User @key(fields: "id") {
id: ID!
name: String
}
# In products service extending User
extend type User @key(fields: "id") {
id: ID! @external
reviews: [Review]
}
// Managed federation lets Apollo Studio handle this dynamically
// Clients query the gateway as if it’s one API
{
user(id: "1") {
name
reviews {
body
}
}
}
// GraphQL server acts as a gateway,
// forwarding queries to different microservices
query {
user(id: "1") {
name
orders {
id
total
}
}
}
const fetchUser = () => fetch('/api/user').then(res => res.json());
const fetchOrders = () => fetch('/api/orders').then(res => res.json());
const resolvers = {
Query: {
user: () => fetchUser(),
orders: () => fetchOrders(),
}
};
// Using schema stitching or Apollo Federation
const gateway = new ApolloGateway({
serviceList: [
{ name: 'users', url: 'http://users-service/graphql' },
{ name: 'orders', url: 'http://orders-service/graphql' },
],
});
// mesh.config.yaml example
sources:
- name: LegacyREST
handler:
openapi:
source: https://legacyapi.example.com/openapi.json
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_USERS = gql`
query {
users {
id
name
}
}
`;
function UsersList() {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return data.users.map(user => <div key={user.id}>{user.name}</div>);
}
import { useQuery, gql } from '@vue/apollo-composable';
const GET_TODOS = gql`
query {
todos {
id
text
}
}
`;
export default {
setup() {
const { result, loading, error } = useQuery(GET_TODOS);
return { result, loading, error };
}
};
// In Angular service
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private apollo: Apollo) {}
getUsers() {
return this.apollo.watchQuery({
query: gql`
query {
users {
id
name
}
}
`
}).valueChanges;
}
}
// Example: Next.js page with Apollo
import { gql, useQuery } from '@apollo/client';
const GET_POSTS = gql`
query {
posts {
id
title
}
}
`;
export default function Posts() {
const { data, loading } = useQuery(GET_POSTS);
if (loading) return <p>Loading...</p>;
return <ul>{data.posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>;
}
npm install @nestjs/graphql graphql-tools graphql apollo-server-express
// Code-first example:
@ObjectType()
class User {
@Field()
name: string;
@Field()
email: string;
}
@Resolver(of => User)
export class UserResolver {
@Query(returns => User)
getUser() {
return { name: 'Alice', email: 'alice@example.com' };
}
}
@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: true,
}),
],
})
export class AppModule {}
// Example: Deploying a Node.js Apollo Server to Heroku
// 1. Create a Git repo with your server code
// 2. heroku create
// 3. git push heroku main
// 4. heroku open
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: false, // disable in production
});
// Sign up at Apollo Studio
// Connect your server with Apollo Studio by adding API key in Apollo Server setup
// Example: Express health check endpoint
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
// REST example - multiple requests needed to get user and posts
GET /users/1
GET /users/1/posts
// GraphQL example - one query fetches user and posts
{
user(id: "1") {
name
posts {
title
content
}
}
}
// REST returns all user data, even if client only needs name
GET /users/1
// Response:
{
"id": "1",
"name": "Alice",
"email": "alice@example.com",
"address": {...},
"phone": "123456789"
}
// GraphQL query only fetches the name
{
user(id: "1") {
name
}
}
// REST versioning example
GET /api/v1/users/1
GET /api/v2/users/1
// GraphQL uses @deprecated directive instead
type User {
oldField: String @deprecated(reason: "Use newField instead")
newField: String
}
// REST response for not found
Status: 404 Not Found
// GraphQL response with partial data and errors
{
"data": {
"user": null
},
"errors": [
{
"message": "User not found",
"path": ["user"]
}
]
}
| Feature | REST | GraphQL |
|---------------------|--------------------------------|------------------------------|
| Endpoints | Multiple | Single |
| Data Fetching | Fixed responses | Flexible queries |
| Versioning | URL-based | Schema evolution |
| Over-fetching | Common | Minimal |
| Tooling | Mature | Growing, powerful |
| Real-time | Via other tech | Built-in subscriptions |