What Are the Three Types of Operations in GraphQL?
October 29, 2025
Key Takeaways
- GraphQL has three operation types: 
Query(to read data),Mutation(to write data), andSubscription(to receive real-time data). - Queries are for fetching data. They allow clients to request exactly the data they need, including nested relationships, in a single round trip, preventing the over-fetching and under-fetching common in REST APIs.
 - Mutations are for modifying data. Any operation that creates, updates, or deletes data is a mutation. They are executed serially to ensure data integrity and can return the newly modified data in the response.
 - Subscriptions are for real-time updates. They use a persistent connection (like WebSockets) to push data from the server to the client when a specific event occurs, enabling features like live chat and notifications.
 
GraphQL's Three Pillars: Query, Mutation, and Subscription
In a traditional RESTful architecture, the type of operation is determined by the HTTP method (GET, POST, PUT, DELETE) combined with the URL path. A GET /users/123 fetches a user, while a DELETE /posts/456 removes a post.
GraphQL streamlines this entire process. All communication typically happens over a single endpoint (e.g., /graphql), and it’s the structure of the JSON-like request body itself that defines the action. This structure is built upon three fundamental operation types that cover every possible client-server interaction.
The three GraphQL operation types are:
- Query: For reading and fetching data.
 - Mutation: For writing and modifying data.
 - Subscription: For receiving continuous, real-time data updates from the server.
 
These operations are not arbitrary; they are explicitly defined within the GraphQL schema using three special root types: type Query { ... }, type Mutation { ... }, and type Subscription { ... }. The schema acts as a strongly-typed, unbreakable contract between the client and the server, defining exactly what operations are available.
A simple analogy is a visit to a library:
- A Query is like using the library catalog to look up a book's details (e.g., author, publication date, location). It's a read-only action.
 - A Mutation is like checking out a book or donating a new one. These actions change the state of the library's collection.
 - A Subscription is like asking the librarian to notify you the moment a new book by your favorite author arrives. You establish a "watch," and the librarian pushes information to you when it becomes available.
 
Reading and Writing Data: A Deep Dive into Queries and Mutations
Queries and Mutations are the workhorses of any GraphQL API, handling the vast majority of client requests. Understanding their distinct purposes and behaviors is the first step to mastering GraphQL development.
Queries: Asking for Exactly What You Need
The Query type is the most common and fundamental operation in GraphQL. Its purpose is to provide a declarative and efficient way to read data from the server. It directly solves two of the most significant pain points of traditional REST APIs:
- Over-fetching: REST APIs often return a fixed data structure with more information than the client needs, wasting bandwidth.
 - Under-fetching: REST APIs often require the client to make multiple requests to different endpoints to gather all necessary data (e.g., one call to 
/user/123and then another to/user/123/posts), increasing latency. 
With GraphQL Queries, the client specifies precisely the fields it wants, including data from related objects, and the server returns a JSON object that mirrors the query's shape—all in a single request.
Key Features and Syntax
- Selective Fields: Choose only the data you need.
 - Nested Objects: Traverse relationships between objects in the same query.
 - Arguments: Pass parameters to filter, paginate, or specify the exact resource you need.
 
Code Example: GraphQL Query
Imagine you need to display a user's profile page, which includes their basic info and the titles of their five most recent blog posts.
# This query fetches a specific user by their ID # and includes only their name, email, and the titles of their last 5 posts. query GetUserProfile { user(id: "a7d8-4f5e-b9b0") { id name email profile { bio avatarUrl } posts(options: { sort: "DESC", limit: 5 }) { id title createdAt } } }
On the server, each field in this query (user, id, name, posts, etc.) maps to a "resolver" function. The GraphQL engine executes these resolvers to fetch the data from a database or another service. A key performance benefit is that resolvers for different fields at the same level (like name and email) can often be executed in parallel, making data fetching highly efficient.
Mutations: The Structured Way to Change Data
While Queries are for reading data, Mutations are for writing it. Any operation that creates, updates, or deletes data (CUD in CRUD) should be implemented as a Mutation. This explicit separation is a core design principle of GraphQL, making the intent of an operation immediately clear.
Key Differentiator: Serial Execution
The most critical difference from Queries is how they are executed. While fields within a Query can be resolved in parallel, the top-level fields in a Mutation are executed serially (one after another). This is a crucial guarantee for data integrity. If a single GraphQL request contains two mutations, such as updateUsername and logActivity, you are guaranteed that updateUsername will complete before logActivity begins.
Code Example: GraphQL Mutation
A powerful feature of Mutations is that they can also fetch data back in the same operation. After creating a new blog post, you don't need a separate query to get its system-generated id and createdAt timestamp.
# This mutation creates a new post using variables # and, upon success, returns the new post's system-generated ID, # its title, and its creation timestamp. mutation CreateNewPost($title: String!, $content: String!, $authorId: ID!) { createPost(input: {title: $title, content: $content, authorId: $authorId}) { post { id title createdAt author { id name } } success errors { message } } }
In this common pattern, the mutation payload returns the newly created post object, a success boolean, and an array of errors, providing a robust and informative response to the client.
Real-Time Power: How Subscriptions Push Data to Clients
While Queries pull data on demand, Subscriptions push data when it changes. They are the key to building event-driven, real-time applications within the GraphQL paradigm. They are perfect for features where a client needs to be notified by the server of an event without repeatedly polling for changes.
Common use cases include:
- Live Chat: Receiving new messages in a chat room instantly.
 - Real-time Notifications: Alerting a user when someone comments on their post.
 - Live Dashboards: Watching analytics or system metrics update in real-time.
 - Collaborative Apps: Seeing a colleague's edits to a shared document as they happen.
 
How Subscriptions Work
Subscriptions move beyond the simple request/response model of Queries and Mutations. They maintain a persistent, stateful connection between the client and server, typically using WebSockets.
The workflow is as follows:
- Connection: The client initiates a WebSocket connection with the GraphQL server.
 - Subscription Query: The client sends a 
subscriptionoperation over this connection. This query defines the event it’s listening for and the data shape it wants to receive when the event occurs. - Server Listens: On the server, the subscription resolver doesn't return data immediately. Instead, it uses a publish/subscribe (Pub/Sub) mechanism (like Redis Pub/Sub or an in-memory event emitter) to subscribe the long-lived connection to a specific event "topic" or "channel."
 - Event Trigger: A separate event, almost always a Mutation, occurs. For example, a user executes a 
createMessagemutation. - Data Push: The mutation resolver, after successfully writing to the database, publishes the new data payload to the relevant topic in the Pub/Sub system. The Pub/Sub system then finds all clients subscribed to that topic and pushes the data to them over their respective WebSocket connections.
 
sequenceDiagram
    participant Client
    participant GraphQL Server
    participant Pub/Sub System
    participant Database
    Client->>+GraphQL Server: Establishes WebSocket & sends 'subscription onNewMessage'
    GraphQL Server->>+Pub/Sub System: Subscribes client connection to 'NEW_MESSAGE' channel
    Pub/Sub System-->>-GraphQL Server: Acknowledges subscription
    GraphQL Server-->>-Client: Confirms subscription is active
    Note over Client, Database: Connection is idle, waiting for server-pushed events.
    Client->>+GraphQL Server: User A executes 'createMessage' Mutation
    GraphQL Server->>+Database: Writes new message to DB
    Database-->>-GraphQL Server: Confirms write success
    GraphQL Server->>+Pub/Sub System: Publishes new message payload to 'NEW_MESSAGE' channel
    Pub/Sub System->>GraphQL Server: Pushes payload to its listeners
    GraphQL Server-->>-Client: Pushes new message data to User B over WebSocket
Code Example: GraphQL Subscription
Here is what a client would send to listen for new messages in a specific chat room.
# This subscription listens for new messages in a specific chat room. # Every time a new message is created in that room, the server # will push the message's id, content, and author's name to the client. subscription OnNewMessageInRoom($roomId: ID!) { newMessage(roomId: $roomId) { id content createdAt author { id name } } }
Whenever a createMessage mutation successfully adds a message to the specified room, every client subscribed to this event will instantly receive the new message payload.
Conclusion: Choosing the Right Operation for the Job
Mastering GraphQL's three operation types is fundamental to leveraging the full power of the technology. They provide a complete and expressive vocabulary for building everything from simple data-driven websites to complex, collaborative, real-time applications.
To summarize their roles:
- Use Queries for efficient, side-effect-free data retrieval.
 - Use Mutations for predictable, structured, and serial data modification.
 - Use Subscriptions for event-driven, real-time data pushes from server to client.
 
However, the power and flexibility of a single GraphQL endpoint also present unique security and management challenges. A sophisticated API Gateway like the open-source Apache APISIX is essential for any production-grade GraphQL API. By parsing GraphQL operations, a gateway can provide deep, contextual control that a simple reverse proxy cannot, such as:
- Fine-grained Security: Apply different authentication or authorization policies based on whether an operation is a Query or Mutation, or even block specific fields.
 - Denial-of-Service Protection: Shield your backend from malicious or poorly formed requests using query depth limiting and complexity analysis.
 - Performance Optimization: Intelligently cache responses for common, non-sensitive queries to reduce load on your servers.
 
