Waqas Ahmad — Software Architect & Technical Consultant - Available USA, Europe, Global

Waqas Ahmad — Software Architect & Technical Consultant

Specializing in

Distributed Systems

.NET ArchitectureCloud-Native ArchitectureAzure Cloud EngineeringAPI ArchitectureMicroservices ArchitectureEvent-Driven ArchitectureDatabase Design & Optimization

👋 Hi, I'm Waqas — a Software Architect and Technical Consultant specializing in .NET, Azure, microservices, and API-first system design..
I help companies build reliable, maintainable, and high-performance backend platforms that scale.

Experienced across engineering ecosystems shaped by Microsoft, the Cloud Native Computing Foundation, and the Apache Software Foundation.

Available for remote consulting (USA, Europe, Global) — flexible across EST, PST, GMT & CET.

services
Article

REST vs GraphQL for APIs: When to Choose Which

REST vs GraphQL: over-fetching, versioning, and when GraphQL pays off.

services
Read the article

Introduction

This guidance is relevant when the topic of this article applies to your system or design choices; it breaks down when constraints or context differ. I’ve applied it in real projects and refined the takeaways over time (as of 2026).

REST is the default for APIs—simple, cacheable, and widely understood—but fixed response shapes can lead to over-fetching or many endpoints when clients have different data needs. This article compares REST and GraphQL in depth: what each is, strengths and weaknesses, when to choose which, and how to implement both in .NET. For architects and tech leads, starting with REST and adding GraphQL only when multiple clients and varying data needs justify it keeps complexity under control.

For a deeper overview of this topic, explore the full API Architecture guide.

Decision Context

  • System scale: APIs serving one or many clients (web, mobile, partners); from a few resources to many. Applies when you’re choosing between REST and GraphQL for a new or evolving API.
  • Team size: Backend and front-end; someone must own the API contract and (for GraphQL) the schema and resolvers. Works when the team can agree on resource design (REST) or schema design (GraphQL).
  • Time / budget pressure: Fits greenfield and “we have many clients with different data needs”; breaks down when the team has no GraphQL experience and must ship quickly—then REST is the safer default.
  • Technical constraints: .NET (ASP.NET Core); REST with JSON or GraphQL (HotChocolate, etc.). Browser and mobile clients; caching and tooling differ between REST and GraphQL.
  • Non-goals: This article does not optimise for gRPC or other styles; it focuses on REST vs GraphQL and when to choose which.

What is a REST API?

REST (Representational State Transfer) is an architectural style for APIs. You expose resources as URLs (e.g. /api/orders, /api/orders/123) and use HTTP methods to operate on them:

Method Purpose Example
GET Read resource(s) GET /api/orders
POST Create resource POST /api/orders
PUT Replace resource PUT /api/orders/123
PATCH Update resource (partial) PATCH /api/orders/123
DELETE Delete resource DELETE /api/orders/123

Responses are typically JSON. The response shape is fixed per endpoint—the client gets whatever fields the server returns.

Key characteristics:

  • Multiple endpoints (one per resource)
  • Fixed response shape per endpoint
  • HTTP caching works well (GET requests)
  • Versioning via URL (/api/v1/orders) or header
  • Widely supported by tools and developers

What is GraphQL?

GraphQL is a query language and runtime for APIs. Instead of multiple endpoints, you have one endpoint (e.g. /graphql). The client sends a query specifying exactly which fields it wants.

# Client query
query {
  order(id: 123) {
    id
    status
    customer {
      name
      email
    }
  }
}
// Server returns exactly what was requested
{
  "data": {
    "order": {
      "id": 123,
      "status": "Shipped",
      "customer": {
        "name": "John Doe",
        "email": "john@example.com"
      }
    }
  }
}

Key characteristics:

  • Single endpoint
  • Client defines the response shape (no over-fetching)
  • Nested queries (get related data in one request)
  • Strongly typed schema
  • Versioning by adding fields; deprecate old fields

REST vs GraphQL: comparison table

Aspect REST GraphQL
Endpoints Multiple (one per resource) Single (/graphql)
Response shape Fixed by server Defined by client
Over-fetching Yes (get all fields) No (get only requested)
Under-fetching Yes (multiple requests) No (nested queries)
Caching HTTP caching works Needs normalised cache
Versioning URL or header Add fields; deprecate
Learning curve Low Medium
Tooling Broad (OpenAPI, Postman) Growing (Apollo, Relay)
N+1 problem Rare Common (use DataLoader)

REST: strengths and weaknesses

Strengths

Simplicity. REST is straightforward: resources as URLs, HTTP methods, JSON. Most developers know it.

HTTP caching. GET requests are cacheable at HTTP level (CDN, browser). Add Cache-Control headers.

Tooling. OpenAPI/Swagger generates docs, client SDKs, and mock servers.

Weaknesses

Over-fetching. The endpoint returns all fields, even if client needs only a few.

Under-fetching. To get related data, client makes multiple requests.

Versioning. Changing shape requires a new version or breaking clients.

GraphQL: strengths and weaknesses

Strengths

No over-fetching. Client requests exactly the fields it needs.

No under-fetching. Nested queries fetch related data in one request.

Strongly typed schema. Schema defines types, queries, mutations. Tools provide autocomplete.

Weaknesses

N+1 problem. Resolvers that hit DB per parent cause N+1. Use DataLoader to batch.

Caching harder. No HTTP cache; use normalised cache (Apollo Client).

Complexity. Must limit query depth and cost to prevent abuse.

Code examples

REST API in ASP.NET Core

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IOrderService _orderService;

    public OrdersController(IOrderService orderService)
        => _orderService = orderService;

    [HttpGet]
    public async Task<ActionResult<IEnumerable<OrderDto>>> GetOrders()
        => Ok(await _orderService.GetAllAsync());

    [HttpGet("{id}")]
    public async Task<ActionResult<OrderDto>> GetOrder(int id)
    {
        var order = await _orderService.GetByIdAsync(id);
        return order == null ? NotFound() : Ok(order);
    }

    [HttpPost]
    public async Task<ActionResult<OrderDto>> CreateOrder(CreateOrderRequest request)
    {
        var order = await _orderService.CreateAsync(request);
        return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
    }
}

GraphQL API with Hot Chocolate

// Query.cs
public class Query
{
    public async Task<Order?> GetOrder(int id, [Service] IOrderService svc)
        => await svc.GetByIdAsync(id);

    public async Task<IEnumerable<Order>> GetOrders([Service] IOrderService svc)
        => await svc.GetAllAsync();
}

// Program.cs
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>();

When to choose REST

Choose REST when:

  • Clients are simple with predictable data needs
  • You want HTTP caching (CDN, browser)
  • Team knows REST well; want broad tooling
  • Simple CRUD operations with fixed shapes

When to choose GraphQL

Choose GraphQL when:

  • Many clients with different data needs
  • Over-fetching is a real problem (mobile on slow networks)
  • Under-fetching causes too many round-trips
  • You can invest in DataLoader and resolver auth

Enterprise best practices

1. REST: use OpenAPI/Swagger. Generate docs, SDKs, and validate requests.

2. GraphQL: limit query depth and cost. Prevent abuse.

3. GraphQL: use DataLoader. Batch database calls to avoid N+1.

4. Both: authorise at the right level. REST: controller. GraphQL: resolver.

5. Both: monitor and trace. Use OpenTelemetry. Track latency and errors.

Common issues

Issue REST GraphQL
Over-fetching Common Solved
Under-fetching Common Solved
N+1 queries Rare Common (use DataLoader)
Caching HTTP works Needs normalised cache
Auth per field No Required

You can also explore more patterns in the API Architecture resource page.

Summary

REST is simple, cacheable, and widely supported—it fits most cases; GraphQL shines when you have many clients with different data needs and want to reduce over-fetching. For most .NET shops, adding GraphQL before that pain is real adds schema and tooling complexity without clear payoff. Next, map your clients and their data shapes; if over-fetching or versioning pain is significant, prototype GraphQL; otherwise stay with REST and refine your resource design.

Position & Rationale

I use REST as the default for most APIs—simple resources, HTTP caching, and broad tooling. I use GraphQL when we have multiple clients (web, mobile, partners) with genuinely different shapes of data and over-fetching or under-fetching is a real pain; then a single endpoint and client-defined queries can pay off. I avoid GraphQL when the team has no experience and the main ask is “modern API”—REST is enough for many cases. I prefer REST first; add GraphQL only when we have a clear need (e.g. mobile needs fewer fields, web needs more, and we don’t want to maintain multiple REST endpoints). I don’t choose GraphQL for “flexibility” alone if we have one client and stable requirements; the complexity (N+1, caching, auth per field) may not be worth it.

Trade-Offs & Failure Modes

REST sacrifices fine-grained client control over response shape; you gain simplicity, HTTP caching, and wide support. GraphQL sacrifices HTTP caching and simple mental model; you gain one endpoint and client-specified fields. N+1 in GraphQL is common without DataLoader; auth per field can get complex. Failure modes: adopting GraphQL without a clear multi-client or over-fetching problem; ignoring N+1 in GraphQL resolvers; no caching strategy for GraphQL.

What Most Guides Miss

Most guides compare REST vs GraphQL on features but don’t stress that client diversity is the main driver—one client and one shape → REST is simpler; many clients with different needs → GraphQL can help. Another gap: N+1 in GraphQL is the first production bug for many teams; use DataLoader or batch resolvers from day one. Caching for GraphQL is harder than REST (no HTTP cache by URL); you need a normalised cache or query-level cache.

Decision Framework

  • If one or few clients, stable resource shape → REST; use resources and HTTP methods; cache by URL.
  • If many clients with different data needs and over/under-fetching is painful → Consider GraphQL; design schema and use DataLoader for N+1.
  • For most .NET teams → Start with REST; add GraphQL only when the pain (multiple endpoints, versioning, client-specific shapes) justifies the complexity.
  • For GraphQL → Use DataLoader (or equivalent) to batch and avoid N+1; plan auth and caching.
  • For REST → Version the API (URL or header); use DTOs and avoid over-exposing.

Key Takeaways

  • REST = default for most APIs; simple, cacheable, widely supported.
  • GraphQL = when many clients need different shapes; one endpoint, client-defined queries; watch for N+1 and caching.
  • Start with REST; add GraphQL when over-fetching or multi-client pain is real.
  • For GraphQL: DataLoader for N+1; plan auth per field and caching.

If you’re building or modernizing API platforms, I provide API architecture consulting covering API-first design, governance, and integration strategy.

When I Would Use This Again — and When I Wouldn’t

I’d use REST again for most APIs—especially when we have one or a few clients and stable resource shapes. I’d use GraphQL again when we have multiple clients with genuinely different data needs and we’re willing to invest in schema, resolvers, DataLoader, and caching. I wouldn’t choose GraphQL “because it’s flexible” without a concrete over-fetching or multi-client problem. I also wouldn’t adopt GraphQL without addressing N+1 from the start (DataLoader or batching).

services
Frequently Asked Questions

Frequently Asked Questions

What is a REST API?

A REST API exposes resources as URLs and uses HTTP methods (GET, POST, PUT, DELETE). Responses are JSON. The shape is fixed per endpoint.

What is GraphQL?

GraphQL is a query language. The client specifies which fields it wants; the server returns only those fields. Single endpoint, no over-fetching.

What is over-fetching?

When the API returns more data than the client needs. REST endpoints return all fields; GraphQL lets client specify.

What is under-fetching?

When the client needs multiple requests to get all data. GraphQL solves this with nested queries.

What is the N+1 problem?

If a resolver fetches data for each parent, you get N+1 database queries. Use DataLoader to batch.

How do I cache GraphQL?

HTTP caching does not work. Use normalised cache (Apollo Client) or persisted queries.

How do I version REST?

URL versioning (/api/v1/orders) or header (Api-Version: 1). Deprecate old versions.

How do I version GraphQL?

Add new fields (non-breaking). Mark old fields as @deprecated. No URL versioning.

Can I use both together?

Yes. REST for simple CRUD, GraphQL for complex queries. Or BFF that calls REST and exposes GraphQL.

Which has better tooling?

REST has broader tooling (OpenAPI, Postman). GraphQL tooling is growing (Apollo, Relay).

Is GraphQL faster?

Not inherently. It can reduce round-trips but each request may be more complex.

When should I not use GraphQL?

When clients are simple, data needs predictable, and you want HTTP caching.

How do I authorise in GraphQL?

At the resolver level. Check permissions before returning data.

What is Hot Chocolate?

A GraphQL server for .NET. Schema-first or code-first, with ASP.NET Core integration.

What is Apollo?

A GraphQL platform with client (React, iOS, Android) and server (Node.js). Caching, dev tools, managed cloud.

services
Related Guides & Resources

services
Related services