Working with GraphQL

GraphQL Query image


Introduction

The Linx GraphQLQuery function allows you to execute a query against a GraphQL server and takes care of the necessary schema validation, input objects as well as resulting data.

GraphQL queries access not just the properties of one resource but also follow references between them. While typically working with REST APIs, you require making several requests to multiple URL endpoints and then using the returned data to then fetch the next needed related data in a subsequent request. GraphQL APIs, on the otherhand , you are able to replace multiple REST requests with a single call to fetch the data you specify. The advantage of this is that you limit your bandwidth usage as well as lessening the amount of steps needed to achieve your goal. A more in depth comparison can be found in this HowToGraphQL article.

In this article:

  • GraphQLQuery function properties configuration
  • Executing GraphQL queries
    • Writing a basic query
    • Defining input parameters
    • Handling responses

This support article uses examples from the GitHub GraphQL API as a reference, if you want to follow along practically, you can read-up more about getting started with the GitHub GraphQL API.


GraphQLQuery properties

Authentication

Like all APIs, you need to authorize any requests against the server, most typically this is done via a Bearer token.

You can add your authentication details to the GraphQLQuery function:
image

Server URL

All requests made to a GraphQL server are made to the same url (unlike REST endpoints).

You can set this URL as a $.Setting in your Solution:

You can then reference this $.Setting value whenever you use a GraphQLQuery function in your Solution:

image

Server Schema

The Schema is the definition of all the available methods and data structures which one can execute queries against. This definition is used by Linx in order to construct the relevant input and outputs of the query. You are able to manually paste the schema into this property or alternatively you are able to fetch the schema from the chosen GraphQL server.

Fetching the Schema

In order to retrieve the whole schema of the GraphQL server, you can fetch the schema using the built-in GraphQL Schema editor.

To open the GraphQL Schema editor, expand () the Schema property of the GraphQLQuery function:

image

Complete the server and authentication properties, then click FETCH SCHEMA.

Note: The Server URL, Authentication Scheme, Authentication Token and Headers properties in the GraphQL Server Schema editor popup are only used to retrieve the database schema, not to execute the query at run-time.

If you have already provided values for the run-time properties, then those values will also display in the matching properties in the GraphQL Server Schema editor. Values that are only resolved at run-time i.e. a local String variable building up a URL using expressions as the Server URL will not be populated in the GraphQL Server Schema editor.

Any property values in the GraphQL Server Schema editor (except for the actual schema definition) will not be saved or used at run-time.

The schema will be retrieved from the GraphQL server and saved to the GraphQLQuery function.


Executing GraphQL queries in Linx

GraphQL overview

Queries

The query type defines read-only GraphQL operations that retrieve data from the server.

Mutations

The mutation type defines GraphQL operations that change data on the server and are followed by a fetch. Mutations require inputs objects of some kind whether a basic type or complex object.

Objects

Objects in GraphQL represent the structure of the resources you can access. Linx uses the definition of these objects to create the necessary input and output data structures for you to use in your Solution.

Enums

Enums represent possible sets of values for a field. So for example, the RepositoryVisibility of a Repository is an Enum which has the possible values of ‘INTERNAL’, ‘PRIVATE’ or ‘PUBLIC’. In Linx, Enums do not have an equivelant and thus you will need to explicitly type out the chosen option as a String.

Scalar types

Scalars are primitive values: Int , Float , String , Boolean , or ID . The Linx basic types will be translated into the relative GraphQL scalar type when used in the GraphQLQuery function. For additional types such as URL , these are converted to a String.


Writing a basic query

Let’s take the below simple query which retrieves a list of repository names for the authenticated user from the GitHub GraphQL server with a hardcoded limit of 30 results:

query repositories  { 
    viewer { 
        login
        repositories(first: 30) {
            edges {
                node {
                    name
               }
            }
        }
    }
}

You just need to add this syntax into the Query property of the GraphQLQuery function and run your process and the query will execute.

Note: Building the query syntax outside of the GraphQLQuery function and passing it in dynamically is not possible, therefore you must explicitly type out the query syntax inside the Query property of the GraphQLQuery function.

Input Parameters

You are able to define custom input parameters for a query which enables you to pass data values into the GraphQLQuery function from other places in your Linx Solution.

Input parameters are indicated by using the GraphQL Query syntax’s$ character, followed by a name (this can be any name) and declaration of the subsequent type like below:

$customerId: String

Linx will then expose the necessary fields in the GraphQLQuery properties to pass in values to the referenced input parameters.

Using the input parameter in a query/mutation takes the following structure:

mutation CustomMutationName ($ParamName1: type , $ParamName2: type ) {
  mutationName(input: {field1: $ParamName1 , field2: $ParamName2} ) {
    MutationNamePayload
  }
}

Defining basic input types

If you require to use a basic type of input parameter in your query, you must define the expected type of value that is needed. You are able to define several input values which you can then use within your query syntax.

So using the same example, if you wanted to dynamically pass in the limit of results, you would do so by creating a name for an input parameter with the corresponding type.

Linx supports scalar type coercion from a Linx basic type into the accepted GraphQL scalar type defined in the schema (see scalar types above ↑).

In the above example, if we wanted to use a dynamic value to limit the result set in place of 30, we would first define a type of the higher level query:

query repositories ($repoCount: Int) { 

And then to use this value inside of the actual query, we would reference the input parameter i.e. $repoCount:

query repositories ($repoCount: Int) { 
    viewer { 
        login
        repositories(first: $repoCount) {
            edges {
                node {
                name}
            }
        }
    }
}

Linx will then pick this up and display an input parameter property for you to reference a value for, which can either be hardcoded or reference a dynamic value such as a $.Setting.

GraphQLQuery input parameter

By default, every type is nullable - it’s legitimate to return null as any of the scalar types. Use an exclamation point to indicate a type cannot be nullable, so Int! is a non-nullable integer which means that it is required for the query to execute. Linx will display that a field is required with a red X next to the field name.

Required input parameter

Note: When a field displays the value null, this will pass in an empty String (“”) value. If you want to explicitly pass in a NULL value then you must set the value to =$.System.Null.

Complex objects

You are able to reference complex objects as the input for the query, as long as it is a valid object in the GraphQL Server Schema.

Lets take the example mutation of creating a new repository on GitHub.

This mutation requires input fields which are part of the CreateRepositoryInput object. To create a custom object in GraphQL, you need to wrap the fields in curly brackets {}.

So first, lets declare the input values for the mutation.

mutation {
  createRepository(input:{clientMutationId: "1234", description: "Hello World! in GraphQL", name: "hello-world-graphql", visibility: PRIVATE }) {
    clientMutationId
    repository { 
        name
        description
        visibility
    }
  }
}

And lets say we wanted to dynamically pass in each of these field values.

So we would need to declare them as variable inputs.

First, we need to create a higher level query object, in this case I’ve called it createRepo . Then you need to define each of the needed input parameters like we did previously (note how the $repoName is used in the required field so I’ve indicated that with the ! character next to the type). I’ve then referenced the input parameters in the appropriate fields in my mutation.

mutation createRepo ($clientMutationId: String , $desc: String , $repoName: String!){
  createRepository(input:{clientMutationId: $clientMutationId, description: $desc , name: $repoName, visibility: PRIVATE }) {
    clientMutationId
    repository { 
        name
        description
        visibility
    }
  }
}

Linx then exposes these fields as inputs for me to add values dynamically to:

Input fields

So as you can see you are able to use the basic types to create a complex object to submit in the mutation.

However, the beauty of GraphQL is that all the objects are already defined in the GraphQL Server Schema which we have already retrieved.

In this case, you need to submit the fields of a CreateRepositoryInput object, but instead of typing out each of the needed fields as well as their corresponding data types you can just declare an input which is of the type CreateRepositoryInput! (this must be a valid type as defined in the GraphQL Server Schema).

mutation createRepo ($repoDetails: CreateRepositoryInput! ){
  createRepository(input: $repoDetails) {
    clientMutationId
    repository { 
        name
        description
        visibility
    }
  }
}

When you reference a complex object, Linx will expose the necessary object according to the predefined schema definition for that object.

CreateRepositoryInput fields
You can then edit the individual fields using the values editor.

Note: When setting individual input fields which are part of an object, you must explicitly set all the fields to a value.

If you only want to submit certain fields of an object and not the entire object, you need to explicitly set the unneeded field to $.System.Null.

Like mentioned before the null value that is displayed by default will submit an empty String (“”) value instead which will cause that field to be used in the query which may cause unexpected consequences.


Explicitly set NULL values - these fields won’t be submitted

If you wanted to dynamically determine whether or not to submit certain fields, you could use expressions inside of the type editor when setting the individual fields:


Response objects

The result of a query adheres to the server Schema and Linx will automatically handle these as the needed structure. You can then access the individual objects of the response using a variety of Linx methods like you would access any other data object i.e. using a ForEach function to loop through a list of nodes.

For example, the original example query to retrieve a list or repository names will have the below result:

If we wanted to return the ID of each repository we just need to alter the query syntax:

query repositories ($repoCount: Int!) { 
    viewer { 
        login
        repositories(first: $repoCount) {
            edges {
                node {
                   name
                   id
                 }
            }
        }
    }
}

And then the new response structure will be handled by Linx automatically:

Mapping the response object to a Linx custom type

In some cases, you may want to “wrap” a GraphQLQuery call inside its own Linx function and then returned a structured type. In order to return the appropriate data structure for you to work with outside the function, you must create a Linx type that maps to the same structure as the result of the query execution. A simple ‘trick’ to do this is first write out the particular data structure to a String type, copy the text and then import the JSON as a custom type.

So for example, lets say we wanted to return the result of the GraphQLQuery call as the result of the function.

First, we would add a String type to the function and assign it the value of the edges object from the repositories object from the result:

image

When the function is debugged, you can see the result of the GraphQLQuery execution is converted to a JSON string which is then copied:

You can then directly import the JSON as a custom type.

This will then create a data structure which matches the JSON:

image

Now that you have a Linx type that matches the structure, you are able to use this type as a result of the function:

image

Finally, you are able to return the GraphQLQuery result as a result of the function by assigning the output value of the GraphQLQuery to the result of the function:

Assigning the result object

Now when the function is executed, the data will be returned from the function in a structured manner:


Linx sample Solutions

View Linx GraphQL samples on GitHub →


Additional external resources