To communicate with the GraphQL server, you'll need an API access token.
To create an Access Token:
The API notifies you if you try to execute a mutation with a read-only key.
While a traditional REST API has numerous endpoints – or URLs – the GraphQL API has a single endpoint:
https://api.onsign.tv/graphql
The endpoint remains constant no matter what operation you perform. The one exception is uploading files. Check our file upload guide for more info.
Because GraphQL operations consist of multiline JSON, it is recommended using the Playground to familiarize yourself with GraphQL calls. You can also use cURL or any other HTTP-speaking library.
In REST, HTTP verbs determine the operation performed. In GraphQL, you'll provide a JSON-encoded body whether you're performing a query or a mutation, so the HTTP verb is POST. The exception is an introspection query, which is a simple GET to the endpoint.
To query GraphQL using cURL, make a POST request with a JSON payload.
The payload must contain a string called query:
curl https://api.onsign.tv/graphql -H 'Content-Type: application/json' -H 'Authorization: token YOUR-TOKEN' --data-binary '
{
"query": "query { organization { id, name }}"
}
'
POST body, use outer single quotes so you don't have to escape inner double quotes.
The two types of allowed operations in the GraphQL API are queries and mutations.
Comparing GraphQL to REST, queries operate like GET requests, while mutations operate like POST/PATCH/DELETE requests.
The mutation name determines which modification is executed.
Queries and mutations share similar forms, with some important differences.
GraphQL queries return only the data you specify. To form a query, you must specify fields within fields (also known as nested subfields) until you return only values without subfields.
Queries are structured like this:
query {
JSON objects to return
}
For a real-world example, see this Example query.
To form a mutation, you must specify three things:
Mutations are structured like this:
mutation {
mutationName(input: {MutationNameInput!}) {
MutationNamePayload
}
}
The input object in this example is MutationNameInput, and the payload object is MutationNamePayload.
In the mutations reference, the listed input fields are what you pass as the input object. The listed return fields are what you pass as the payload object.
For a real-world example, see this Example mutation.
There is a limit to the number of calls you can make or nested fields you can request. For information about rate limiting, see "GraphQL resource limitations."
Let's walk through a more complex query and put this information in context.
The following query looks up the organization, finds 5 directories in ascending order,
and returns each directory's id, name and the first 10 children, along with their
id, name and kind:
query {
organization {
contents(kind: FOLDER, first: 5) {
nodes {
id
name
children(first: 10) {
nodes {
id
name
kind
}
}
}
}
}
}
Looking at the query line by line:
query {
Because we want to read data from the server, not modify it, query is the root operation.
(If you don't specify an operation, query is also the default.)
organization {
To begin the query, we must provide the root object, organization, since all data to be returned will belong to the API key's organization.
contents(kind: FOLDER, first: 5) {
To account for all the fileds, folders and apps in the organization, we call the contents connection.
Some details about the contents object:
ContentConnection.first, to limit the number of results, so we provide 5.kind argument, which is an ContentKind enum that accepts APP, FOLDER, etc. as values.
To find only content that are folders, we give the kind key a value of FOLDER.
nodes {
We know contents is a connection because it has the ContentConnection type.
So here we retrieve the individual items belonging to the connection.
Now that we know we're retrieving an Content object, we can look at the docs and specify the fields we want to return:
id
name
children(first: 10) {
nodes {
id
name
kind
}
}
Here we specify the id, name, and children fields of the Content object.
The children field is of the type ContentConnection as well, so we can query the same fields we have queried previously.
We also add the kind field to return the kind of the content, which is of the type ContentKind, the same enum we mentioned previously.
List the first 10 players and the items being played for the primary loop:
{
organization {
players(first: 10) {
nodes {
id
name
tags
loop(name: PRIMARY) {
items {
edges {
id
position
}
}
}
}
}
}
}
Listing the 100 players that connected to the server the last:
{
organization {
players(first: 10, orderBy: {field: LAST_SEEN_AT}) {
nodes {
id
name
lastSeen
isConnected
}
}
}
}
List campaigns inside a playlist:
{
organization {
playlists(first: 10) {
nodes {
id
name
items(first: 10) {
edges {
node {
... on Campaign {
id
name
}
}
}
}
}
}
}
}
Mutations often require information that you can only find out by performing a query first. This example shows two operations:
query FindPlayerID {
organization {
players(first: 1) {
nodes {
id
}
}
}
}
mutation UpdatePlayerTag {
updatePlayerTags(input: { id: "ID from query above", tags: ["tag one"]}) {
player {
id
name
tags
}
}
}
Although you can include a query and a mutation in the same Explorer window if you give them names (FindPlayerID and UpdatePlayerTag in this example), the operations will be executed as separate calls to the GraphQL endpoint. It's not possible to perform a query at the same time as a mutation, or vice versa.
Let's walk through the example. The task sounds simple: update the list of tags given to a player.
So how do we know to begin with a query? We don't, yet.
Because we want to modify data on the server (update the tags of a player), we begin by searching the schema for a helpful mutation.
The reference docs show the updatePlayerTags mutation, with this description: Updates the player tags.
That's exactly what we want!
The docs for the mutation list three input fields:
clientMutationId (String)id (ID!)tags ([String!]!)
The !s indicate that id and tags are required fields.
A required tags makes sense: we want to update the player tags, so we'll need to specify which tags we want to be set.
But why is id required? It's because the id is the only way to identify which player we want to update.
This is why we start this example with a query: to get the player ID.
Let's examine the query line by line:
query FindPlayerID {
Here we're performing a query, and we name it FindPlayerID.
Note that naming a query is optional; we give it a name here so that we can include it in same Explorer window as the mutation.
players(first: 1) {
We specify the players connection, fetching the first player.
id
This is where we retrieve the id of the player to pass as the id input to the mutation.
When we run the query, we get the player id, which should be a string containing a series of alphanumeric characters such as "AuCj".
id returned in the query is the value we'll pass as the id in the mutation.
Neither the docs nor schema introspection will indicate this relationship;
you'll need to understand the concepts behind the names to figure this out.
With the ID known, we can proceed with the mutation:
mutation UpdatePlayerTags {
Here we're performing a mutation, and we name it UpdatePlayerTags. As with queries, naming a mutation is optional; we give it a name here so we can include it in the same Explorer window as the query.
updatePlayerTags(input: { id: "AuCj", tags: ["tag one", "tag two"] }) {
Let's examine this line:
updatePlayerTag is the name of the mutation.
input is the required argument key. There will always be a mandatory input type for each mutation.
{ id: "AuCJ", tags: ["tag one", "tag two"]} is the required argument value.
This will always be an input object (hence the curly braces) composed of input fields
(id and tags in this case) for a mutation.
The rest of the call is composed of the payload object.
This is where we specify the data we want the server to return after we've performed the mutation.
These lines come from the updatePlayerTags docs, which specifies two possible return fields:
clientMutationId (String)
player (Player!)
In this example, we return the required field (player),
from which we select the id, name and tags fields.
When we run the mutation, this is the response:
{
"data": {
"updatePlayerTags": {
"player": {
"id": "AuCj",
"name": "Sample",
"tags": ["tag one", "tag two"]
}
}
}
}
That's it! Check out that the player tags has been updated by going to the player page on the main website and checking the player tags there.
One final note: when you pass multiple fields in an input object, the syntax can get unwieldy. Moving the fields into a variable can help. Here's how you could rewrite the original mutation using a variable:
mutation($myVar:UpdatePlayerTagsInput!) {
updatePlayerTags(input:$myVar) {
player {
id
name
tags
}
}
}
variables {
"myVar": {
"id": "o2CA",
"tags": ["tag one", "tag two"]
}
}
There is a lot more you can do when forming GraphQL calls. Here are some places to look next: