How GraphQL Works - The New Standard for Flexible API Design

15 min read | 2025.12.20

What is GraphQL?

GraphQL is a query language and runtime for APIs, released by Facebook (now Meta) in 2015. Its main feature is that clients can specify exactly what data they need and receive responses with precisely that data, nothing more, nothing less.

Today, many large-scale services including GitHub, Shopify, Twitter (X), and Netflix have adopted GraphQL.

Why was it created: Facebook developed it to solve the problem of “needing to call multiple endpoints with REST APIs, increasing network traffic” that they faced during mobile app development.

GraphQL Fundamentals

Query

Operations for fetching data.

# Fetch user information and posts at once
query {
  user(id: "123") {
    name
    email
    posts {
      title
      createdAt
    }
  }
}

Mutation

Operations for modifying data.

# Create a new post
mutation {
  createPost(input: { title: "Hello", content: "World" }) {
    id
    title
  }
}

Subscription

Operations for subscribing to data changes in real-time.

# Receive new messages in real-time
subscription {
  newMessage(roomId: "456") {
    content
    sender {
      name
    }
  }
}

Schema Definition

In GraphQL, the API shape is defined through a schema.

# Type definitions
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  createdAt: DateTime!
}

# Query and Mutation definitions
type Query {
  user(id: ID!): User
  posts(limit: Int): [Post!]!
}

type Mutation {
  createPost(input: CreatePostInput!): Post!
}

input CreatePostInput {
  title: String!
  content: String!
}

Scalar types: ID, String, Int, Float, and Boolean are built-in types. ! means non-null.

Comparison with REST API

AspectREST APIGraphQL
EndpointsMultiple per resourceSingle (/graphql)
Data fetchingServer decidesClient specifies
Over-fetchingLikely to occurDoes not occur
Under-fetchingLikely to occurDoes not occur
VersioningManaged via URL or headersHandled through schema evolution
CachingHTTP caching is easyRequires dedicated mechanisms

Over-fetching and Under-fetching

REST API case:
GET /users/123         → User info (including unnecessary items)
GET /users/123/posts   → Post list (requires separate request)

GraphQL case:
query {
  user(id: "123") {
    name              # Only required items
    posts { title }   # Fetched in one request
  }
}

How Resolvers Work

Resolvers are functions that transform GraphQL queries into actual data.

const resolvers = {
  Query: {
    user: async (parent, args, context) => {
      return await context.db.users.findById(args.id);
    },
    posts: async (parent, args, context) => {
      return await context.db.posts.findMany({ take: args.limit });
    },
  },
  User: {
    posts: async (parent, args, context) => {
      return await context.db.posts.findMany({
        where: { authorId: parent.id }
      });
    },
  },
};

N+1 Problem: When resolving nested fields, database queries can multiply rapidly. Address this with batching and caching mechanisms like DataLoader.

Benefits and Considerations of GraphQL

Benefits

  • Efficient data fetching: Get only the data you need in a single request
  • Type safety: Strict type definitions through schema
  • Developer experience: Auto-generated documentation through introspection
  • Easy evolution: Adding fields doesn’t affect existing clients

Considerations

  • HTTP caching is difficult: POST requests are the default
  • Complex query control: Protection against malicious queries is needed
  • Learning cost: New concepts must be learned
  • File uploads: Not included in the standard specification

Summary

GraphQL is an API design pattern that enables client-driven data fetching optimization. It’s not a replacement for REST APIs, but should be chosen based on use cases. It’s particularly effective for applications with complex data relationships or when supporting diverse clients.

← Back to list