Learn/Advanced Topics

GraphQL & JSON — Queries, Responses & Schema Patterns

GraphQL speaks JSON. Every request wraps a query in JSON, every response is JSON, and the schema itself is queryable as JSON. This guide covers how JSON flows through the entire GraphQL lifecycle — from query variables to error handling to introspection.

How GraphQL Uses JSON

JSON in the GraphQL Lifecycle

JSON appears in three places:

WhereFormatExample
Request body{"query": "...", "variables": {...}}Sent via HTTP POST
Response body{"data": {...}, "errors": [...]}Always returned as JSON
Introspection{"__schema": {...}}The schema itself as JSON

The Request: Query + Variables as JSON

GraphQL HTTP request bodyjson
1{
2 "query": "query GetUser($id: ID!) { user(id: $id) { name email posts { title } } }",
3 "variables": {
4 "id": "user_42"
5 },
6 "operationName": "GetUser"
7}

Note

The query field is a GraphQL string (not JSON), but it is wrapped in a JSON object. The variables field is a JSON object that maps to the query's $ parameters. operationName is optional but useful when a document contains multiple operations.

Variables — Type-Safe JSON

Variables are passed as JSON and must match the types declared in the query:

Variables for a mutationjson
1{
2 "query": "mutation CreatePost($input: PostInput!) { createPost(input: $input) { id title } }",
3 "variables": {
4 "input": {
5 "title": "GraphQL & JSON Guide",
6 "body": "Everything you need to know...",
7 "tags": ["graphql", "json", "api"],
8 "published": true
9 }
10 }
11}

The Response: Predictable JSON

Successful Response

The response JSON mirrors the exact shape of the query:

GraphQL responsejson
1{
2 "data": {
3 "user": {
4 "name": "Alice Chen",
5 "email": "[email protected]",
6 "posts": [
7 { "title": "Getting Started with GraphQL" },
8 { "title": "JSON Best Practices" }
9 ]
10 }
11 }
12}

Tip

The response shape is identical to the query shape. If you request user { name email }, you get exactly {"user": {"name": "...", "email": "..."}}. No extra fields, no missing fields. This predictability is a core advantage of GraphQL.

Error Response

GraphQL errors include precise location and path information:

GraphQL error formatjson
1{
2 "data": {
3 "user": {
4 "name": "Alice Chen",
5 "email": null
6 }
7 },
8 "errors": [
9 {
10 "message": "Not authorized to access User.email",
11 "locations": [{ "line": 3, "column": 5 }],
12 "path": ["user", "email"],
13 "extensions": {
14 "code": "FORBIDDEN",
15 "timestamp": "2026-04-02T14:30:00Z"
16 }
17 }
18 ]
19}

Important

GraphQL returns partial data with errors. The HTTP status is still 200. The data and errors can coexist — a resolved field appears in data and a failed field appears as null in data with an entry in errors.

GraphQL vs REST — JSON Response Comparison

REST: Multiple Endpoints, Fixed Shapes

REST — GET /api/users/42json
1{
2 "id": 42,
3 "name": "Alice Chen",
4 "email": "[email protected]",
5 "bio": "Senior developer at...",
6 "avatar_url": "https://...",
7 "created_at": "2026-01-15T10:30:00Z",
8 "followers_count": 1234,
9 "following_count": 56
10}

The client gets all fields whether it needs them or not (over-fetching). To get user + posts, the client makes a second request to /api/users/42/posts.

GraphQL: One Endpoint, Custom Shape

GraphQL — POST /graphql (only requested fields)json
1{
2 "data": {
3 "user": {
4 "name": "Alice Chen",
5 "posts": [
6 { "title": "GraphQL Guide", "likes": 42 }
7 ]
8 }
9 }
10}

The client requests exactly the fields it needs — no over-fetching, no second request.

FeatureREST JSONGraphQL JSON
EndpointsMany (/users, /posts, /comments)One (/graphql)
Response shapeFixed by serverDefined by client query
Over-fetchingCommon — returns all fieldsEliminated — only requested fields
Under-fetchingCommon — needs multiple requestsEliminated — nested in one query
Error formatHTTP status codes200 + errors array in body
CachingHTTP caching (ETag, Cache-Control)Requires client-side cache (Apollo)
File uploadsNative (multipart/form-data)Needs apollo-upload-client or similar

Fetching GraphQL with JavaScript

Using fetch()typescript
1async function graphqlFetch<T>(
2 query: string,
3 variables?: Record<string, unknown>
4): Promise<T> {
5 const response = await fetch('/graphql', {
6 method: 'POST',
7 headers: { 'Content-Type': 'application/json' },
8 body: JSON.stringify({ query, variables }),
9 });
10
11 const json = await response.json();
12
13 if (json.errors?.length) {
14 const messages = json.errors.map((e: { message: string }) => e.message);
15 throw new Error(`GraphQL errors: ${messages.join(', ')}`);
16 }
17
18 return json.data as T;
19}
20
21interface UserData {
22 user: { name: string; email: string };
23}
24
25const data = await graphqlFetch<UserData>(
26 `query GetUser($id: ID!) { user(id: $id) { name email } }`,
27 { id: "user_42" }
28);
29console.log(data.user.name); // "Alice Chen"

Introspection — The Schema as JSON

GraphQL can describe its own schema via an introspection query. The response is JSON:

Introspection response (partial)json
1{
2 "data": {
3 "__schema": {
4 "queryType": { "name": "Query" },
5 "types": [
6 {
7 "name": "User",
8 "kind": "OBJECT",
9 "fields": [
10 { "name": "id", "type": { "name": "ID", "kind": "SCALAR" } },
11 { "name": "name", "type": { "name": "String", "kind": "SCALAR" } },
12 { "name": "posts", "type": { "name": null, "kind": "LIST" } }
13 ]
14 }
15 ]
16 }
17 }
18}

Try It — Validate a GraphQL JSON Response

Try It Yourself

A valid GraphQL JSON response — try adding an errors array

Frequently Asked Questions

Is GraphQL based on JSON?
GraphQL queries use their own syntax (not JSON), but the request body sent over HTTP wraps the query in JSON: {"query": "...", "variables": {...}}. All GraphQL responses are standard JSON with a predictable structure: {"data": {...}, "errors": [...]}.
What does a GraphQL response look like?
Every GraphQL response is a JSON object with a "data" key (containing the requested fields) and optionally an "errors" array. The data shape exactly mirrors the query structure — this is what makes GraphQL predictable.
How are GraphQL errors different from REST errors?
In REST, errors return different HTTP status codes (404, 500). In GraphQL, the HTTP status is almost always 200. Errors are returned inside the response body as an "errors" array alongside partial "data". Each error has a "message", "locations", and "path".
Can I use JSON Patch with GraphQL?
Not directly — GraphQL uses mutations instead of PATCH/PUT methods. However, you can design mutation inputs that accept partial updates (using nullable fields) to achieve similar behavior.
What is GraphQL introspection?
Introspection is a built-in feature where you can query the GraphQL schema itself. The response is a JSON document describing all types, fields, and relationships. Tools like GraphiQL and Apollo Studio use introspection to auto-generate documentation.
Should I use GraphQL or REST?
Use GraphQL when clients need flexible data fetching (mobile vs web), when you have deeply nested relationships, or when over-fetching is a problem. Use REST when you need caching (HTTP caching works natively), simple CRUD operations, or file uploads.