GraphQL, developed by Facebook in 2012 and open-sourced in 2015, is a query language for APIs where clients declaratively specify the exact fields they need — eliminating over-fetching and under-fetching endemic to REST. The schema-first approach defines all types, queries, mutations, and subscriptions in SDL (Schema Definition Language), serving as the single source of truth for both server and client. The N+1 problem is GraphQL's primary performance challenge: a query for 100 posts with their authors triggers 100 separate author lookups; DataLoader batches and deduplicates these into a single query.

Key Points

  • GraphQL vs REST: REST returns fixed shapes; GraphQL returns client-specified shapes — one endpoint (/graphql), all operations as POST (except persisted queries via GET).
  • Schema types: Query (read), Mutation (write), Subscription (real-time via WebSocket); Object types, Scalar types (String, Int, Float, Boolean, ID, custom scalars like DateTime, UUID), Enum, Union, Interface.
  • Resolver chain: each field has a resolver function; nested resolvers receive (parent, args, context, info); the GraphQL execution engine calls resolvers depth-first, accumulating the result tree.
  • DataLoader pattern: collects all author_id values during a single request tick, then issues one SELECT WHERE id IN (...) query — batch size is typically bounded at 1000; also deduplicates repeated IDs.
  • Query complexity analysis: depth limits (max 10 levels deep), field count limits (max 200 fields), and custom complexity scoring per resolver prevent malicious queries from overloading backends.
  • Persisted queries: client sends a hash of the query instead of the full query text; server looks up the pre-registered query by hash — reduces payload size 90% and enables query allowlisting for security.
  • Subscriptions deliver real-time updates via WebSocket (graphql-ws protocol) or SSE; each subscription filter must be carefully indexed — a "newMessage where roomId = 42" subscription creates a channel per room.
  • Federation (Apollo Federation 2, GraphQL Mesh): composes multiple subgraph schemas into a supergraph; each service owns its types; the gateway routes fields to the correct subgraph — enables independent team ownership of the schema.

GraphQL schema with N+1 risk and DataLoader solution

# SDL Schema Definition
type Query {
  post(id: ID!): Post
  posts(first: Int, after: String): PostConnection!
}

type Post {
  id: ID!
  title: String!
  author: User!       # triggers N+1 without DataLoader
  comments(first: Int): [Comment!]!
}

# DataLoader pattern (Node.js / TypeScript)
const authorLoader = new DataLoader<string, User>(async (authorIds) => {
  // Called ONCE per request tick with all batched IDs
  const authors = await db.users.findMany({
    where: { id: { in: authorIds as string[] } }
  });
  // Must return in same order as input IDs
  return authorIds.map(id => authors.find(a => a.id === id)!);
});

// Resolver (called N times, but DataLoader batches the DB call)
const resolvers = {
  Post: {
    author: (post, _, { loaders }) =>
      loaders.authorLoader.load(post.authorId),
  },
};

Real-World Example

Facebook uses GraphQL at massive scale for the Facebook app — a single newsfeed query fetches posts, reactions, comments, author profiles, and media from dozens of microservices via their Relay framework. Shopify's Storefront API is 100% GraphQL; their Admin API offers both REST and GraphQL, with GraphQL covering more capabilities and being recommended for new integrations.