Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

REST vs GraphQL: An Honest Comparison From a Laravel Developer

About Post

Every few months someone declares REST dead and GraphQL the future. Every few months someone else writes a post about ripping GraphQL out and going back to REST. Both groups are usually right about their own project, and wrong about yours.

I build APIs in Laravel for web portals and mobile apps, and I've watched teams pick each one for the wrong reasons. So here's an honest comparison: what each one really solves, what it quietly costs, and how to choose.

The problem GraphQL was built to solve

Imagine a tenant app's home screen. It needs the current contract's end date, the tenant's name, and the last three payments. With a typical REST API, that might be:

GET /api/contracts/42           -> returns 30 fields, you need 1
GET /api/tenants/7              -> returns 25 fields, you need 1
GET /api/contracts/42/payments  -> returns all of them, you need 3

That's both classic REST complaints in one screen:

  • Over-fetching: you download far more data than you use. On a weak mobile connection, that matters.
  • Under-fetching: one screen needs several round trips, often one after another.

With GraphQL, the client asks for exactly the shape it wants, in one request:

query {
  contract(id: 42) {
    endDate
    tenant { name }
    payments(last: 3) { amount paidAt }
  }
}

The response mirrors the query. No extra fields, no extra trips. When you have many different clients with different needs, that's genuinely powerful.

What GraphQL costs you

Here's the part the conference talks skip.

Caching gets harder

REST works with HTTP caching out of the box. GET /api/units/12 is a URL: browsers, CDNs and proxies all know how to cache it, with ETag and Cache-Control doing the rest.

GraphQL usually sends every query as a POST to one endpoint. To HTTP, every request looks the same, so that free caching layer disappears. You can get some back with persisted queries sent over GET, or client-side caches like Apollo's, but it's work you didn't have to do before.

N+1 queries move into your resolvers

Ask for 50 contracts with their tenant's name, and a naive resolver runs one query for the contracts and then one per contract for the tenant. 51 queries. It's the same N+1 problem we fight in Eloquent, but now the client decides the shape of the query, so you can't just add one with() in a controller.

The fix is batching, usually with the DataLoader pattern: collect all the tenant IDs requested in one pass, then load them in a single query. In Laravel, the Lighthouse package batches relationship loading for its relation directives, which helps a lot. But you have to know it's there and keep custom resolvers in line.

Security needs new thinking

With REST, each endpoint has a known cost. With GraphQL, a client can write a deeply nested query that asks for contracts, their tenants, their contracts, their payments, and so on. You need query depth and complexity limits, and authorization on fields and types, not just on routes.

More moving parts

A schema, resolvers, a GraphQL server library, client tooling, and a team that understands all of it. Error handling is different too: a GraphQL response often comes back as HTTP 200 with an errors array inside, which surprises monitoring tools that only look at status codes.

Side by side

RESTGraphQL
Data shapeDecided by the server per endpointDecided by the client per query
Round trips per screenOften severalUsually one
HTTP cachingBuilt inNeeds extra work
N+1 riskIn controllers, easy to spotIn resolvers, needs batching
Typed contractOptional (OpenAPI)Built in (the schema)
Learning curveLowMedium
Laravel supportFirst-class (routes, API resources)Via packages like Lighthouse

REST can fix most of its own problems

Before switching paradigms, notice that the REST complaints have REST answers:

  • Over-fetching: API resources that return only what clients use, or sparse fieldsets like ?fields=id,name.
  • Under-fetching: an ?include=tenant,payments parameter backed by eager loading, or a purpose-built endpoint for an important screen (sometimes called a backend-for-frontend).
  • Typed contracts: an OpenAPI spec, which also gives you generated docs and clients.

A dedicated GET /api/tenant/home endpoint that returns exactly what the home screen needs is not a hack. It's often the simplest, fastest and most cacheable answer.

My default: REST, with good resources and an OpenAPI spec, until I can name the specific problem GraphQL would solve for this project. "It's more modern" is not a problem.

When GraphQL earns its place

  • Many different clients (web, several mobile apps, partners) need very different slices of the same data.
  • Frontend teams are blocked waiting for backend changes to every endpoint.
  • The data is a rich graph that clients explore in unpredictable ways.
  • You're aggregating several backend services behind one API.

When REST is enough (which is often)

  • One web app and one mobile app, built by the same team.
  • Mostly CRUD with a few well-known screens.
  • Public or cache-heavy data where HTTP caching saves real money.
  • A small team that would rather ship features than maintain a schema layer.

Neither choice is permanent, either. Plenty of teams run REST for most things and add GraphQL for the one area where clients really need flexibility.

Which one is your team using right now, and if you could choose again from scratch, would you pick the same?

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close