Accent colour
 
Shortcuts: press ?
 

GraphQL Explained — How Modern Apps Ask for Exactly the Data They Need

Muhammad Tahir November 15, 2026

Introduction

Imagine ordering food at a restaurant where the menu only offers fixed set meals. You want the main course from one set, the dessert from another, and no starter at all. The waiter cannot help: you must order whole set meals, take what you do not want, and pay for all of it. Then you visit a restaurant where you order exactly the dishes you want, nothing more and nothing less.

That is, in essence, the difference between a traditional REST API and GraphQL. REST gives you fixed endpoints that return fixed sets of data. GraphQL lets the client describe exactly the data it needs, and the server returns exactly that — no more, no less. It is a small shift in who decides the shape of the data, and it has changed how many modern applications are built.

GraphQL was developed at Facebook in 2012 and released as open source in 2015, after the company struggled to serve its mobile apps efficiently with REST. Since then it has been adopted by companies such as GitHub, Shopify, Airbnb, and Netflix, and it is now a standard tool in the full-stack developer's toolkit. This guide explains what GraphQL is, the problem it solves, how it works in practice, where it beats REST, where it does not, and how to decide whether it fits your project.

The Problem GraphQL Solves

To understand GraphQL, start with the problem. In a REST API, the server defines a set of endpoints, and each endpoint returns a fixed shape of data. A typical app might fetch a user profile from `/users/42`, that user's posts from `/users/42/posts`, and each post's comments from `/posts/17/comments`. That is three separate requests for one screen of data. This is called under-fetching: the client needs several round trips to gather everything it needs, and each round trip adds delay, especially on a slow mobile connection.

The opposite problem is over-fetching. An endpoint designed for one screen often returns far more data than another screen needs. A user profile endpoint might return the user's address, phone number, settings, preferences, and full history, when the client only wanted a name and a profile picture. The extra data wastes bandwidth and slows the response, and on a mobile app it can mean real money and battery life.

Both problems come from the same root cause: with REST, the server decides what data each response contains. The client asks for an endpoint and takes whatever it gets. That works well when the server and the client are built together and change together. It breaks down when many different clients — a web app, an iOS app, an Android app, a smartwatch — all need slightly different data from the same server, or when the data a screen needs changes frequently.

GraphQL flips the decision. The client sends a query that describes exactly the fields it wants, and the server returns exactly those fields. The same endpoint serves every client, and each client asks for its own shape of data. A mobile app can ask for just a name and a photo, while a web dashboard asks for the full profile, and both use the same API.

What GraphQL Actually Is

GraphQL is a query language for APIs and a runtime for answering those queries. It is not a database, and it is not a replacement for your backend logic. It is a layer that sits in front of your existing data sources — databases, microservices, third-party APIs — and presents them to clients as a single, consistent graph of data.

The central idea is the graph. In GraphQL, your data is modelled as a graph of types connected by relationships. A `User` has `posts`, each `Post` has an `author` and `comments`, each `Comment` has an `author`. A client navigates this graph with a query, following the relationships it needs and stopping where it wants. The query mirrors the shape of the response, which makes it easy to read: what you ask for is what you get, in the same structure.

Every GraphQL API has a schema, which is a description of all the types, fields, and operations the API supports. The schema is the contract between the client and the server. It says: these are the things you can ask for, these are the types of the answers, and these are the rules. Because the schema is written down in a standard language, tools can read it and generate documentation, validate queries before they are sent, and even generate client code automatically. That self-describing quality is one of GraphQL's biggest practical advantages.

A GraphQL server normally exposes a single endpoint, usually `/graphql`, which accepts POST requests. Every query, whether it fetches a user, a list of products, or a dashboard summary, goes to that one endpoint. This surprises developers who are used to REST's many endpoints, but it is deliberate: the query itself says what data is wanted, so the URL does not need to.

Queries, Mutations, and Subscriptions

GraphQL has three kinds of operation. The most common is the query, which reads data. A query names the fields it wants and can nest fields to follow relationships. For example, a query might ask for a user's name, their three most recent posts, and the author name of each comment on those posts — all in one request. The server resolves the query field by field, fetching each piece from wherever it lives, and returns a single JSON response shaped like the query.

The second kind is the mutation, which changes data. Mutations create, update, or delete things: publishing a post, changing a password, placing an order. A mutation is like a query that also has a side effect, and it returns the data the client needs after the change — for example, the updated post, so the screen can refresh without a second request. Mutations run in sequence, one after another, so that two changes to the same data do not clash.

The third kind is the subscription, which is for live data. A subscription keeps a connection open and pushes updates to the client whenever something changes: a new message arrives, a stock price moves, a delivery changes status. Subscriptions are less common than queries and mutations, but they are valuable for real-time features such as chat and live dashboards, and they are built into the language rather than bolted on.

Each field in a query can take arguments, which work like function parameters. A query can ask for `posts(first: 10)`, or `user(id: 42)`, or `search(term: "graphql")`. Arguments make a single field flexible: the same `posts` field serves a homepage that wants ten posts and a profile page that wants five, without needing a separate endpoint for each.

How a Query Is Answered

When a GraphQL server receives a query, it does not run a single database call. It walks through the query field by field, using small functions called resolvers. Each resolver knows how to fetch one field: the resolver for `User.posts` might query the posts table, and the resolver for `Post.author` might fetch a user record. Resolvers are independent, so the server can fetch data from many different sources — a database, another service, a cache — and assemble it into one response.

This design is flexible, but it has a trap that every GraphQL developer meets sooner or later: the N+1 problem. If a query asks for 100 posts and the author of each one, a naive server will run one query to fetch the posts and then 100 more queries, one per author. The fix is batching: a tool such as DataLoader collects all the author requests, groups them by the IDs needed, and fetches them in a single database query. Batching and caching in resolvers is a standard part of building a production GraphQL server, and it is worth learning early.

Another important piece is that the schema gives the server and client a shared understanding. The client can ask the server for its schema — a process called introspection — and tools can then generate typed client code, autocomplete field names in an editor, and catch mistakes before the query is ever sent. This tight feedback loop is a large part of why developers enjoy working with GraphQL: many errors that would only appear at runtime with REST are caught while writing the code.

GraphQL Versus REST

The honest answer to "should I use GraphQL or REST?" is that it depends on the shape of your problem. GraphQL is strongest when the client needs flexible, varied data. If you have several clients with different data needs, if your screens need deeply nested data, or if your frontend team wants to move fast without waiting for the backend team to build a new endpoint for every screen, GraphQL removes a real bottleneck. It is also strong when your data comes from many sources, because the GraphQL layer can hide that complexity behind one clean schema.

REST is strongest when the problem is simple and well understood. A small API with a few resources, a public API where caching and standard HTTP behaviour matter, or a service where each endpoint maps neatly to one piece of data — in these cases REST is simpler, easier to cache, and easier to reason about. REST endpoints can be cached by ordinary HTTP caches and CDNs, because each URL identifies one resource. GraphQL, with its single endpoint and POST requests, needs application-level caching instead, which is more work to set up.

GraphQL also has costs. The server is more complex to build and operate: you need resolvers, batching, query complexity limits, and protection against abusive queries. A malicious client can send a deeply nested query that asks for enormous amounts of data, so a production server must limit how deep and how expensive a query can be. File uploads, which are trivial with REST, need a separate convention in GraphQL. And error handling is different: a GraphQL response can contain partial data and partial errors at the same time, which clients must be written to handle.

A useful rule of thumb: if your clients keep asking for slightly different data from the same resources, GraphQL will pay for itself. If your API is simple, stable, and heavily cached, REST will be the cheaper choice. Many large systems use both: GraphQL for the client-facing API, and REST for internal service-to-service calls where the data shape is fixed.

Getting Started

If you want to try GraphQL, start by exploring a public API. GitHub's GraphQL API is free to use and well documented, and its built-in explorer lets you write queries and see results immediately. Try fetching a repository, its issues, and the authors of the comments on those issues in a single query, and compare that with how many REST requests the same screen would need. That comparison makes the value of GraphQL concrete.

For building your own server, the most popular route is Apollo Server for Node.js, which has good documentation and a large community. Alternatives include GraphQL Yoga, Strawberry for Python, and gqlgen for Go. On the client side, Apollo Client and Relay are the main choices for web and mobile apps, with code generation tools that turn your schema into typed functions your editor can check.

When designing a schema, start from the client's needs, not the database. Ask what screens the app has and what data each one needs, then design types and fields that serve those screens. Keep the schema stable: once clients depend on a field, removing it breaks them, so prefer adding new fields over changing old ones. And from the beginning, add query complexity limits, depth limits, and authentication checks on every resolver — a GraphQL endpoint that accepts any query from anyone is an invitation to abuse.

Conclusion

GraphQL changes who decides the shape of an API response: the client, not the server. By letting clients describe exactly the data they need, it solves the over-fetching and under-fetching that slow down REST APIs, and it gives frontend teams the freedom to build new screens without waiting for new endpoints. Its schema, tooling, and single endpoint make it a strong choice for applications with several clients and varied data needs.

It is not a replacement for REST in every case. It adds complexity to the server, needs careful protection against expensive queries, and loses some of REST's built-in caching. But for the problem it was built to solve — serving many different clients from one API, efficiently — it remains the best tool available. The next time you find yourself adding a third endpoint to fetch the data for one screen, it is worth asking whether a single query would have done the job.

Frequently Asked Questions

What is GraphQL in simple terms? GraphQL is a query language for APIs. Instead of calling fixed endpoints that return fixed data, the client sends a query describing exactly the fields it wants, and the server returns exactly those fields.

Is GraphQL a database? No. GraphQL is an API layer that sits in front of your data sources, such as databases and other services. It does not store data itself; it fetches data from your existing systems and presents it in a consistent way.

Does GraphQL replace REST? Not always. GraphQL is better when clients need flexible, varied data and when many clients share one API. REST is often simpler and easier to cache when the API is small, stable, and public. Many systems use both.

What is a GraphQL schema? The schema is a written description of everything an API can do: the types of data, the fields on each type, and the queries and mutations available. It acts as a contract between client and server, and tools use it to validate queries and generate code.

What is the N+1 problem in GraphQL? It happens when a query asks for a list of items and a related field on each one, and the server makes one database query per item instead of one query for all of them. The standard fix is batching with a tool such as DataLoader.

Was this article helpful?

Muhammad Tahir

Author

 

Get new articles by email

 

Comments