GraphQL vs REST: Choosing the Right API Architecture

A practical, code-backed comparison of GraphQL and REST to help you choose the right architecture for your API.

The Core Difference

REST exposes fixed endpoints that return fixed data shapes. GraphQL exposes a single endpoint where clients specify exactly what data they need. This single distinction drives most of the practical trade-offs between them.

REST Example

// Two separate requests, each returning its full fixed shape
GET /api/users/42
GET /api/users/42/posts
{ "id": 42, "name": "Jane", "email": "jane@example.com", "createdAt": "2026-01-01" }

GraphQL Example

query {
  user(id: 42) {
    name
    posts {
      title
      publishedAt
    }
  }
}

One request, and the client gets exactly name and each post’s title/publishedAt — nothing more, nothing less.

Setting Up a Simple GraphQL Server

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Post {
    id: ID!
    title: String!
  }
  type User {
    id: ID!
    name: String!
    posts: [Post!]!
  }
  type Query {
    user(id: ID!): User
  }
`;

const resolvers = {
  Query: {
    user: (_, { id }) => db.users.findById(id),
  },
  User: {
    posts: (user) => db.posts.findByUserId(user.id),
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => console.log(`Server ready at ${url}`));

Where REST Still Wins

  • Simple CRUD APIs where over/under-fetching isn’t a real problem
  • Strong HTTP caching semantics (ETags, Cache-Control) that GraphQL doesn’t get for free
  • Simpler mental model and tooling for small teams and small APIs

Where GraphQL Wins

  • Multiple client types (web, mobile, third-party) with very different data needs from the same backend
  • Complex, deeply nested data where REST would require many round-trips or bespoke endpoints
  • Rapidly evolving frontend requirements, since clients can request new field combinations without backend changes

Common GraphQL Pitfalls

  • N+1 queries in resolvers — solve with a batching library like DataLoader.
  • No built-in caching — requires deliberate client-side caching (Apollo Client, urql) since there’s no single resource URL to cache against.
  • Overly permissive queries — implement query depth/complexity limits to prevent expensive, deeply nested queries from overwhelming your server.

Conclusion

Neither architecture is objectively better — REST remains the simpler default for straightforward APIs, while GraphQL earns its added complexity when you have genuinely varied clients and deeply nested data needs. Many real systems use both: REST for simple resource CRUD, GraphQL for complex, client-driven data fetching.