What it is
GraphQL is a specification for an API where the client sends a query describing the shape of data it wants, and the server returns JSON in that shape. The server publishes a schema: types, fields, and how they relate. Resolvers are functions that know how to load each field. A mutation changes data. A query only reads.
The name collides with Microsoft Graph, which is not GraphQL. Microsoft Graph is a REST API. If someone says "call the Graph", ask which one. This page is GraphQL the query language. The comparison with resource-oriented HTTP is REST versus GraphQL and REST APIs for Application Development.
Why and when it is used
GraphQL helps when many clients need different slices of the same graph (a mobile screen wants three fields, a desktop screen wants thirty) and you would otherwise build a pile of one-off REST endpoints. It also gives you a schema you can generate types from (TypeScript clients).
Skip it when the API is a handful of resources, when HTTP caching of GET responses matters, or when the team does not want to operate resolvers, query-cost limits and a schema process. A clear REST API is not a failure. Adopting GraphQL to look modern, then implementing each query as a pass-through to the same REST service with no design, adds a layer and no leverage.
How it works
One endpoint, often POST /graphql, receives the query text and variables. The server validates the query against the schema, plans the resolvers, and executes them. Because the client chooses fields, a naive resolver that hits the database per field creates the N+1 problem: loading a list of authors and then each author's books in separate queries. DataLoader-style batching exists to fix that and must be used deliberately.
Authorisation is per field or per object, not once per URL, because one query can touch many types. A field you forget to protect is a data leak even if the "endpoint" requires login. Errors can be partial: some fields return data and others return an error array. Clients must handle that. Mutations should still be explicit business actions, not a generic "update anything".
Technologies and dependencies
A server library in your language, a schema file or code-first schema, and a client. Caching at the HTTP layer is weaker than REST GET caching; clients use normalised caches instead. Frontend and Backend still applies: resolvers run on the server. API Authentication: Keys, Sessions and OAuth still applies: the query is not self-authenticating. Observability must log operation names, not only "POST /graphql", or every request looks the same.
How to get started
- Design three types on paper (User, Document, Comment) and the questions a screen actually asks. Do not expose your database tables one-for-one without thinking.
- Implement a read-only schema with one resolver you can trace.
- Send a query that asks for only two fields. Confirm the response omits the rest.
- Add auth and a test where a user cannot read another user's document through a nested field.
- Add batching before you add more relations. Measure the queries.
- Add a mutation for one action, with input validation.
If step 2 already feels like overhead next to a single REST call, stop and ship REST. You can add GraphQL when there is a second client with a different shape.
Cautions and trade-offs
Unbounded query depth and breadth are a denial-of-service: a client can ask for a huge nested graph. Limit depth, complexity or both. Introspection in production reveals the whole API; some teams turn it off, others accept it because the schema is not secret but the data is.
Schema changes are a compatibility problem. Removing a field breaks clients you do not ship yourself. Deprecate first. Resolvers that hide slow downstream calls make one GraphQL request fan out into hundreds of internal calls. And do not confuse a schema with authorisation. A field in the schema is a field someone will query the day you forget the check.