GraphQL basics

Learn GraphQL fundamentals for Navixy Repository API: how it differs from REST, plus queries, mutations, and core type concepts.

circle-exclamation

This article introduces GraphQL for developers who are new to it. If you've worked with REST APIs before, you'll find GraphQL takes a different approach to requesting and modifying data.

For detailed information, see the official GraphQL documentationarrow-up-right and GraphQL specificationarrow-up-right.

What is GraphQL?

GraphQL is a query language for APIs. The key idea behind it is that you specify exactly what data you need, and the API returns only that data. This is different from REST APIs, where each endpoint returns a fixed structure, and you often get more data than you need (or have to make multiple requests to get everything you need).

How GraphQL differs from REST

If you've used REST APIs, you're familiar with this pattern:

  • Different endpoints for different resources (/devices, /devices/123, /assets)

  • HTTP methods determine the action (GET to read, POST to create, PUT to update, DELETE to remove)

  • Each endpoint returns a predefined data structure

GraphQL works differently:

Aspect
REST
GraphQL

Endpoints

Multiple (/devices, /assets, /users)

Single (/graphql)

Data returned

Fixed structure per endpoint

You specify exactly what fields you want

Multiple resources

Multiple requests needed

One request can fetch related data

HTTP methods

GET, POST, PUT, DELETE

Always POST (with query or mutation inside)

Sample request

Imagine you need to display a device's title, its status name, and the organization it belongs to.

With REST, you might need multiple requests:

With GraphQL, you make one request and specify exactly what you need:

The response contains only what you asked for:

Single endpoint

Navixy Repository API has one endpoint.

Every request — reading data, creating something, or subscribing to updates — goes to the same URL. The request body tells the API what you want to do.

Operations

GraphQL supports three types of operations:

Operation
Purpose
Similar to REST

Query

Read data

GET

Mutation

Create, update, or delete data

POST, PUT, DELETE

Subscription

Receive real-time updates

WebSocket connection

Queries

A query retrieves data without changing anything. It's the GraphQL equivalent of a REST GET request.

Here's a simple query that fetches a device:

Let's break this down:

  • query tells GraphQL you want to read data

  • device is the name of the query (what you're fetching)

  • (id: "550e8400-...") is an argument that specifies which device

  • { id title } lists the fields you want returned

The response mirrors your query structure:

Notice that the response contains only id and title — exactly what you requested. If you also need the device's status, add it to your query:

The status field returns an object, so you specify which fields of that object you want (code and title).

Mutations

A mutation modifies data, creating, updating, or deleting something. It combines what REST does with POST, PUT, and DELETE into one operation type.

Here's a mutation that updates a device's title:

Let's break this down:

  • mutation tells GraphQL you want to change data

  • deviceUpdate is the name of the mutation

  • input: { ... } provides the data for the update, including the current version for optimistic locking

  • { device { ... } } specifies what to return after the update

The response shows the updated data:

This is useful because you can confirm the change and get the new version number without making a separate query.

Here's a mutation that creates a new asset:

Subscriptions

A subscription opens a persistent connection to receive updates as they happen. Instead of polling the API repeatedly, you tell it what events you're interested in, and it pushes data to you.

At the moment, Navixy Repository API doesn't support subscriptions.

Fields

Fields are the pieces of data you can request from an object. When you write a query, you're selecting which fields to include in the response.

Every object type in the API has a defined set of fields. For example, a Device has fields like:

  • id — the unique identifier

  • title — the display name

  • status — the current status (which is itself an object with its own fields)

  • organization — the organization that owns it

  • customFields — custom data specific to your setup

You only get the fields you ask for. If you don't need customFields, don't include it in your query, and it won't be in the response.

Nested fields

Some fields return objects rather than simple values. When a field returns an object, you must specify which fields of that object you want.

Here, status and organization are objects, so we specify their fields inside curly braces. The title and code fields return simple values (strings), so they don't need nested fields. The features field returns an array of enum values.

Arguments

Arguments allow you to filter or configure what data a query returns. You pass them in parentheses after the field name.

The device query requires an id argument to know which device to fetch:

The devices query (plural) can take optional arguments to filter results:

This requests the first 10 devices from a specific organization with a specific status. The organizationId is a required argument, and filter allows additional criteria like filtering by multiple status IDs.

Types

Every field in GraphQL has a type that defines what kind of data it holds. It can be predefined, like string or boolean, or API-specific, like DeviceIdType. Understanding types helps you read the API reference and write correct queries.

Scalar types

Scalars are primitive values — basic building blocks. When calling a GraphQL API, you must specify nested subfields until you return only scalars.

GraphQL has the following built-in scalar types:

Type
Description
Example

String

Text

"Truck 42"

Int

Whole number

42

Float

Decimal number

3.14

Boolean

True or false

true

ID

Unique identifier (serialized as string)

"550e8400-e29b-41d4-a716-446655440001"

Navixy Repository API adds these custom scalar types:

Type
Description
Example

DateTime

Date and time with timezone (ISO 8601)

"2025-01-15T14:30:00.000Z"

Date

Date without time (ISO 8601)

"2025-01-15"

JSON

Arbitrary JSON data

{"color": "red", "priority": 1}

Code

Machine-readable identifier (lowercase alphanumeric with underscores)

"truck", "device_type"

HexColorCode

Color in hexadecimal format

"#FF5733"

Object types

Object types represent the things you can fetch from the API: devices, organizations, assets, users, and so on. Each object type defines a set of fields you can request.

Here's what the Device type looks like in the schema:

This tells you:

  • Device is an object type

  • It has fields like id, version, title, status, etc.

  • Each field has its own type (ID!, Int!, String!, DeviceStatus!)

Some fields return scalar types (id returns ID!), while others return other object types (status returns DeviceStatus!). When a field returns an object type, you must specify which of its fields you want:

In the API reference, you'll see object types like Device, Organization, DeviceStatus, and many others.

Enums

Enums are fixed sets of values. Unlike strings, you can only use the predefined values.

For example, ActionPermission is an enum with these values:

  • READ

  • CREATE

  • UPDATE

  • DELETE

When using an enum in a query, write the value without quotes:

Input types

Input types are used for mutation arguments. They look similar to object types but can only be used as inputs, not outputs.

In the API reference, you'll see input types like CreateDeviceInput, UpdateAssetInput, and DeviceFilter. They define what fields you can (or must) provide:

Interfaces

Interfaces define a set of fields shared by multiple types. If a type implements an interface, it guarantees those fields exist.

For example, the Node interface requires the id field. Many types implement it: Device, Asset, Organization, and others. This means you can always request id from any of these types.

The Versioned interface provides the version field used for optimistic locking. Types implementing it will always have this field available for concurrency control.

Type syntax

The API reference uses special notation to indicate whether fields are required and whether they return single values or lists.

Required vs nullable

The ! symbol means a value is required (non-null):

Syntax
Meaning

String

May be null

String!

Never null — always returns a string

For arguments, ! means you must provide a value:

Arrays

Square brackets [] indicate a list of values:

Syntax
Meaning

[String]

A list of strings (list and items may be null)

[String!]

A list of non-null strings (list may be null)

[String]!

A non-null list of strings (items may be null)

[String!]!

A non-null list of non-null strings

The most common pattern is [Type!]! — a guaranteed list where every item exists:

Reading complex types

When you see a type like [DeviceEdge!]! in the API reference:

  1. Start from the inside: DeviceEdge — this is an object type

  2. Add !: DeviceEdge! — each item is never null

  3. Add []: [DeviceEdge!] — it's a list

  4. Add outer !: [DeviceEdge!]! — the list itself is never null

So [DeviceEdge!]! means "you'll always get a list, and every item in that list will be a valid DeviceEdge object."

Understanding responses

GraphQL responses are JSON objects with a consistent structure.

Successful responses

A successful response has a data field containing your requested data:

The structure inside data matches your query structure exactly.

Error responses

If something goes wrong, the response includes an errors array:

The extensions object contains machine-readable information for handling the error in your code. See Error handling for more information.

Schema

A GraphQL API has a schema that defines all available types, fields, queries, and mutations. The schema is like a contract: it tells you exactly what you can request and what you'll get back.

Navixy Repository API schemaarrow-up-right is public and available to developers.

Introspection

GraphQL APIs are self-described. You can explore the schema using tools like our sandboxarrow-up-right or by querying the API directly — this is called introspection. GraphQL has special built-in fields that start with __ (double underscore) for this purpose.

For example, to see all fields available on the Device type:

See also

Last updated

Was this helpful?