GraphQL
By the Syncfusion DevTutorials team · Last updated
Let the client ask for exactly the fields it needs, in one request, with a GraphQL query.
GraphQL is an API style where the client sends a single query describing exactly the data it wants, and the server returns exactly that data. Instead of calling multiple endpoints and receiving fixed response shapes, the client chooses the fields it needs for a particular screen. This helps reduce extra network requests and avoids sending data that is never used.
Watch the Home Screen Make Four Trips
The Brew & Bytes home screen needs:
- Customer name
- Loyalty points
- Recent order totals
- Featured drink
With a traditional REST API, those values come from multiple endpoints. Press Run.
Program.cs — four REST calls
Results:
- Four separate HTTP requests are sent.
- Four network trips are required.
- The screen uses only a few values from each response.
The responses also contain additional fields:
email
phone
address
imageUrl
calories
placedAt
The screen never uses them. Sending data that the client does not need is called over-fetching.
In simple words: REST returns fixed responses. GraphQL allows the client to request exactly the fields it needs.
TL;DR
- GraphQL usually exposes a single endpoint: POST /graphql
- Queries specify exactly which fields should be returned.
- Responses match the shape of the query.
- A schema defines available types and fields.
- A resolver provides the value for a field.
- Queries read data.
- Mutations change data.
- GraphQL can suffer from the N+1 problem.
- Use batching (DataLoader) to solve N+1.
- GraphQL errors often return: 200 OK with an
errorscollection. - Apply query depth and cost limits to prevent abuse.
A Real-World Example
Imagine a waiter serving a table. A poor experience looks like this:
- Ask for coffee.
- Wait.
- Ask for water.
- Wait.
- Ask for dessert.
- Wait.
Many trips are required. A better waiter collects the entire order at once and brings back exactly what everyone requested. GraphQL works similarly. The client describes all required data in one request, and the server returns everything together.
GraphQL combines multiple data needs into a single request and returns only requested fields.
What You Will Learn
In this lesson, you will learn:
- What over-fetching is.
- How GraphQL queries work.
- What schemas and resolvers do.
- How mutations modify data.
- How batching solves N+1 query problems.
- How GraphQL reports errors.
- Why query limits are important.
One Query, One Trip
The Brew & Bytes home screen can be loaded with a single GraphQL query:
{
customer(id: 7) {
name
points
orders {
id
total
}
}
featured {
name
price
}
}
Query Components
Field with argument:
customer(id: 7)
Selection set:
{
name
points
}
Nested selection set:
orders {
id
total
}
Program.cs — one GraphQL query
Benefits:
- One network request.
- One response.
- Only requested fields are returned.
The response shape mirrors the query structure:
{
"customer": {
"name": "Asha Rao",
"points": 120,
"orders": [...]
},
"featured": {
"name": "Cold Coffee",
"price": 180
}
}
Notice what does not appear:
email
phone
imageUrl
calories
because they were never requested.
The structure of the query determines the structure of the response.
Real GraphQL servers are built with frameworks such as: Hot Chocolate (.NET), GraphQL Yoga (TypeScript), Apollo Server (TypeScript). The examples in this lesson use a tiny educational GraphQL engine to demonstrate the concepts.
Schema and Resolvers
A GraphQL server exposes a schema. The schema defines:
- Available types
- Fields
- Arguments
- Mutations
Example:
type Query {
customer(id: Int!): Customer
featured: MenuItem
}
type Customer {
id: Int!
name: String!
points: Int!
orders: [Order!]!
}
Understanding the Symbols
Required integer:
Int!
Required string:
String!
Required list of orders:
[Order!]!
What Is a Resolver?
Each field has a small function called a resolver.
Example:
customer → Load customer
name → Return customer name
orders → Return customer orders
points → Return loyalty points
Resolvers execute only for fields that appear in the query.
If a client requests:
{
customer(id: 7) {
name
}
}
then the server does not need to compute:
points
orders
email
because they were never requested.
The N+1 Problem
GraphQL makes nested queries easy, but it can unintentionally create excessive database activity.
Consider:
{
orders {
id
items {
name
}
}
}
Execution:
- Load all orders.
- Load items for order 1.
- Load items for order 2.
- Load items for order 3.
Database queries: 1 + N
This is called the N+1 problem.
Program.cs — N+1 vs batching
Without Batching
X-Db-Queries: 4
With Batching
X-Db-Queries: 2
Execution:
- Load all orders.
- Load all items in a single query.
The response remains identical.
The database work becomes dramatically smaller.
Use a DataLoader whenever a resolver appears inside a collection. Popular GraphQL frameworks include built-in support for batching and DataLoader patterns.
Change Data with Mutations
Queries retrieve data. Mutations change data. Example mutation:
mutation {
placeOrder(
item: "Latte"
quantity: 2
) {
id
total
}
}
Program.cs — a mutation
The mutation:
- Creates a new order.
- Calculates the total.
- Saves the order.
- Returns requested fields.
Response:
{
"placeOrder": {
"id": 103,
"total": 440
}
}
The client chooses which fields to receive.
For example:
{
id
}
or:
{
id
total
}
or:
{
id
total
item
}
Errors and Depth Limits
GraphQL handles errors differently from REST. A request may return: 200 OK while still containing errors.
Program.cs — errors and depth limit
Invalid Field
Request:
{
customer(id: 7) {
email
}
}
Response:
{
"errors": [
{
"message": "Cannot query field \"email\"..."
}
]
}
The HTTP status remains:
200 OK
Important Rule
Always check:
"errors"
not just the HTTP status code.
A GraphQL response with status 200 is not automatically successful. Check the errors property on every response.
Why Depth Limits Matter
Objects can reference each other.
Example:
Customer
└─ Orders
└─ Customer
└─ Orders
└─ Customer
A malicious user could create extremely deep queries:
customer {
orders {
customer {
orders {
customer {
orders {
...
}
}
}
}
}
}
These queries consume significant resources. A depth limit prevents this. Example rule:
Maximum depth: 3
Allowed:
customer {
orders {
id
}
}
Rejected:
customer {
orders {
customer {
orders {
id
}
}
}
}
Many production GraphQL servers also implement:
- Cost limits
- Complexity analysis
- Query size limits
REST vs GraphQL
| Feature | REST | GraphQL |
|---|---|---|
| Endpoints | Multiple endpoints | Usually one endpoint |
| Response Shape | Defined by server | Defined by client |
| Over-fetching | Common | Rare |
| Under-fetching | Possible | Rare |
| Multiple Requests | Often needed | Usually fewer |
| Caching | Simple HTTP caching | More complex |
| Learning Curve | Lower | Higher |
When GraphQL Works Well
GraphQL is often a good choice when:
- Mobile apps need small responses.
- Different screens need different fields.
- Data comes from many sources.
- A page requires several related resources.
When REST Works Well
REST is often better when:
- APIs are public.
- HTTP caching is important.
- Resources map naturally to URLs.
- Simplicity matters more than flexibility.
Where You See This
- GitHub GraphQL API retrieves repositories, issues, pull requests, and users with a single query.
- Shopify provides GraphQL APIs for products, customers, and orders.
- Mobile applications use GraphQL to reduce network traffic.
- Many .NET teams use Hot Chocolate.
- Many TypeScript teams use GraphQL Yoga or Apollo Server.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
Cannot query field "x" | The field is not defined in the schema | Add the field or correct the spelling |
| Empty screen with no visible error | The application ignores GraphQL errors | Always inspect the errors collection |
| High database load on list pages | N+1 query problem | Use batching and DataLoader |
| Slow server under heavy load | Deep or expensive queries | Add depth and complexity limits |
| Unexpected null values | Resolver failures | Handle errors and validate resolver logic |
| Large responses return slowly | Too many fields are requested | Request only the fields actually needed |
Exercises
What to Remember
- GraphQL has one endpoint. The client sends a query and gets back exactly the fields that it asked for.
- The reply has the same shape as the query.
- Each field has a resolver. Queries read data, and mutations change data.
- Fields inside lists cause N+1 queries. Batch them with a DataLoader.
- Errors come back in an
errorslist, often with status200. Always check it. - Limit the depth of queries. In real projects, use Hot Chocolate, GraphQL Yoga or Apollo, not a home-made engine.
Next: gRPC. You will see how services talk to each other fast, with a strict contract and binary messages.