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 errors collection.
  • 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:

  1. Ask for coffee.
  2. Wait.
  3. Ask for water.
  4. Wait.
  5. Ask for dessert.
  6. 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:

  1. What over-fetching is.
  2. How GraphQL queries work.
  3. What schemas and resolvers do.
  4. How mutations modify data.
  5. How batching solves N+1 query problems.
  6. How GraphQL reports errors.
  7. 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:

  1. Load all orders.
  2. Load items for order 1.
  3. Load items for order 2.
  4. 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:

  1. Load all orders.
  2. 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:

  1. Creates a new order.
  2. Calculates the total.
  3. Saves the order.
  4. 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

FeatureRESTGraphQL
EndpointsMultiple endpointsUsually one endpoint
Response ShapeDefined by serverDefined by client
Over-fetchingCommonRare
Under-fetchingPossibleRare
Multiple RequestsOften neededUsually fewer
CachingSimple HTTP cachingMore complex
Learning CurveLowerHigher

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

SymptomCauseFix
Cannot query field "x"The field is not defined in the schemaAdd the field or correct the spelling
Empty screen with no visible errorThe application ignores GraphQL errorsAlways inspect the errors collection
High database load on list pagesN+1 query problemUse batching and DataLoader
Slow server under heavy loadDeep or expensive queriesAdd depth and complexity limits
Unexpected null valuesResolver failuresHandle errors and validate resolver logic
Large responses return slowlyToo many fields are requestedRequest 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 errors list, often with status 200. 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.