ExoSense GraphQL API¶
Info
The API is available at the Organization ExoSense Tier.
Overview¶
The ExoSense API uses GraphQL, a query language for supporting requests for data, modify objects, or to delete / create objects such as assets. GraphQL can be compared to REST and RPC (Remote Procedure Calls)
The ExoSense API is provided for software integrations such as but not limited to:
- Retrieving and displaying Asset status and/or signal values in another business software tool.
- Automating the creation of Assets during manufacturing of devices
- Automating user invitations from a separate registration system or billing process
Info
Currently the API is not recommended for nor does it fully support building custom user applications.
API Endpoint¶
This API has a single endpoint specific to your ExoSense instance and does not change no matter what query is being made, which is a difference from RESTful APIs.
https://<$YOUR_EXOSENSE_INSTANCE_DOMAIN>/api/graphql
<$YOUR_EXOSENSE_INSTANCE_DOMAIN>
is your ExoSense domain (e.g. example.apps.exosite.io)
Using the GraphQL API¶
All requests take the following HTTP format. Note that the call is an HTTP POST request with a JSON encoded body for the operations. This API supports two operations, being queries and mutations.
POST https://<$YOUR_EXOSENSE_INSTANCE_DOMAIN>/api/graphql
Authorization: Automation $TOKEN
Content-Type: application/json
Accept: */*
{
"query": "....",
"operationName": "....",
"variables": {"myvar1":"value1","myvar2":"value2", ...}
}
- The
query
field in the JSON object is required. - The value for
query
is a GraphQL query encoded as a string. operationName
andvariables
optional fields,operationName
is only required if multiple operations are present in the query.
Hint
GraphQL queries are like a GET
request in a RESTful API. GraphQL mutations operate like POST
, PATCH
, and DELETE.
Queries¶
The GraphQL query essentially asks for fields in an object. A very simple example shows how to ask for a list of assets with the asset id and name. The object being 'assets'. Note that this call as is assumes all assets at the root level of the API token permission access.
For more information of GraphQL queries, the graphql.org site covers these details in more depth
# Example of a Query, for Assets and to include the id and name fields
{
assets {
id
name
}
}
// Response JSON object
{
"data": {
"assets": [
{
"id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
"name": "Customer A Child 1 Asset 1"
},
{
"id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
"name": "Customer A Child 1 Asset 2"
},
{
"id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
"name": "Customer A Child 1 Asset 3"
},
{
"id": "da965bda-d9ea-48d0-b2bf-869773744d3c",
"name": "Customer A Child 1 Asset 4"
},
{
"id": "73b79e78-d159-4c3e-b40d-f8b55dd9e286",
"name": "Customer A Child 1 Asset 5"
},
{
"id": "3a5b452a-a1b3-4efb-90c2-128853424491",
"name": "Customer A Child 1 Asset 6"
},
{
"id": "21811b22-d01a-4867-b1b2-81a2b554b63c",
"name": "Customer A Child 1 Asset 7"
},
{
"id": "3582118a-d390-457e-9836-2e72331d2b70",
"name": "Customer A Child 1 Asset 8"
}
]
}
}
Arguments¶
Arguments allow you to specify query details for the the object and all nested objects. An example is to include the pagination
argument (which happens to be an object itself) in with the assets
query to limit the number of returned items.
# Query with arguments
{
assets(pagination: {limit:3,offset:0}) {
id name
}
}
// JSON response, noting that only 3 assets were returned
{
"data": {
"assets": [
{
"id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
"name": "Customer A Child 1 Asset 1"
},
{
"id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
"name": "Customer A Child 1 Asset 2"
},
{
"id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
"name": "Customer A Child 1 Asset 3"
}
]
}
}
Aliases¶
Aliases allow you to query the same field but different arguments, so the result can be differentiated.
# Example query using aliases
{
asset1: asset(id: "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b") {
name
}
asset2: asset(id: "c6b918c7-a61d-4be9-aed6-06582d243e87") {
name
}
}
{
"data": {
"asset1": {
"name": "Customer A Child 1 Asset 1"
},
"asset2": {
"name": "Customer A Child 1 Asset 2"
}
}
}
Note that GraphQL Fragments can also be used for more complex queries that reuse many of the same variables/fields, check out the GraphQL documentation for more information.
Operation Name & Type¶
Operation names are recommended, though optional unless you have a multi-operational document in which they are required. They allow the code to be more user friendly, in addition to helping with debugging. It's like a function name in programming.
The operation type is either query
mutation
or subscription
describing the type of operation. It's required unless using shorthand syntax (the simple examples above). Shorthand syntax assumes no name or variables are provided.
# Example that includes the Operation Type and Name
query GetAllAssets {
assets(pagination:{limit:3}) {
name id
}
}
// Resulting JSON object response
{
"data": {
"assets": [
{
"id": "6fe9fdcd-926e-4b39-a224-f0fd8c00a99b",
"name": "Customer A Child 1 Asset 1"
},
{
"id": "c6b918c7-a61d-4be9-aed6-06582d243e87",
"name": "Customer A Child 1 Asset 2"
},
{
"id": "85290cc8-db5b-4506-9f66-e1944f0a7322",
"name": "Customer A Child 1 Asset 3"
}
]
}
}
Mutations¶
Mutations allow server side operations such as creating, deleting, and modifying objects. Mutations are similar to queries in syntax and responses. The one distinction is that when there are multiple fields in a mutation, they run in series, rather than parallel.
# example mutation request to delete an asset
mutation DeleteAsset {
deleteAsset(id: "3582118a-d390-457e-9836-2e72331d2b70") {
id
}
}
//response
{
"data": {
"deleteAsset": {
"id": "3582118a-d390-457e-9836-2e72331d2b70"
}
}
}
Variables¶
Variables allow the client application making API requests to use a static string for the query but support dynamic variables that could be changed based on user inputs, etc. Note that the variable has to be declared for the query including specifying the type (in this case the type is Pagination
). Note that types are defined by the API itself. The variable is then passed in with the HTTP requests JSON body. Variables are declared with a $ prefix.
# query that includes a variable
query GetAllAssets($UserPaginationSetting: Pagination ) {
assets(pagination: $UserPaginationSetting)
{
name id
}
}
# the variables dictionary to pass in with the static string
{
"UserPaginationSetting": {
"limit": 3
}
}
POST /api/graphql HTTP/2
Host: example.apps.exosite.io
Content-Type: application/json
Authorization: Automation <TOKEN HERE>
Accept: */*
Content-Length: 195
{
"query":"query GetAllAssets($UserPaginationSetting: Pagination ) { \n assets(pagination: $UserPaginationSetting) \n {\n name id\n }\n}\n",
"variables":{"UserPaginationSetting":{"limit":3}}
}
Format Notes¶
The examples provided in this document generally have been formatted for ease of readability. The following examples work exactly the same but the resulting query string passed into the HTTP request will contain any additional characters such as new lines.
query GetAllAssets {
assets(pagination:{limit:3}) {
name id
}
}
# Note: drops the Operation Name
query {
assets(pagination:{limit:3}) {name id}
}
# Note: Shorthand syntax only supported for query type
# and not recommended for documents containing many queries
{ assets(pagination:{limit:3}) {name id}}
# Note: Query is all on one line, otherwise same
query GetAllAssets { assets(pagination:{limit:3}) {name id}}
The resulting HTTP request for this last one looks like:
POST /api/graphql HTTP/2
Host: example.apps.exosite.io
Content-Type: application/json
Authorization: Automation <TOKEN WOULD BE HERE>
Accept: */*
Content-Length: 74
{"query":"query GetAllAssets { assets(pagination:{limit:3}) {name id}}"}
Authentication¶
This API uses Authentication Tokens that can be created in the application. Each token has a specific permission. This token is used in the Authorization header in the HTTP request. $TOKEN
below is replaced with the actual token.
Authorization: Automation $TOKEN
If you haven't already, navigate to Setup > API Tokens and create a new token. The role you choose for the token, similar to a user role, will limit the ExoSense™ access granted to the token.
Save the token in a safe and secure location. You won't be able to access it again, so don't lose it. If you do lose it, a new token must be generated.
Responses¶
GraphQL responses takes the same shape as the query, returned as a JSON object. The response JSON object contains two fields, data
and errors
. The errors field is only returned if there were errors, otherwise it is not present.
{
"data": { ... },
"extensions": { ... },
"errors": [ ... ]
}
Resource Limitations¶
The ExoSense GraphQL API has limitations in place to protect against excessive or abusive calls to Exosite's servers and prevent service disruption. These resource usage limits are tracked per request, as a rate for the API Token, and as a rate for the specific ExoSense instance.
The following resources are used to validate requests to the ExoSense GraphQL API. These resources, unless noted, are used per request and over a time period (rate).
Resources | Unit | Description |
---|---|---|
time | ms | The time resource is a measure of the execution time taken for the request. |
query | [Future / Not Used] Represents the function's baseline resource usage | |
cost | [Future / Not Used] Calculated as a "cost" weighting of how much information is being requested. | |
requests | requests | Number of API requests per API token within Rate Limit window. (Note: Only used for Rate) |
Per Request Limits¶
Each API request will be validated against the following resource limits.
Resource | Limit | Enforced |
---|---|---|
time | 10000 | Yes |
cost | TBD | No (Future) |
Request Limit Usage and Limits Details in the Request Response
{
"data": {...},
"extensions": {
"cost": {
"usage": {
"cost": 0,
"time": 182
},
"limits": {
"cost": 1000000,
"time": 10000
}
}
}
}
Handling the time
limit
If developers see their per request is nearing the time
limit, they should split up their requests.
Rate Limits¶
The rate limits uses a rolling window to enforce resource limitations over time.
Rate limit window: 10 seconds (rolling)
The following rate limits are in place for each API Token within the rate limit window.
Resource | Rate Limit | Enforced |
---|---|---|
time | 100000 | Yes |
requests | 10 | Yes |
cost | TBD | No (Future) |
query | TBD | No (Future) |
Rate Limit Usage and Limits in Request Response
{
"data": {...},
"extensions": {
"cost": {
"rateUsage": {
"time": 825,
"cost": 0,
"query": 28,
"requests": 1
},
"rateLimits": {
"cost": 10000000,
"query": 500000,
"time": 100000,
"requests": 10
}
}
}
}
Tracking Limits¶
Each API request within the limits will include an extensions
object in addition to the returned data
. The information in extensions
should be used to adjust API usage before exceeding limits. If a limit has been exceeded, only the errors
object will be returned.
Summary of Response Objects
data: # Requested data if request is within limits
extensions: # Object Returned with response in addition to the requested `data`
cost:
rateUsage: # Object containing the current state of the rate limit usage after the request
rateLimits: # Object containing the rate limits in place for the API token
usage: # Object containing the specific request's usage of resources
limits: # Object containing the per request resource limits in place
time: # The time the request took
overUsageLimit: # Deprecated, boolean flag, no longer used
overRateLimit: # Deprecated, boolean flag, no longer used
errors: # Array of error messages if request exceeded any limits
An example response that is under Request Limits
{
"data": {...},
"extensions": {
"cost": {
"overRateLimit": false,
"rateUsage": {
"query": 0,
"time": 0,
"requests": 1,
"cost": 0
},
"rateLimits": {
"query": 1000000,
"time": 100000,
"requests": 10,
"cost": 1000000
},
"limits": {
"query": 1000000,
"time": 10000,
"cost": 1000000
},
"time": 115,
"overUsageLimit": false,
"usage": {
"query": 0,
"time": 115,
"cost": 0
}
}
}
}
An example response that is over a Request Limit
{
"errors": [
{
"message": "ExceededRateLimitRequests"
}
]
}
Limit Errors¶
There are a number of possible limit related errors that can be encountered when requesting data through the API. They fall into two categories - Usage
and Rate
. Usage errors are encountered when a single query exceeds any of the usage limits defined above. Rate errors are encountered when a single field in the usage limits is reached over a period of time.
Error | Cause | Action |
---|---|---|
ExceededMaximumTime | This error is available, but will not be encountered on a per request basis, only on rate limiting. A timeout will occur before it is reached | Not a reachable error |
ExceededRateLimitTime | The sum of time taken for requests in the window has exceeded the per request time * rate scale | Try again after the rate period` and reduce the frequency of requests |
ExceededRateLimitRequests | The number of requests has exceeded the requests rate limit for the given window_size for the token | Reduce the request frequency and try again when the window rolls over from the first request |
Error Handling
- If usage errors are encountered, consider using pagination and requesting smaller chunks of data to prevent asking for too much data in one particular request.
- If rate limits are encountered, consider reducing the frequency that requests are made.
Schema¶
GraphQL is powerful in that with a request, you can perform an introspection of the API realtime. (i.e. it is self documented) . Details about GraphQL Introspection
To perform this introspection, you can query the __schema
and __type
fields, although there are several tools that support GraphQL natively and provide an automated built-in way to view the documentation.
Tools¶
There are a number of tools that natively support GraphQL to test and run queries and introspect of the API, including Insomnia and Postman which are commonly used tools to test HTTP requests, REST APIs, etc.
https://insomnia.rest/graphql/
https://www.getpostman.com/product/api-client
Libraries¶
There are a number of libraries, tools, and services built for GraphQL, which can be found on the GraphQL organization site.
Getting Started¶
Exosite provides an example JSON file that can be imported into Insomnia and provides a number of example queries and mutation requests. Please submit a support request to get a copy of this file.