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: