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 aVARIABLES
section built into the tool for variables to be entered. Enter any variables you want to use there, and avoid using thevariables
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:
- 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.
- Introduce the variable in operation as an argument. Here's an example:
query TopPosters ($exampleNumber:Int!){
- 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 theVARIABLES
section of the editor. The wordvariables
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.
ATLAS
Comments