Skip to content

Transforming JSON responses

The REST API allows you to retrieve information about a device using the FetchDevice endpoint. The JSON responses for devices can be large, especially for Bluetooth® Low Energy (LE) devices. Here is a JSON example for a Thingy:52:

{
  "id": "CA:B2:31:EE:E0:9E",
  "name": "CA:B2:31:EE:E0:9E",
  "type": "BLE",
  "tags": [],
  "state": {
    "1800": {
      "uuid": "1800",
      "characteristics": {
        "2A00": {
          "uuid": "2A00",
          "path": "1800/2A00",
          "value": [
            65,
            112,
            112,
            108,
            101,
            32,
            84,
            86
          ],
          "properties": {
            "broadcast": false,
            "read": true,
            "writeWithoutResponse": false,
            "write": false,
            "notify": false,
            "indicate": false,
            "authorizedSignedWrite": false
          },
          "descriptors": {}
        },
        "2A01": {
          "uuid": "2A01",
          "path": "1800/2A01",
          "value": [
            128,
            2
          ],
          "properties": {
            "broadcast": false,
            "read": true,
            "writeWithoutResponse": false,
            "write": false,
            "notify": false,
            "indicate": false,
            "authorizedSignedWrite": false
          },
          "descriptors": {}
        }
      }
    },
    "1801": {
      "uuid": "1801",
      "characteristics": {
        "2A05": {
          "uuid": "2A05",
          "path": "1801/2A05",
          "value": [],
          "properties": {
            "broadcast": false,
            "read": false,
            "writeWithoutResponse": false,
            "write": false,
            "notify": false,
            "indicate": true,
            "authorizedSignedWrite": false
          },
          "descriptors": {
            "2902": {
              "uuid": "2902",
              "value": [
                0,
                0
              ],
              "path": "1801/2A05/2902"
            }
          }
        }
      }
    },
    "D0611E78BBB44591A5F8487910AE4366": {
      "uuid": "D0611E78BBB44591A5F8487910AE4366",
      "characteristics": {
        "8667556C9A374C9184ED54EE27D90049": {
          "uuid": "8667556C9A374C9184ED54EE27D90049",
          "path": "D0611E78BBB44591A5F8487910AE4366/8667556C9A374C9184ED54EE27D90049",
          "value": [],
          "properties": {
            "broadcast": false,
            "read": false,
            "writeWithoutResponse": false,
            "write": true,
            "notify": true,
            "indicate": false,
            "authorizedSignedWrite": false
          },
          "descriptors": {
            "2900": {
              "uuid": "2900",
              "value": [
                1,
                0
              ],
              "path": "D0611E78BBB44591A5F8487910AE4366/8667556C9A374C9184ED54EE27D90049/2900"
            },
            "2902": {
              "uuid": "2902",
              "value": [
                0,
                0
              ],
              "path": "D0611E78BBB44591A5F8487910AE4366/8667556C9A374C9184ED54EE27D90049/2902"
            }
          }
        }
      }
    }
  },
  "$meta": {
    "version": "1.0",
    "createdAt": "2018-11-06T22:20:15.417Z",
    "updatedAt": "2018-11-06T22:20:44.676Z"
  }
}
 ```

In some cases, you might want to fetch only a subset of the information the server gives you. You can control which information is returned using a *transform*.

## Concepts

A *transform* is a [JSONata](https://jsonata.org/) expression that you send as a query string parameter, which transforms one or more resources in the default JSON response. JSONata is a REST-based analog to [GraphQL](https://graphql.org/), because you can express what the response should contain and how it is shaped.

The API provides transform support only for the [`FetchDevice`](https://api.nrfcloud.com/v1/#operation/FetchDevice) and [`ListDevices`](https://api.nrfcloud.com/v1/#operation/ListDevices) endpoints.

## Basic examples

This section contains basic transform examples.

### Example: fetching all device IDs

If you want to fetch the IDs of all your devices, transform the response with `transform=id`:

```bash
export API_KEY=YOUR_API_KEY
curl --request GET \
  --url "https://api.nrfcloud.com/v1/devices?includeState=true&transform=id" \
  --header "Authorization: Bearer $API_KEY"

Instead of all device data, you receive an abbreviated list:

{
    "items": [
        "CA:B2:31:EE:E0:9E",
        "ba4130fc-5d1f-423b-bd1c-05213932a884",
        "MyGenericDevice1"
    ],
    "total": 3
}

Example: Bluetooth LE device IDs

Use transform=type='BLE' ? id to return only the IDs of Bluetooth LE devices:

curl --request GET \
  --url "https://api.nrfcloud.com/v1/devices?includeState=true&transform=type='BLE' ? id" \
  --header "Authorization: Bearer $API_KEY"

The server returns the IDs for Bluetooth LE devices associated with your account, and null for devices that do not match the transform:

{
    "items": [
        "CA:B2:31:EE:E0:9E",
        null,
        null
    ],
    "total": 3
}

Example: filtering by device type

If you are filtering by device type, use the deviceTypes parameter and pass in BLE:

curl --request GET \
  --url "https://api.nrfcloud.com/v1/devices?transform=id&deviceTypes=BLE" \
  --header "Authorization: Bearer $API_KEY"

Example: returning ID and device type

The following command returns a JSON object containing the id and type of all your devices:

curl --request GET \
  --url "https://api.nrfcloud.com/v1/devices?includeState=true&transform=\{ 'id': id, 'type': type \}" \
  --header "Authorization: Bearer $API_KEY"

The server returns a list of devices by ID and type:

{
    "items": [
        {
            "id": "CA:B2:31:EE:E0:9E",
            "type": "BLE"
        },
        {
            "id": "ba4130fc-5d1f-423b-bd1c-05213932a884",
            "type": "Gateway"
        },
        {
            "id": "MyGenericDevice1",
            "type": "Generic"
        }
    ],
    "total": 3
}

Considerations

There are a few things to consider for these examples:

  • Although strict JSONata syntax requires valid JSON, which requires double quotes around field names, the REST API accepts single or double quotes. Whichever you choose, wrap the entire URL in the opposite type of quotes from the field names, as in these examples.
  • In the transform, values that are not wrapped in quotes are considered variables that map to the transformed resource. In this case, the JSON for the resource contains both an id and type property, which return as values for fields of the same name. You can give the field any name you want, such as deviceId and deviceType.
  • If you use shell environment variables such as API_KEY as in these examples, they must be either unquoted or in double quotes so the shell substitutes the value for the variable name.
  • You must escape braces with a backslash (\{ and \}), or cURL interprets them as two different HTTP requests and sends them in a syntax that the REST API does not accept. If you are using another REST API utility like Insomnia or Postman, you do not need to escape braces.

Advanced transforms

The previous examples worked with top-level properties of the Device resource. You can also use a transform to retrieve and shape data deep within the resource's JSON representation.

Example: characteristics

You can retrieve only the characteristics and their values from the Bluetooth LE example at the top of this page with the following command:

curl --request GET \
  --url "https://api.nrfcloud.com/v1/devices/CA:B2:31:EE:E0:9E?transform=\{ 'id': id, 'characteristics': $map(state.*.characteristics.*, function($c) \{ \{'uuid': $c.uuid, 'value': $c.value \} \})\}" \
  --header "Authorization: Bearer $API_KEY"

This returns the following response:

{
  "id": "CA:B2:31:EE:E0:9E",
  "characteristics": [
    {
      "uuid": "2A00",
      "value": [
        65,
        112,
        112,
        108,
        101,
        32,
        84,
        86
      ]
    },
    {
      "uuid": "2A01",
      "value": [
        128,
        2
      ]
    },
    {
      "uuid": "2A05",
      "value": []
    },
    {
      "uuid": "8667556C9A374C9184ED54EE27D90049",
      "value": []
    }
  ]
}

The JSONata Exerciser UI lets you simplify JSONata syntax for transforms. The exerciser requires double quotes around all field names.

Example: GET /devices and device types

If you want to call the GET /devices endpoint to fetch all of your devices, but apply a different transform per device type, use a ternary expression:

type = 'BLE' ? { 'id': id, 'characteristics': $map(state.*.characteristics.*, function($c) { {'uuid': $c.uuid, 'value': $c.value } })} : { 'id': id, 'reportedName': state.reported.name, 'connectedBLEDevices': $keys(state.reported.statusConnections) }

This applies one transform to Bluetooth LE types, and another to all the other (IP-based) types. However, this can get more complicated if you want to conditionally apply a third transform, because JSONata does not support switch/case.

Note

Transforms only work on the JSON of a Device resource, not on the entire JSON response, which might contain metadata, paging data, and more. When experimenting with JSONata, paste in the JSON for a single resource to work out the transformation expression. This is applied to each resource contained in the endpoint's response.