5+ years software engineer
5+ years software engineer
5+ years software engineer
5+ years software engineer
Use REST by default. Reach for GraphQL only when your clients' data needs clearly justify the extra backend complexity. That's not a hedge — it's the actual industry reality. Most production systems still default to REST because it's simpler to build, cheaper to operate, and easier to scale and debug. GraphQL is genuinely powerful, but you pay for that power with complexity you now own and maintain forever. This is one decision inside the larger tech stack decision; here's how I make it.
Everything else follows from one architectural distinction:
/users, /orders/123), each returning a fixed shape, riding directly on HTTP verbs and status codes./graphql) and lets the client ask for exactly the fields it wants, nested however it needs, in one request.Concretely, imagine a screen that needs a user, their recent orders, and product recommendations. With REST that's often three round-trips:
http1GET /users/42 2GET /users/42/orders?limit=5 3GET /users/42/recommendations
With GraphQL it's one request, shaped by the client:
graphql1query DashboardData($id: ID!) { 2 user(id: $id) { 3 name 4 orders(limit: 5) { id total status } 5 recommendations { id title price } 6 } 7}
So the real trade is REST = simplicity plus HTTP infrastructure for free, versus GraphQL = flexible, client-shaped fetching plus fewer round-trips. Keep that framing and the decision gets easy.
Lean REST, hard, if any of these describe you:
The underrated point: REST integrates naturally with the entire HTTP toolchain. A GET /products/123 can be cached at the CDN edge with a single header —
http1Cache-Control: public, max-age=3600
— and rate limiting, conditional requests (ETag, If-None-Match), and observability tooling all understand it out of the box. GraphQL gets none of that automatically, because to a cache every request is an opaque POST /graphql. You rebuild that infrastructure yourself. On the e-commerce and plugin backends I've shipped — Lightfunnels, the Lumin AI plugins — REST covered the overwhelming majority of endpoints without complaint, and free HTTP caching was a real performance win, not a footnote.
GraphQL starts making real sense when:
The mobile dashboard above is the canonical case: four REST round-trips on a flaky mobile connection, or one GraphQL query returning exactly what the screen renders. When you have several clients each wanting a different shape, that flexibility stops being a luxury and starts saving real coordination cost between frontend and backend teams.
This is where teams get surprised, so be honest before you commit:
ts1// Without batching: resolving `customer` for each order = N+1 queries 2// With DataLoader: all customer lookups in one tick are batched into ONE query 3const customerLoader = new DataLoader(async (ids: readonly string[]) => { 4 const rows = await db.customer.findMany({ where: { id: { in: [...ids] } } }); 5 return ids.map((id) => rows.find((r) => r.id === id)); 6}); 7 8const resolvers = { 9 Order: { 10 customer: (order) => customerLoader.load(order.customerId), 11 }, 12};
In practice, GraphQL doesn't remove complexity — it moves it from the frontend to the backend. That's sometimes a great trade. Just make it on purpose, not because GraphQL is trendy.
Start with REST unless you can clearly answer yes to at least one of these:
If none are true, REST is almost always right. And remember it's not binary: a very common modern setup uses REST for public and CRUD endpoints, with GraphQL as a backend-for-frontend (BFF) aggregation layer only where the UI's shape justifies it.
| REST | GraphQL | |
|---|---|---|
| Endpoints | Many, fixed responses | One, client-shaped responses |
| Caching | Free via HTTP / CDN | Manual, application-level |
| Round-trips for nested data | Often several | One query |
| Best for | CRUD, single client, simple ops | Multiple clients, nested data |
| Main risk | Endpoint sprawl, over/under-fetching | N+1, query-cost attacks, backend complexity |
| Debugging | curl / Postman / logs | Specialized tooling (GraphiQL, Apollo) |
| Learning curve | Low | Moderate to high |
Whichever you choose, it's one column of a bigger decision — see the full tech stack guide, and pair this with your backend choice, since where GraphQL's complexity lands depends heavily on whether you're running Node or Django.
An opinionated, experience-backed framework for choosing your frontend, backend, database, and API — from a freelance full-stack developer who ships real apps.
If your frontend is React, choose Node.js; if you're Python-first or data-heavy, choose Django. A developer's situational guide with a decision table.
For most people, start with React — bigger ecosystem, more jobs, and it carries into React Native. When Angular wins, plus a decision table and skills-transfer breakdown.