0

GraphQL vs REST — what problem GraphQL solves

Intermediate5 min read·eng-13-018
interviewcompare

Concept

GraphQL vs REST — two different approaches to API design. REST exposes resources; GraphQL exposes a query language.

The problem GraphQL solves: REST APIs suffer from over-fetching and under-fetching.

  • Over-fetching: GET /users/42 returns 20 fields, but the mobile app only needs name and avatar. Wasted bandwidth.
  • Under-fetching: To show a user's profile with their orders, you need GET /users/42, then GET /users/42/orders (N+1 requests). Multiple round trips.
  • Rigid endpoints: REST endpoints return fixed shapes. You can't ask for a custom subset without building a new endpoint.

What GraphQL is: A query language for APIs. The client specifies exactly what it needs — field by field, nested relationships — in a single request.

GraphQL request: A POST to a single endpoint (/graphql) with a query in the body. The server parses the query, resolves each field via "resolvers," and returns exactly what was asked for.

GraphQL strengths:

  • Client controls the shape of the response.
  • One request can fetch complex, nested data (user + orders + products).
  • Self-documenting via the schema (introspection).
  • Strong typing (schema defines every type).

GraphQL weaknesses:

  • Caching is harder (all requests are POSTs to one URL — CDN can't cache).
  • Complex queries can be expensive (N+1 on resolvers without DataLoader).
  • Steeper learning curve for server developers.
  • File uploads are non-standard.
  • Over-fetching problem moves to the server (clients can ask for anything — must add query depth/complexity limits).

When to use REST: Public APIs, simple CRUD, when caching is important, when you want clear URL semantics. When to use GraphQL: Complex UIs with varying data needs, mobile apps (bandwidth-sensitive), team where frontend and backend move independently.

Code Example

php
<?php
// REST — fixed response shape, multiple round trips for related data
// GET /users/42 → returns user
// GET /users/42/orders → returns orders
// GET /orders/7/items → returns items
// 3 round trips to build one UI component!

// GRAPHQL — one request, client specifies exact shape
// POST /graphql
// Body:
// query {
//   user(id: 42) {
//     name
//     avatar
//     orders(status: "pending") {
//       id
//       total
//       items {
//         productName
//         quantity
//       }
//     }
//   }
// }
// One request — returns exactly what was asked

// Laravel GraphQL with Lighthouse (graphql-lighthouse/lighthouse)
// Schema definition (schema.graphql):
/*
type Query {
    user(id: ID! @eq): User @find
    users: [User!]! @all
}

type User {
    id: ID!
    name: String!
    email: String!
    orders: [Order!]! @hasMany
}

type Order {
    id: ID!
    total: Float!
    status: String!
    items: [OrderItem!]! @hasMany
}

type Mutation {
    createUser(name: String!, email: String!): User @create
    deleteUser(id: ID!): User @delete
}
*/

// PHP resolver (for custom logic)
namespace App\GraphQL\Queries;

class UserResolver
{
    public function resolve($root, array $args): mixed
    {
        return User::findOrFail($args['id']);
    }
}

// N+1 PROBLEM in GraphQL — same as Eloquent, solved with DataLoader
// Without DataLoader: resolving 10 users' orders → 10 queries
// With DataLoader: batches all order lookups → 1 query
// Lighthouse handles this automatically with @hasMany (uses batch loading)

// COMPARISON TABLE:
// REST:
//   Multiple endpoints for different resources
//   Server defines response shape
//   GET /users → easy to cache
//   Multiple requests for related data
//   Simple to implement CRUD

// GraphQL:
//   Single /graphql endpoint
//   Client defines response shape
//   POST /graphql → harder to cache
//   One request for complex nested data
//   Resolver functions for each field type
//   Schema is the contract

// HYBRID APPROACH (common in Laravel):
// Use REST for simple CRUD and public APIs
// Use GraphQL for complex frontend data fetching
// Some teams use both: REST for mutations, GraphQL for queries

// REST vs GraphQL over-fetching example:
// REST: GET /users/42 → {id, name, email, phone, address, avatar, created_at, ...all 20 fields}
// Mobile app uses: name, avatar only — 18 fields wasted

// GraphQL: query { user(id: 42) { name avatar } }
// → {name: "Alice", avatar: "https://..."}  — exactly 2 fields