GraphQL is often described as "a query language for your API", which is accurate but unhelpful if you have never used one. The clearest way to understand it is to follow a request from the client’s query through the schema to the resolvers that fetch the data.
The schema is the contract
At the heart of every GraphQL API is a schema: a strongly typed description of all the data available and how it relates. It defines types, their fields, and the queries and mutations clients may perform. The schema is the single source of truth — clients can explore it, tools can validate against it, and it doubles as living documentation.
Queries: asking for data
A query mirrors the shape of the data you want. You name the fields you need, including nested ones, and the response comes back in exactly that structure. Ask for a user’s name and the titles of their posts, and that is precisely what you receive — not the whole user, not the whole posts, just those fields.
query {
user(id: "42") {
name
posts {
title
}
}
}Mutations: changing data
Where queries read, mutations write. They look similar but signal intent to change state — creating, updating or deleting — and typically return the affected data so the client can update without a second request. Keeping reads and writes clearly separated is part of what makes a GraphQL API predictable.
Resolvers: where the work happens
Each field in the schema is backed by a resolver — a function that knows how to fetch that piece of data. When a query arrives, GraphQL walks the requested fields and calls their resolvers, assembling the result. This is powerful but is also where performance lives: a carelessly written resolver can fire a database query per item, the classic N+1 problem, which is solved with batching techniques.
- The schema is a typed contract describing all available data.
- Queries read and mutations write, both shaped by the client.
- Resolvers fetch each field; watch for N+1 query problems.
Frequently asked questions
Does GraphQL need a special database?
No. GraphQL sits above your data sources. Resolvers can fetch from any database, REST API or service.
How do you cache GraphQL?
Because there is one endpoint, simple URL caching does not apply. Caching is done at the client (normalized caches) and within resolvers (data loaders), or with persisted queries.