Start a conversation

Forming calls with GraphQL in Aurora

In Aurora, GraphQL replaces REST API as the primary method of communication between your custom applications and the back end of your Community.

Using a GraphQL-powered API instead of REST API streamlines the developer experience, enabling multiple actions or queries to occur within a single endpoint and from a single request.

In addition, the Community GraphQL API makes it possible to specify data queries down to specific fields. This means that you can make more targeted requests and get back only the data that you need. This can be more efficient than making a call using a REST API, which will often return more data than you need.

Aurora's approach to data querying and distribution provides a flexible and streamlined way to expand your community's capabilities and provide a faster, more versatile experience for your users.

REST API has numerous endpoints; GraphQL API has a single endpoint:

https://[COMMUNITY-URL]/t5/s/api/2.1/graphql

The endpoint remains constant no matter what operation you perform.

Communicating with GraphQL

Because GraphQL operations can be as complex or as simple as you need, Khoros recommends using our GraphQL Documentation to generate GraphQL calls. You can also use cURL or any other HTTP-speaking library.

In REST, HTTP verbs determine the operation performed. In GraphQL, you can define with each operation whether it is a query, mutation, or both. So, the HTTP operation type is POST regardless of the type of operation(s) used in your request. The exception is an introspection query, a simple GET to the endpoint.

About query and mutation operations

There are two allowed operations in Khoros's GraphQL API. These are queries and mutations.

When comparing GraphQL to REST, query operations are the same as GET requests. They're used to retrieve information from the server. This request may or may not be accompanied by supporting data through a payload, such as a particular board ID or user name.

Mutations, on the other hand, do write-specific tasks similar to POST/PATCH/PUT/DELETE requests made using REST. They're intended to create, update, or delete information from the server.

These operations can be performed in the same request as queries. This is a common way contextual information is gathered and passed along between operations. While these operations can be performed together, they are functionally different.

About queries

GraphQL queries are specific to the data you want to retrieve. This means that you need to specify each bit of information you wish to have returned, including any nested objects, down to the field(s) you want to be returned.

The structure of queries follows this general pattern:

query {
  QUERY-FIELD {
    SCALAR-FIELD
    SCALAR-FIELD
    TYPE-FIELD {
      FIELD
      FIELD
      FIELD
    }
  }
  ENUM-FIELD
  SCALAR-FIELD
}

Standard fields found in GraphQL are scalars, types, and enums. You can find more information about GraphQL's structure and fields in the GraphQL Documentation.

About mutations

To form a mutation, three things must be present in the request:

  • Mutation name: Defines the type of modification you wish to perform.
  • Input object: Defines the data you are sending to the server. This data includes input fields and values passed as an argument to the mutation name.
  • Payload object: Defines the data you want to have returned by the API once the operation is complete. This object is passed as the body of the mutation name.

Here is an example of mutation structure:

mutation {
  name(input: "someName") {
    namePayload
  }
}

The input object in this example is nameInput, and the payload object is namePayload.

The input fields are placed within the input object, and the payload fields are placed in the payload object. The details about these fields are outlined in the mutation reference.

Working with variables

Variables are a timesaving tool that makes queries more dynamic and versatile. Their primary function is to reduce input object complexity as they contain a bundle of specific fields and values together in one place.

NOTE
The Query Explorer has a VARIABLES section built into the tool for variables to be entered. Enter any variables you want to use there, and avoid using the variables designation prior to the JSON object.

Here's an example query with a single fragment and variable:

fragment UserView on User {
    id
    login
    messagesCount
    solutionsCount
    kudosGivenCount
    kudosReceivedCount
}

query TopPosters ($exampleNumber:Int!){
    users (sorts: {
        kudosReceivedCount: { direction: DESC, order: 1 }
        messagesCount: { direction: DESC, order: 2 }
    }, first: $exampleNumber) {
        edges {
            node {
                ...UserView
            }
        }
    }
}
{
   "exampleNumber": 2
}

There are three steps to using variables:

  1. Define the variable in its variables object. The variables object is not connected to the operation. Here's what that looks like:
{
   "exampleNumber": 2
}

The object, being valid JSON, can be as simple as a single field or as complex as entire objects with sub-objects and fields of differing types.

  1. Introduce the variable in operation as an argument. Here's an example:
query TopPosters ($exampleNumber:Int!){
  1. Use the variable within the operation. This can be done once or as many times as necessary. Here's how we used it:
, first: $exampleNumber) {

In this case, we're passing the integer 2 to the operation to indicate we want to return only the first 2 users that match our sort criteria.

Example query

Now that we have covered the basics of how queries work let's see a real-world example of a query used in Khoros Communities Aurora.

The following query returns information about the current user. This information includes the identifier, login information, and various user statistics stored in the User object.

fragment UserView on User {
    id
    login
    topicsCount
    messagesCount
    solutionsCount
    kudosGivenCount
    kudosReceivedCount
}

query Self {
    self {
        ...UserView
    }
}

Looking at the composition:

fragment UserView on User {
    id
    login
    topicsCount
    messagesCount
    solutionsCount
    kudosGivenCount
    kudosReceivedCount
}

We are requesting a specific set of information from the server. Rather than pass the entire fragment each time we're referencing it in the operation, we have established a fragment to list out the fields we want to have returned.

query Self {
    self {
        ...UserView
    }
}

Here, we're creating the query operation, naming the request Self, and requesting information from the UserView fragment to be returned in the response payload.

Example mutation

Mutations create new content, modify existing records, or initiate changes to Community data. In this example, we will create a new user using details provided by a defined variable.

fragment Error on Error {
    __typename
    message
    fields
}

fragment CreatedUser on User {
    id
    login
    email
}

mutation CreateUser($createInput: CreateUserInput!) {
    createUser(createInput: $createInput) {
        result {
            ...CreatedUser
        }
        errors {
            ...on DuplicateLogin {
                ...Error
            }
             ...on DuplicateLoginWithSuggestion {
                ...Error
                suggestedLogin
            }
             ...on BannedLogin {
                ...Error
            }
             ...on SameLogin {
                ...Error
            }
             ...on InvalidLoginValue {
                ...Error
            }
             ...on DuplicateEmail {
                ...Error
            }
             ...on BannedEmail {
                ...Error
            }
             ...on RequiredFieldNotSetError {
                ...Error
            }
             ...on LessThanMinValueError {
                ...Error
                min
            }
             ...on GreaterThanMaxValueError {
                ...Error
                max
            }
             ...on NotValidPossibleValueError {
                ...Error
                possibleValues
            }
             ...on PermissionDeniedError {
                ...Error
            }
        }
    }
}

{
    "createInput": {
        "login": "testUser1",
        "email": "testUser1@mysite.bla",
        "password": "example123abc!",
        "firstName": "User1",
        "lastName": "Test",
        "biography": "My Bio."
    }
}
NOTE
The variables section in the example above would not work in the Query Editor in our GraphQL Documentation.
In the Query Editor, variables must be passed in the VARIABLES section of the editor. The word variables would not be included, just the JSON. See the Working with Variables section for an example formatted for the Query Editor.

Let's break down the example:

fragment Error on Error {
    __typename
    message
    fields
}

fragment CreatedUser on User {
    id
    login
    email
}

Fragments are bits of logic that can be shared between multiple queries and mutations. In this case, we're defining the information about the user we expect to see returned upon a successful request and the information we want to receive in the event an error occurs.

mutation CreateUser($createInput: CreateUserInput!) {
    createUser(createInput: $createInput) {

In this section, we are initiating a mutation and giving it the name CreateUser. We are also pointing to the createInput variable we are setting in our request's variables, and utilizing the CreateUserInput argument.

On the next line, we're executing the createUser mutation using the information from the createInput variable.

result {
    ...CreatedUser
}

In this section, we're defining what we expect to see returned to us by the server. In this case, it's the id, login, and email address as listed in the CreatedUser fragment earlier.

errors {
    ...on DuplicateLogin {
        ...Error
    }
    ...on DuplicateLoginWithSuggestion {
        ...Error
        suggestedLogin
    }
    ...on BannedLogin {
        ...Error
    }
    ...on SameLogin {
        ...Error
    }
    ...on InvalidLoginValue {
        ...Error
    }
    ...on DuplicateEmail {
        ...Error
    }
    ...on BannedEmail {
        ...Error
    }
    ...on RequiredFieldNotSetError {
        ...Error
    }
    ...on LessThanMinValueError {
        ...Error
        min
    }
    ...on GreaterThanMaxValueError {
        ...Error
        max
    }
    ...on NotValidPossibleValueError {
        ...Error
        possibleValues
    }
    ...on PermissionDeniedError {
        ...Error
    }
}

This section lets the server know that if any of the listed errors occur, we expect to see the __typename, message, and fields information returned as defined in the Error fragment earlier.

Choose files or drag and drop files
Was this article helpful?
Yes
No
  1. ATLAS

  2. Posted
  3. Updated

Comments