Last modified: January 24, 2026

This article is written in: 🇺🇸

GraphQL

GraphQL is a query language for APIs that allows clients to request exactly the data they need in a single request. It provides a type system to describe data and offers a more efficient, flexible, and powerful alternative to traditional REST-based architectures. These notes explore the fundamentals of GraphQL, including its anatomy, how it processes queries, common best practices, and example commands illustrating how to interact with a GraphQL server. ASCII diagrams and code blocks provide additional clarity.

What is GraphQL?

GraphQL is a specification and set of tools that enable clients to define the structure of the data required and receive precisely that data from a server. It was initially developed by Facebook to improve the efficiency and flexibility of data fetching in mobile apps, but it has since been widely adopted in various contexts.

The diagram below offers a conceptual view of how a GraphQL client communicates with a GraphQL server, which in turn interacts with one or more data sources (databases, microservices, etc.):

+------------------------+                  +------------------------+
|                        |                  |                        |
|    GraphQL Client      |                  |    GraphQL Server      |
| (Web, Mobile, etc.)    |                  |  with Schema & Resolvers|
+------------------------+                  +-----------+------------+
         | 1. Sends Query or Mutation                  |
         |-------------------------------------------->|
         |                     2. Receives Query       |
         |                        Parses & Validates    |
         |                        Resolves Fields       |
         |                        Fetches Data          |
         |                        from Data Sources     |
         |                     3. Gathers Results       |
         |<--------------------------------------------|
         | 4. Receives Response (Precisely the data needed)
         |
+------------------------+                   
|                        |                   
|  Renders or Processes  |                   
|  the Returned Data     |                   
|                        |                   
+------------------------+

The client sends a query or mutation describing the shape of the data it wants, the server resolves that request by retrieving information from data sources, and it sends back exactly what was requested.

Core Concepts of GraphQL

GraphQL centers around three key concepts: the schema, the query language itself, and resolvers. A schema defines the data types and relationships, the query language allows clients to specify what data they need, and resolvers act as functions that map these requests to actual data.

Schema Definition Language (SDL)

Schemas in GraphQL are described using a human-readable syntax called the Schema Definition Language. A schema outlines the types, fields, and relationships. For instance:

type Book {
  id: ID!
  title: String!
  author: Author!
}

type Author {
  id: ID!
  name: String!
  books: [Book!]!
}

type Query {
  book(id: ID!): Book
  books: [Book!]!
  author(id: ID!): Author
}

type Mutation {
  addBook(title: String!, authorId: ID!): Book!
}

This example schema defines a Book type with an id, title, and an author. It also defines a Query type for retrieving books or authors, and a Mutation type for adding new books.

Queries, Mutations, and Subscriptions

GraphQL offers three primary operations:

Resolvers

Resolvers are functions that map the fields in a query to actual backend data. A resolver for Query.book might look like this in JavaScript:

const resolvers = {
  Query: {
    book: (parent, args, context, info) => {
      // parent is the result of the previous resolver in the chain
      // args contains the arguments passed in the query
      // context might hold things like database connections or user info
      // info provides query execution details
      return context.db.getBookById(args.id);
    },
    books: (parent, args, context) => {
      return context.db.getAllBooks();
    }
  },
  Mutation: {
    addBook: (parent, args, context) => {
      return context.db.createBook(args.title, args.authorId);
    }
  },
  Book: {
    author: (parent, args, context) => {
      return context.db.getAuthorById(parent.authorId);
    }
  }
};

Resolvers can also include logic for caching or transformations. They are where much of the server-side computation and fetching takes place.

GraphQL vs REST

GraphQL and REST address similar problems but approach them differently:

A key performance aspect of GraphQL is reducing “over-fetching” and “under-fetching.” Over-fetching happens when a REST endpoint returns unneeded fields, and under-fetching means a client must call multiple endpoints to get all the data it needs.

Query Execution and Performance

A simplistic mathematical way to describe potential benefits is to note that REST might require N endpoints to fulfill a complex request, whereas GraphQL can potentially achieve the same result in a single request. If T_rest denotes the total time for multiple requests and T_graph denotes the single GraphQL request time, and if T_graph < T_rest, then GraphQL can offer performance improvements in network-limited scenarios.

Best Practices for GraphQL

There are several considerations for designing and deploying a GraphQL API:

Working with GraphQL from the Client Side

Clients can query a GraphQL server over an HTTP POST request with a JSON body. The table below highlights a few common fields often included in the request:

Field Description
query The GraphQL query or mutation string
variables A JSON object holding variable values for the query
operationName Identifies which named operation in the request should execute

Example Commands with curl

Below are some practical examples of how to interact with a GraphQL API using curl. Each example command is followed by an example response and a brief interpretation.

1) Query Single Book

CODE_BLOCK_PLACEHOLDER - Example output:

CODE_BLOCK_PLACEHOLDER - Interpretation of the output:
The server returned a JSON object with the requested book data, including the author’s name. The book field matches the query structure.

2) Query List of Books

CODE_BLOCK_PLACEHOLDER - Example output:

CODE_BLOCK_PLACEHOLDER - Interpretation of the output:
The server responded with an array of books, each containing the id and title. No other fields are included because they were not requested.

3) Mutation: Add a Book

CODE_BLOCK_PLACEHOLDER - Example output:

CODE_BLOCK_PLACEHOLDER - Interpretation of the output:
A new book was successfully created, returning the book’s id, title, and its associated author object.

Subscriptions Example

Subscriptions enable real-time data. While curl isn’t typically used for real-time operations, the concept is that a client subscribes to an event, like new books being added:

curl -X POST -H "Content-Type: application/json" \
-d '{
  "query": "query GetBook($id: ID!) { book(id: $id) { id title author { name } } }",
  "variables": {"id": "1"}
}' \
http://example.com/graphql

Whenever a new book is added, the client receives a push notification with the new book data.