---
title: GraphQL vs REST
slug: api-docs/graphql-vs-rest
description: Choosing between GraphQL and REST when calling Litmus APIs. Unify defaults to GraphQL for the UNS data fabric; Edge and Edge Manager are REST-first. Trade-offs and examples.
docTags: 
createdAt: 2026-05-11T22:04:18.988Z
---

# GraphQL vs REST

Litmus uses both. Knowing which module is which prevents the most common 200-with-no-data confusion.

## Which modules use which

| Module                                                   | Protocol                                                                                               |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| LE DeviceHub                                             | GraphQL at `/devicehub/v2` (most ops); a few REST endpoints for CSV upload/download and storage toggle |
| LE Digital Twins                                         | GraphQL at `/digitaltwins/v2`                                                                          |
| LE Integration                                           | GraphQL at `/cc/v2`                                                                                    |
| LE OPC UA                                                | GraphQL at `/opcua/v2`                                                                                 |
| LE DataHub, Flows, Analytics, Applications, System, Auth | REST                                                                                                   |
| LEM (all modules)                                        | REST                                                                                                   |
| LUNS (all modules)                                       | GraphQL at `/mqtt/gql`                                                                                 |

## GraphQL gotchas

### 1. Error envelope

GraphQL endpoints return **HTTP 200** even on validation errors. The error sits in the body:

```json
{
  "data": null,
  "errors": [
    { "message": "Driver not found", "path": ["CreateDevice"] }
  ]
}
```

Your client must check `body.errors`, not just the HTTP status. Treat `errors` being a non-empty array as a failure.

### 2. Operation name is in the body

REST callers think in URLs. GraphQL callers think in operations. The URL stays the same; the *query* or *mutation* selects the action:

```bash
# Same URL, two different operations:
curl -X POST .../devicehub/v2 -d '{"query":"query { ListDriverGroups { ... } }"}'
curl -X POST .../devicehub/v2 -d '{"query":"mutation { CreateDevice(input: {...}) { ID } }"}'
```

### 3. Field selection is required

You must explicitly name the fields you want back. Asking for everything in a single query is not idiomatic - request only what you'll read.

### 4. Pagination, filtering, sorting

These vary per operation. Most list operations accept an `input` argument with `Limit`, `Offset`, `Filter`, `Sort` fields. Check the operation's schema before assuming defaults.

## REST conventions in Litmus

- Path-versioned: `/devicehub/v2`, `/digitaltwins/v2`, `/dm/template/v2` - new major versions don't break old clients.
- Resumable uploads: `DELETE` -> `POST {size}` -> `PUT {bytes}` pattern. See [Resumable Uploads](#).
- Async tasks (LEM): `POST` returns `taskId`, then `GET /async-task/{id}/subtasks` polls until `SUCCESS`.
- Standard HTTP status codes - 200/201 success, 400 validation, 401 auth, 403 forbidden, 404 missing, 5xx server.

## How to spot GraphQL vs REST on the API portal

Browsing `api.litmus.io`:

- GraphQL requests show as `POST` with a JSON body containing `query` and optional `variables`.
- REST requests show varied HTTP methods and typed bodies.
- Look at the URL: anything ending in `/v2` with no further path (`/devicehub/v2`, `/cc/v2`, `/opcua/v2`, `/digitaltwins/v2`, `/mqtt/gql`) is GraphQL.
