Skip to content

GraphQL Basics in IZI

Published: · IZI Team

GraphQL is a query language where you define exactly what you want to receive. Unlike REST, there are no multiple endpoints: one URL, one POST, and you select the fields you need.

If you come from REST, the main mental shift: forget URL as a resource. The URL is always https://api.izi.is/graphql — what to do is described in the request body.

query GetClub($clubId: ID!) {
club(id: $clubId) {
id
name
timezone
devices {
id
name
status
}
}
}

Breaking it down:

  • query — operation type (read data)
  • GetClub — operation name (useful for logs and debugging, optional but recommended)
  • $clubId: ID! — variable of type ID, required (!)
  • club(id: $clubId) — field invocation with argument
  • Nested fields — select only what you need
query ActiveSessions($clubId: ID!) {
activeSessions(clubId: $clubId) {
id
deviceName
clientName
startedAt
balance
}
}

Variables are passed as a separate object:

{
"clubId": "club_abc123"
}
mutation TopUpClientBalance($input: TopUpInput!) {
topUpBalance(input: $input) {
success
newBalance
transactionId
}
}
{
"input": {
"clientId": "client_xyz",
"amount": 500,
"clubId": "club_abc123"
}
}

Mutations always return the operation result. Check the success field — don’t rely solely on HTTP 200.

subscription SessionUpdates($clubId: ID!) {
sessionUpdated(clubId: $clubId) {
id
status
deviceId
updatedAt
}
}

Subscriptions use WebSocket. Subscription endpoint: wss://api.izi.is/graphql.

Never interpolate data directly into query strings — it’s both a security risk and inconvenient:

# ❌ Bad — string interpolation
query { club(id: "club_abc123") { name } }
# ✅ Good — variables
query GetClub($id: ID!) {
club(id: $id) { name }
}
# Variables: { "id": "club_abc123" }

If the same fields appear in multiple queries, extract them into fragments:

fragment DeviceFields on Device {
id
name
status
zone {
id
name
}
}
query AllDevices($clubId: ID!) {
devices(clubId: $clubId) {
...DeviceFields
}
}

IZI API responses always include data and optionally errors:

{
"data": {
"topUpBalance": null
},
"errors": [
{
"message": "Insufficient permissions",
"extensions": {
"code": "FORBIDDEN",
"field": "topUpBalance"
}
}
]
}

HTTP status may be 200 even on error. Handle the errors field explicitly — don’t rely on HTTP codes for business errors. Full list — API Error Codes.

View the full schema:

{
__schema {
types {
name
kind
description
}
}
}

View fields of a specific type:

{
__type(name: "Club") {
fields {
name
type { name kind }
description
isDeprecated
deprecationReason
}
}
}

Multiple operations in one HTTP request:

[
{ "query": "query { me { id } }" },
{ "query": "query { clubs { id name } }" }
]

Response is an array in the same order. Limit: 10 operations per batch.

ClientUse Case
InsomniaDevelopment and debugging
Apollo ClientFrontend (React/Vue)
graphql-requestNode.js scripts and backend
gql + httpxPython

Frequently asked questions

What is the difference between query and mutation in IZI API?

A query reads data without changing state. A mutation changes data: starts a session, tops up a balance, updates a tariff. Simple rule: if the operation changes something — it's a mutation.

Can I send multiple operations in one POST?

Yes, this is called batching. Send an array of operations in the request body. IZI API supports batches of up to 10 operations.

How do I discover all available fields and types?

Use introspection: send a __schema or __type query. Most GraphQL clients (Insomnia, Apollo Studio) do this automatically.

What is the N+1 problem and how does IZI handle it?

N+1 occurs when fetching N objects triggers N additional requests for related data. IZI API uses DataLoader to batch related object loads automatically.