contact us


When you sit down to build an API, your mind jumps straight to REST. It has been the default for two decades, and defaults are comfortable. But GraphQL keeps turning up in the room, and GraphQL vs REST is now one of the first architectural decisions a product team has to make.
So let us skip the suspense. Choose GraphQL when many different clients need different shapes of the same data. Choose REST when caching, monitoring and operational simplicity matter more than query flexibility. Most organisations end up running both, and that is fine.
Think of it as a kitchen. REST hands every table the same set menu, and if you want the side dish you order a second course. GraphQL hands you the order pad and lets you write down exactly what you want. The food is the same, and the kitchen is the same. What changes is who decides the plate, and what that decision costs you later.
That last part is where this article spends most of its time: the features, the real differences, and the cost, risk and delivery consequences of picking one over the other.
GraphQL is a query language for APIs that enables declarative data fetching. In plain words: the client says exactly which data it wants, and gets exactly that back. It also makes APIs easier to evolve over time. The behaviour is set out in a public specification, so it is a genuine standard rather than a single vendor's product.
Three things it is not, because all three come up in the first meeting.
GraphQL was developed internally by Facebook (now Meta) in 2012 before being open-sourced in September 2015. It was co-created by Lee Byron, Nick Schrock and Dan Schafer while working on Facebook's mobile applications. Keep that context in mind, because it explains most of the design decisions that follow.
The intent to move the project to a vendor-neutral GraphQL Foundation under the Linux Foundation was announced in November 2018, and the Foundation was formally established in 2019.
Crucially, the spec is not frozen in 2018. The September 2025 edition, the first full edition since October 2021, introduced Schema Coordinates (machine- and human-readable addresses for schema elements), OneOf input objects (mutually exclusive inputs expressed in the schema), and descriptions on executable documents. Several of these were designed with codegen tools and LLM-powered agents in mind, which matters if your API is increasingly consumed by AI tooling rather than only by humans.
GraphQL is used by teams of all sizes, across many environments and languages. The best-known adopters are Facebook, GitHub, Pinterest, Shopify, Airbnb and Netflix.
Before we compare anything, here is a simple GraphQL query that fetches a user along with their name and age:
{
user(id: "1") {
name
age
}
}And the JSON response you get back:
{
"data": {
"user": {
"name": "João Inez",
"age": 29
}
}
}Notice that the response mirrors the query, field for field. That is the whole point of the declarative bit: you are writing JSON objects without the values, and you can read what a request will return without ever running it.
REST was defined by Roy Fielding, the computer scientist who set out its principles in his PhD dissertation in 2000.
REST (Representational State Transfer) is a software architectural style that defines a set of constraints which make a web service a true RESTful API. Those constraints are:


Take those five together and you get clients, intermediaries and resource servers all speaking through one shared, cacheable interface. That property is what everything later in this article turns on. Because the interface is uniform and addressable, anything sitting in between, from a browser cache to a content delivery network (CDN), can act on a response without understanding a word of it.
Two reasons pushed companies such as Facebook, Netflix and Coursera towards alternatives:
Go one step further and the real reason is the shape of the data. Most data in modern web and mobile applications is graph-shaped, a network of connected entities rather than a stack of flat tables. News pieces have comments, and those comments have likes or spam flags, created or reported by users. Fetching that through resource-per-endpoint calls means walking the graph one request at a time, which is exactly as slow as it sounds.
So Facebook started building GraphQL. Netflix and Coursera were working on their own alternatives at the same time. After Facebook open-sourced GraphQL, Coursera dropped its efforts and adopted the new technology. Netflix carried on and later open-sourced Falcor, a JavaScript library that models remote data as one virtual JSON graph. Falcor is now largely dormant, so treat it as a historical footnote rather than a live option.
GraphQL provides a query language that lets clients request only the data they need. REST relies on fixed endpoints and server-defined data structures. Whether GraphQL is "better" depends on your requirements and how much flexibility your project needs: GraphQL wins on client flexibility and front-end delivery speed; REST wins on caching, monitoring and operational maturity. Amazon's own decision guide for GraphQL frames the same trade-off in terms of total cost of ownership, which is a useful second opinion when you are building the business case internally.

Let us walk through a practical example, point by point.
Imagine you have a blog, and you want the front page to show all the latest posts. You need to fetch the posts, so you would probably write something like this:
GET /api/posts
[
{ "id": 1, "title": "GraphQL vs REST", "subtitle": "Choosing an API", "date": "2026-02-21" },
{ "id": 2, "title": "Scaling a mobile back end", "subtitle": "Lessons learned", "date": "2026-02-14" }
]But what if you want the author as well? Three options:
1. Fetch the authors from another resource:
GET /api/posts
GET /api/authors?ids=1,2
[
{ "id": 1, "name": "João Inez" },
{ "id": 2, "name": "Ana Silva" }
]2. Modify the resource to also return the author:
GET /api/posts
[
{
"id": 1,
"title": "GraphQL vs REST",
"subtitle": "Choosing an API",
"date": "2026-02-21",
"author": { "id": 1, "name": "João Inez" }
}
]3. Create a resource that returns the posts with the author:
GET /api/posts-with-authors
[
{
"id": 1,
"title": "GraphQL vs REST",
"author": { "id": 1, "name": "João Inez" }
}
]Each option solves the problem and creates a new one. Let us take them one at a time.
With the first approach, fetching the authors from another resource, you end up with two server requests instead of one. Scale that up and you have more requests to more endpoints just to assemble a single view. On a mobile connection, every round trip is latency your user feels in their thumb.
With GraphQL that does not happen. One request, no round trips:
{
posts {
title
subtitle
date
author {
name
}
}
}{
"data": {
"posts": [
{
"title": "GraphQL vs REST",
"subtitle": "Choosing an API",
"date": "2026-02-21",
"author": { "name": "João Inez" }
}
]
}
}The second approach, modifying the resource to also return the author, solves the immediate problem nicely. Then it quietly creates another one somewhere else in your application. Over-fetching.
Back to your blog. This time you also have a sidebar listing the top monthly posts with their titles, subtitles and dates, and it uses the /api/posts resource. You modified that resource, so it now returns the author too. The sidebar does not want the author, but every client calling that endpoint pays for it anyway.
For users on limited data plans and slow connections, useless data is a real cost. GraphQL lets the client ask for the fields it needs and nothing else, so the problem never arises:
{
posts(sort: "monthly_top", limit: 5) {
title
subtitle
date
}
}{
"data": {
"posts": [
{ "title": "GraphQL vs REST", "subtitle": "Choosing an API", "date": "2026-02-21" }
]
}
}Which brings us to the third approach, creating a resource that returns the posts with the author. Structuring endpoints around the views in your project is a common enough pattern.
It does solve the problem above. It also slows front-end development down, because every specific view now needs its own specific endpoint. A view needs one new field, and front-end work stalls until the back-end team ships the update. That coordination cost is the one teams consistently underestimate.
GraphQL hands the client the order pad instead. Adding a field costs nobody a back-end release. You would go from this:
{
posts {
title
author {
name
}
}
}To this:
{
posts {
title
subtitle
date
author {
name
avatarUrl
}
}
}There is no back-end change in between.

| Dimension | GraphQL | REST |
|---|---|---|
| Data fetching | Client specifies the fields; one request returns exactly them | Server defines the response; related data needs more calls |
| Endpoints | One endpoint for the whole schema | One endpoint per resource |
| Caching | Built by you, in the client or a persisted-query layer | Free from HTTP; works in the browser and at the CDN |
| Error handling | 200 OK with an errors array; needs GraphQL-aware tooling | HTTP status codes, understood by every monitoring tool |
| Authorisation | Enforced per field or per resolver | Enforced per endpoint |
| Versioning | Additive; old fields marked @deprecated | Explicit, usually /v1/ to /v2/ |
| Rate limiting | By query cost and depth, since request cost varies | By requests per minute, since cost is roughly uniform |
| Best fit | Several different clients on one back end | Public APIs, read-heavy and cacheable traffic |
A quick recap of the differences, and what each one costs you:
As we said, GraphQL does not replace REST. Look past the differences and the two have a good deal in common, which is part of why REST vs GraphQL is rarely an either-or decision:
Query and Mutation types plays the same role as the list of endpoints in a REST API. A mutation, by the way, is simply a GraphQL operation that writes data rather than reading it.GraphQL was built for mobile clients, and that is still where it earns its keep. A phone app runs on a connection you do not control, so every unnecessary field and every extra round trip is latency the user feels. Look back at the over-fetching example: the sidebar needed three fields, and the modified REST endpoint returned five, including a nested author object the view never rendered. Multiply that across a screen, on a bad network, and it shows.
The more clients you serve from one back end, and the more they differ from each other, the more this matters. It is also a good fit wherever the front end iterates faster than the back end, because the schema absorbs change that would otherwise mean new endpoints.
This is the archetype we build for most often: a low-latency API serving several clients from one back end. On TrustPortal, for example, we delivered the web and mobile application together with the API services behind them, tuned for low latency and built to run in many languages for an international user base. That is exactly the situation where a single, client-shaped data layer pays off, whether you reach it through GraphQL or a carefully designed REST API.
GraphQL is a strong tool. It is not a complete one. If any of the following matters in your project, consider REST.
Every browser ships an HTTP cache that avoids refetching resources and works out when two resources are the same. It costs you nothing, everyone understands it, and it works at the CDN as well as in the browser.
GraphQL has no globally unique identifier for an object at the URL level, because every request goes to the same endpoint, usually over POST. To get caching you build it yourself, either in the client (Apollo Client, Relay, urql) or in a persisted-query layer in front of the server. That is real work, and real operational surface. A REST API gets it from the protocol, and pairs naturally with a cache such as Redis or Memcached.
With REST you can build a monitoring system on top of status codes. A 500 is an incident, a 404 is a missing resource, a 200 means everyone can go home. GraphQL gives you none of that, because it returns 200 OK for almost everything, failures included. A typical GraphQL error looks like this:
{
"errors": [
{
"message": "Cannot query field \"avatarURL\" on type \"Author\".",
"locations": [{ "line": 5, "column": 7 }],
"path": ["posts", 0, "author"]
}
],
"data": null
}Handling and monitoring different error scenarios from that response is harder work, and off-the-shelf alerting on HTTP status codes will not see the failure at all. You need GraphQL-aware instrumentation before you go anywhere near production.
With GraphQL you can query exactly what you want whenever you want, and that freedom has security consequences. If a malicious actor submits an expensive nested query to overload your server or database, and your server has no protections in place, you are exposed to denial-of-service (DoS) attacks. The OWASP GraphQL Cheat Sheet is blunt about this: query depth and amount are unlimited by default, so you have to add limits yourself. A REST endpoint has a bounded cost by construction. A GraphQL query does not.
Most comparisons stop at over-fetching. Yet the operational differences are what actually decide the outcome once the API is live and someone is on call for it.
Authentication is much the same in both, a token in a header, validated per request. Authorisation is a different story. In REST, permission checks sit at the endpoint, and you can work out who may call what by reading the route table. In GraphQL, one query can traverse several types in a single request, so permissions have to be enforced per field or per resolver. That is more code, and it is code that has to be complete: a single unguarded field on a nested type will hand out the data the endpoint above it was protecting. OWASP recommends enforcing authorisation on both nodes and edges precisely because this gap is so easy to leave open.
Rate limiting by requests per minute works for REST, where requests cost roughly the same as each other. It does not work for GraphQL, where one request might be trivial and the next might join half your database. GraphQL APIs need query-depth limits, complexity scoring that assigns a cost to each field, and often persisted queries, which allow only a pre-approved set of operations in production. Budget for that work now, rather than discovering it during an incident.
REST versions by URL, /v1/ to /v2/, which is explicit, easy to communicate, and leaves you maintaining both. GraphQL sidesteps versioning: you add fields and deprecate old ones with the @deprecated directive, and clients migrate at their own pace. This is genuinely useful for a schema serving many clients, with one obligation attached: track field usage, or those deprecated fields will never be removed and the schema will grow forever.
Because a GraphQL resolver runs per field, a query for 50 posts with their authors can fire 51 database queries: one for the posts, one per author. This is the N+1 problem, and it is the single most common reason a shiny new GraphQL API turns out slower than the REST API it replaced. The fix is batching, collecting those 50 author lookups into one database call, usually with DataLoader or an equivalent library. It is not optional at any real volume. REST does not escape N+1 either, but the query pattern behind an endpoint is fixed, so it shows up in development rather than under production load.
One thing has changed the shape of this decision since GraphQL's early days: you rarely build one monolithic graph any more. Federation lets multiple teams each own a subgraph, which a gateway composes into a single graph the client queries as though it were one API. If your organisation is large enough that "GraphQL vs REST" is really "how do many teams expose one coherent API," this is the pattern to weigh.
The ecosystem is standardising here too. The GraphQL Foundation's Composite Schema Specification work aims to make multi-service graphs portable across gateways rather than tied to a single vendor's implementation. It is worth naming in any 2026 comparison, because it changes the operational calculus: federation adds a composition and gateway layer to own, on top of everything in the section above.
REST has no direct equivalent. The closest analogue is an API gateway stitching together several services, but without a shared type system across them. If a unified, strongly typed graph across many teams is the goal, that is a point for GraphQL. If each service can stand alone behind its own endpoints, REST keeps things simpler.
GraphQL vs REST is not the whole field. gRPC is a strong option for internal service-to-service traffic, where a binary protocol and generated clients beat human-readable JSON and nothing is calling you from a browser. tRPC is worth a look when both ends of the stack are TypeScript in a single codebase, since it gives you end-to-end type safety without a schema language or a code-generation step. It will not serve a client you do not own, mind you.
The rough division: REST for public and third-party APIs, GraphQL for varied first-party clients, gRPC for internal traffic between services, tRPC for full-stack TypeScript products. Large estates run more than one of these. That is a normal end state, not a failure to standardise.
Two features have no REST equivalent, and both change how teams work rather than how the API performs.
GraphQL uses its own type system to define the schema of an API, with a syntax called the Schema Definition Language (SDL). The schema is a contract between server and client, setting out how a client can access the data.
type Author {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
subtitle: String
date: String!
author: Author!
}
type Query {
posts(limit: Int): [Post!]!
user(id: ID!): Author
}Once that schema exists, front-end and back-end teams can work independently, because the front end can be tested against mock data generated from it. The front end can also read the schema's types, queries and mutations through introspection, the built-in ability to query the schema itself and get back the list of available types and fields. You get type safety on top, which catches type errors early on both sides. The September 2025 spec's Schema Coordinates make that introspected schema addressable in a standard way, which is handy for codegen, registries and AI tooling.
The GraphQL IDE is one of the more useful parts of the toolchain. It takes advantage of the schema's self-documenting nature and removes most of the guesswork from integration work.
Use a current, maintained client such as GraphiQL (maintained by the GraphQL Foundation), Apollo Sandbox or Altair to inspect your schema and run queries and mutations against your API without writing a client first.
A note on tooling currency: the older GraphQL Playground has been retired and removed from Apollo Server 3. It has been unmaintained since 2019 and carries known security vulnerabilities, so prefer GraphiQL, Apollo Sandbox or Altair for anything new.
The technical comparison rarely settles this on its own. What settles it is delivery speed, operational cost, and the risk you are signing up for.
This is the checklist our engineers run in API discovery, before a line of code is written. Answer all five. Three or more pointing the same way is a clear signal.
Answer those honestly and the decision is usually already made for you. The pattern we see most often in mobile products is a GraphQL layer in front of existing REST services, adopted one client at a time rather than as a single cutover.
GraphQL gives you a flexible, declarative development environment, and it fixes real problems that REST leaves sitting in the client's lap. It has a large community, a mature ecosystem, and implementations in several popular languages, including JavaScript, Go and Java. This post covers the ground that matters for a decision. The GraphQL specification goes considerably deeper on the language itself.
Building an API mainly for a mobile application? GraphQL is a reasonable first option, because bandwidth and round trips are what your users feel. Need edge caching, mature monitoring and predictable request costs? REST remains the better default.
So is GraphQL the end of REST? No, of course not. It is not a perfect technology, and it carries drawbacks REST does not. Collapse the whole comparison into one line and it comes to this: GraphQL vs REST is a question about your clients, your team and your operations, never about which technology looks more modern.
No. GraphQL adoption keeps growing, but REST remains the default for public and third-party APIs, and most organisations run both. GraphQL is usually added in front of existing REST services rather than replacing them.
For a client that needs data from several resources, yes, because one request replaces several round trips and the payload carries no unused fields. For a single cacheable resource, REST is usually faster, since an HTTP cache or CDN can serve it without touching your server at all. An unbatched GraphQL API hit by the N+1 problem can be considerably slower than the REST API it replaced.
When your traffic is read-heavy and served from a CDN, when you have one client with a stable data model, when your alerting is built on HTTP status codes, or when the team maintaining the API is small and already stretched.
Yes, and this is the common pattern. A GraphQL layer sits in front of existing REST services and resolves fields by calling them, which lets you migrate one client at a time instead of committing to a cutover.
The server is the smaller part. Schema design, field-level authorisation, batching, caching and the client-side migration are where the effort goes, and the work is only complete when the old endpoints are retired. Scope it per client rather than as one project.
Neither is inherently more secure, but they fail differently. REST has a bounded cost per endpoint and permission checks in one place. GraphQL needs field-level authorisation, query-depth and complexity limits, and usually persisted queries, because a single query can be arbitrarily expensive and can traverse types the caller should not reach. The OWASP GraphQL Cheat Sheet is the standard reference for locking this down.
Federation lets several teams each own a subgraph, which a gateway composes into a single graph the client queries as one API. The Composite Schema Specification is standardising how those graphs compose across gateways. It suits large organisations exposing one coherent API from many services, at the cost of a composition and gateway layer to operate.
The current stable edition of the GraphQL specification is the September 2025 edition, the first full edition since October 2021. It added Schema Coordinates, OneOf input objects and descriptions on executable documents, several of them aimed at codegen tools and LLM or agent tooling.
Choosing between GraphQL and REST for a product you are building? We design and build APIs for web and mobile products, from low-latency platforms like TrustPortal to high-volume consumer products like Game Achievements. We are happy to talk through the trade-offs against your own clients, team and roadmap before you commit to either. Get in touch with our team.

Web developer, commonly found typing null instead of nil. Love exploring the functional style of Javascript.
People who read this post, also found these interesting: