The problem with REST
In REST each resource is a URL. That leads to two frequent annoyances:
- Over-fetching:
GET /users/1returns all the fields even though you only need the name. - Under-fetching: to render a screen you sometimes need several
calls (
/users/1, then/users/1/orders...), the classic "N+1 requests" problem.
GraphQL
GraphQL offers a single endpoint (/graphql) and a typed schema
that describes what data exists. The client asks for exactly the fields it
needs, no more and no less, in a single request.
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
A query (read) declares the exact shape of what it wants:
query {
user(id: "1") {
name # I ask only for name and email,
email # not the rest of the fields
}
}
And a mutation (write) creates or modifies data:
mutation {
createUser(name: "Ana") {
id
}
}
Resolvers
Behind each field of the schema there is a function called a resolver, which resolves (computes or looks up) the value of that field. A resolver is not magic: it is just a function. It receives standard arguments:
// resolver(parent, args, context, info)
const resolvers = {
Query: {
user(_parent, args, context) {
return context.users.find((u) => u.id === args.id);
},
},
};
- parent: the result of the higher-level field (root: usually ignored).
- args: the query arguments (
{ id: "1" }). - context: data shared per request (DB, authenticated user...).
The GraphQL engine walks the query, calls the resolver of each requested field and assembles the response with the exact requested shape.