// 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 |