DEV Community

丁久
丁久

Posted on • Originally published at dingjiu1989-hue.github.io

GraphQL vs REST API

This article was originally published on AI Study Room. For the full version with working code examples and related articles, visit the original post.

Introduction

GraphQL and REST are the two dominant API design paradigms. REST has been the standard since the early 2000s, while GraphQL emerged from Facebook in 2015 as a solution to REST's limitations in data-intensive applications. Both are widely used in 2026, and the choice depends on your application's data fetching patterns, team expertise, and performance requirements.

Core Philosophy

REST: Resource-Based

REST treats data as resources accessed via endpoints:

GET    /api/users          → List users
GET    /api/users/123      → Get user 123
POST   /api/users          → Create user
PUT    /api/users/123      → Update user 123
DELETE /api/users/123      → Delete user 123
GET    /api/users/123/posts → Get user 123's posts
Enter fullscreen mode Exit fullscreen mode

Each endpoint returns a fixed response structure. The client gets whatever the server decides to send.

GraphQL: Query-Based

GraphQL exposes a single endpoint and lets the client specify exactly what data it needs:

POST /graphql

query {
  user(id: 123) {
    name
    email
    posts(limit: 5) {
      title
      createdAt
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The server returns only the requested fields. No over-fetching, no under-fetching.

Data Fetching

REST often over-fetches (returns unneeded fields) or under-fetches (requires multiple requests):

// REST: three requests to get users + their posts + post comments
const users = await fetch("/api/users").then(r => r.json());
const usersWithPosts = await Promise.all(
  users.map(user => fetch(`/api/users/${user.id}/posts`).then(r => r.json()))
);
// More requests for comments...
Enter fullscreen mode Exit fullscreen mode

GraphQL fetches all required data in a single request:

query {
  users {
    id
    name
    posts {
      title
      comments(limit: 3) {
        body
        author { name }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Caching

REST has straightforward HTTP caching. GET requests are cacheable by browsers, CDNs, and reverse proxies using standard HTTP cache headers (ETag, Cache-Control, Last-Modified). This is REST's strongest advantage for read-heavy public APIs.

GraphQL caching is more complex. POST requests to a single endpoint are not cacheable by default. Solutions include:

  • Automatic Persisted Queries: Cache query strings, send only hash
  • Apollo Client's normalized cache: Client-side cache keyed by type and ID
  • CDN caching: Use GET requests for queries with @cacheControl directives
  • Relay's cache: More opinionated, built for Facebook-scale apps
// Apollo Client normalized cache
const cache = new InMemoryCache({
  typePolicies: {
    User: {
      keyFields: ["id"],
      fields: {
        posts: {
          merge(existing, incoming) {
            return incoming;
          }
        }
      }
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

Type Safety

GraphQL has built-in type safety with a schema definition language:

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
  createdAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String
  published: Boolean!
  author: User!
}

type Query {
  user(id: ID!): User
  users(limit: Int, offset: Int): [User!]!
}
Enter fullscreen mode Exit fullscreen mode

The schema serves as a contract between client and server, with auto-generated documentation (GraphiQL, Apollo Studio).

REST has no built-in type system. Type safety requires tools like OpenAPI/Swagger:

openapi: 3.0.0
paths:
  /api/users/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
Enter fullscreen mode Exit fullscreen mode

OpenAPI provides similar contract guarantees but requires more boilerplate to maintain.

Performance Considerations

REST benefits from:

  • HTTP caching at every level
  • CDN distribution for read endpoints
  • Lightweight parsing (JSON, no query analysis)
  • Connection pooling per endpoint

GraphQL faces performance challenges:

  • N+1 queries: Resolving nested relations requires solutions like DataLoader
  • Query complexity: A malicious client can request expensive nested queries
  • No CDN caching for POST requests (unless using GET-based queries)
// DataLoader prevents N+1 queries in GraphQL
const DataLoader = require("dataloader");

const userLoader = new DataLoader(async (ids) => {
  const users = await db.user.findMany({ where: { id: { in: ids } } });
  return ids.map(id => users.find(u => u.id === id));
});

// In resolver:
posts: (parent) => userLoader.load(parent.authorId)
Enter fullscreen mode Exit fullscreen mode

Tooling and Developer Experience

GraphQL offers superior developer tooling:

  • GraphiQL/Altair: Interactive in-browser query explorer
  • Apollo Studio: Schema registry, operation tracking, performance monitoring
  • Code generation: Typed client SDKs in any language
  • Inline documentation: Field descriptions visible in the query explorer

**RES


Read the full article on AI Study Room for complete code examples, comparison tables, and related resources.

Found this useful? Check out more developer guides and tool comparisons on AI Study Room.

Top comments (0)