How the API works
GraphQL in practice: one POST, selection sets, variables, and reading the response — for developers new to GraphQL.
If you've used REST but not GraphQL, this is everything you need to be productive. You don't need to learn GraphQL in depth first — the format below covers the vast majority of real usage.
It's one POST request
There are no per-resource URLs like GET /issues/123. Every call — reading
or writing — is an HTTP POST to the single /graphql endpoint with a JSON
body containing two things:
{
"query": "…the operation you want to run…",
"variables": { "…values the operation uses…": "…" }
}That's the whole transport. If you can send a JSON POST with a header, you can
call this API in any language — no GraphQL client library required.
Reading the query string
The query field holds a GraphQL operation. It starts with a keyword:
query { … }— read data (like aGET).mutation { … }— change data (likePOST/PATCH/DELETE).
Inside the braces you name the operation you want, then — and this is the part REST doesn't have — you list exactly which fields you want back. The server returns those fields and nothing else, so you never over- or under-fetch:
query {
issue(id: "SXNzdWU6MTAyNA==") {
ticketNumber # ← ask for the fields you want
subject
status
}
}If you leave a field out, it isn't returned. If you ask for a field that has its
own sub-fields (a nested object), you open another { … } and list those too:
query {
issue(id: "SXNzdWU6MTAyNA==") {
subject
messages(first: 5) { # a connection…
edges { node { body } } # …so drill in for the fields you want
}
}
}The Reference section lists every operation, its arguments, and the type it returns — so you always know which fields are available to request.
Use variables, not string interpolation
Don't paste values into the query string. Declare them with a $name and pass
them in the separate variables object. This is safer (no injection, correct
typing) and is how every example in these docs is written:
{
"query": "mutation Create($input: CreateIssueInput!) { createIssue(input: $input) { issue { id ticketNumber } error { code message } } }",
"variables": {
"input": { "subject": "Login broken", "requesterEmail": "jane@example.com" }
}
}The $input: CreateIssueInput! part declares a variable named input of type
CreateIssueInput (the ! means required). The matching value lives in
variables. Every input type is documented in the Reference.
Reading the response
The response mirrors your query. Successful data comes back under data, keyed
by the operation and shaped like the fields you asked for:
{ "data": { "issue": { "ticketNumber": 1024, "subject": "Login broken", "status": "open" } } }Two kinds of failure can appear:
- Transport / query errors (bad syntax, unknown field, missing auth) come
back in a top-level
errorsarray — often withdata: null. - Business errors (validation, not-found, forbidden) come back as an
errorobject inside the mutation payload, sodatais still present. This is why most mutations here select anerror { code message }field. See Errors for the full taxonomy.
Try it without writing code
Open the endpoint in a browser to get the API Explorer (a GraphiQL IDE) with live schema docs, autocomplete, and a run button — the fastest way to learn the shape of an operation before you wire it into code.
Want the language itself? This page covers everything you need for Clad. To go deeper on GraphQL as a language, see the official intro at graphql.org/learn.