Intro
GraphQL is a query language for APIs. One endpoint, a typed schema, and clients ask for exactly the shape they need.
GraphQL — what it is
EXAMPLE
# ===== The model =====
# REST exposes resources; clients fetch fixed shapes per endpoint.
# GraphQL exposes a TYPE GRAPH; clients pick the fields they want.
# Single endpoint: POST /graphql
# Single response: exactly the shape the query asked for
# ===== Tiny schema =====
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post { id: ID! title: String! body: String! }
type Query { user(id: ID!): User }
type Mutation { createPost(userId: ID!, title: String!, body: String!): Post! }
# ===== Tiny query =====
query {
user(id: "u-1") {
name
posts { title }
}
}
# Response shape MATCHES the query:
{ "data": { "user": { "name": "Alex", "posts": [{ "title": "hi" }] } } }
# ===== Three operations =====
# query read
# mutation write
# subscription real-time (websocket / SSE)
# ===== Hello, Apollo Server (Node) =====
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = \`
type Query { hello: String! }
\`;
const resolvers = { Query: { hello: () => 'world' } };
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
console.log('Ready at', url);
# ===== Client (urql, viem-style) =====
import { Client, cacheExchange, fetchExchange } from '@urql/core';
const client = new Client({ url: 'http://localhost:4000', exchanges: [cacheExchange, fetchExchange] });
const result = await client.query('{ hello }', {}).toPromise();
# ===== When GraphQL wins =====
# - Many clients with different needs (web + mobile + partners)
# - Aggregation across services
# - Strong typing across team boundaries
# - Self-documenting via GraphiQL / Apollo Studio
# ===== When GraphQL hurts =====
# - One client + simple CRUD (REST is less ceremony)
# - Caching that REST gets for free (HTTP cache); GraphQL needs client cache work
# - Query depth attacks (mitigate with limits)
# ===== Patterns to internalise =====
# - Schema-first design; treat it as the contract
# - DataLoader for batching DB reads (N+1 killer)
# - Persisted queries in production
# - Pagination via cursors (Relay-style edges)
# ===== Pitfalls =====
# - Resolvers that hit the DB per field (N+1)
# - No depth / complexity limits on public APIs (DoS)
# - Exposing internal IDs and types verbatim
# - Treating GraphQL as REST with extra steps
Why it matters
GraphQL is a schema-first API where clients fetch the exact shape they need. The wins are over-fetching gone and types everywhere; the cost is schema design discipline and N+1 mitigation. Worth it when many clients consume one graph.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# Client asks for exactly the fields it needs.
query {
user(id: 1) { id name }
}
Try it Yourself »
Exercise
A GraphQL request specifies exactly which…
to return
Six letters.
Discussion
Loading…