ServiceNow: Custom GraphQL APIs Part 1

With Paris, ServiceNow has introduced custom GraphQL APIs which enables us to create highly flexible, reusable, and cacheable APIs for integrations or front end engineering.

However, documentation and tutorials for this new technology is sparse for the ServiceNow community. For this tutorial, we will build a GraphQL API for the Automated Test Framework that will be iteratively improved through the tutorial

  • Parts of a GraphQL API
  • A basic example of returning data from a single record
  • how to connect schemas together to return even more data
  • how to return a list of related records

GraphQL has three types of operations: Query, Mutation, and Subscription.

Query provides the ability to read data. Mutation provides the ability to create, update, and delete data. Subscriptions are a provide the ability to subscribe to real time data.

ServiceNow supports two of the operation types: Mutation & Query.

For this tutorial, we’ll be covering just Query, which is the operation type used for reading data.


  • ServiceNow Instance on at least Paris
  • Postman application for testing

First step: creating the API

On the left hand nav go to System Web Services > GraphQL > GraphQL APIs and click New

This should take you to the GraphQL API (sys_graphql_schema) form.

GraphQL Schema form for a new schema

Name is just a user friendly naming field. Schema namespace is a unique system name for your API and is used to identify each GraphQL APIs.

The most interesting part of this form is the Schema field. The Schema field is the structure of your API. It does a couple things.

  1. It decides the available data & “structure” for return results
  2. It determines the available inputs & maps them to the schema

We’ll be creating two queries, one for an ATF test & one for an ATF suite. First, We’ll add the ATF test query.

ATF Test Query

First, lets update the schema

In this Schema, we have created an object type called atfTest that has four fields: active, id, name, and description. These fields will be populated by a JSON object from a scripted Resolver.

We also added an attribute to the Query Object; the attribute is called atfTest. It expects to have an input of type string called id and will return an atfTest object.

This is the skeletal structure of our API. However, it is not enough to return data.

We will need to add Scripted Resolvers which will be the muscle of our API and Resolver Mappings which will be the tendons that connect the Scripted Resolvers & Schema together. Script includes will help us provide consistency when interacting with each object type in the schema.

Scripted Resolver: getTestInformation

Like I said before, the Scripted Resolver is the muscle of our API. It will do the work of processing inputs and generating the output for our API.

That said, a lot of our logic for the atfTest object will not be stored in the Scripted Resolver, but rather in a Script Include called ATFGraphQLAPI. This will allow us to reuse this logic and provide us a single place to update our Logic when we need to update the atfTest object.

Scripted Resolvers should handle what data gets used for populating a particular path in your Schema. You should have ONE function (for each Object Type) that will generate the structured object that will be grafted onto each Object Type.

Script Include: ATFGraphQLAPI

Mapping values to the Schema

Now that we have the the schema, scripted resolver, and the script include; Its time we attach the scripted resolver to the schema and have our first working version of our API. We will use a Resolver Mapping to connect a Path to our scripted resolver.

GraphQL Schema for atf API

The paths available follow the form <object>:<attribute>. For example, Query:atfTest or atfTest:active

GraphQL Resolver Mapping with Path=Query:atfTest and Resolver=getTestInformation

Resolver mappings allow us to map our scripted resolvers to the paths of our schema. We don’t have to map everything. Since our Scripted Resolver return an object with attributes that mirror the atfTest schema’s attributes, the GraphQL engine will automatically map them for us

However, You may have noticed the @source(value: “sys_id”) in our atfTest schema. That allows us to tell the GraphQL engine to map an attribute with a different name to the id object. Its not really necessary here, but it allows us to provide a more intuitive name for traditional developers who may not be familiar with sys_ids.

First Functional API

Now, we can test our API with a REST call. Personally, I like to use Postman over the REST API explorer.

For your own test, you will want to replace the x47399 with your namespace, replace {{test_id}} with your test’s sys_id, and {{api_host}} with your servicenow instance’s URL. Here is the response:

If you have followed along correctly, we have our first functional API! Congratulations!

You may be thinking “That’s dumb, why do this over just using the Table API” Stick with me, it gets WAY better.

Adding Test Results

First, lets update our Schema

There are quite a few new additions here. We’ve added the atfTestStatus Object Type, the atfTestResultList Object Type, and a new attribute to the atfTest called testHistory.

The testHistory attribute and atfTestResultList Object Type will be how we return a list of objects. The testHistory attribute is a little more complicated.

The (limit: string) will add an input called limit, which will allow us to control how many test results are going to be returned by the query. The @source(value: “sys_id”) will pass ONLY the sys_id of the Scripted Resolver instead of the entire schema object.

This is particularly useful functionality if you want to use the same scripted resolver against multiple paths. For example if we had a User Object type & an Incident Object Type, the @source to map multiple user field to the same User Object Scripted Resolver.

Lets add a new Scripted Resolver & Resolver Mapping and update our Script Include so we can get our test results.

GraphQL Resolver Mapping with Path=atfTest:testHistory and Resolver=getTestHistory

Now, lets check out how new version works!

Awesome! Now, we are able to get the details of our test & our test history in a single API call, but we can take it even further.

Adding a Test Suite Query

lets add a Test Suite Query by updating our Schema.

We’ve added two Object Types, atfTestSuite and testList, and we have added an attribute to the Query Object type. This will allow us to query for a test suite rather than a test.

Now lets add our Scripted Resolvers:

Scripted Resolver for Getting Suite Information
Script for getting this an array of Tests related to a Test Suite

Lets update our Script include

And then add our Mappings

Resolver Mapping with Path=Query:atfTestSuite and Resolver=GetSuiteInformation
Resolver Mapping with Path=atfTestSuite:tests and Resolver=getTests

Opinion on Two ATF Features

Before we get into testing our new API, this ATF suite query is a bit opinionated.

There are two features in ATF that I think add needless complexity; Specifically, child Test Suites and the input_filter for suites. I have purposefully NOT added support for these features in this API.

Due to how ServiceNow presents suite results, child test suites obfuscates any failing tests and adds a need for recursive logic to check for tests related to a parent suite. Adding tests directly to suites is much simpler and more visible.

The input_filter feature allows admins/QAs/devs/whoever to specify a query that identifies tests to run. This means a change to the query or a change to a test could change the tests related to a suite.

This makes identifying suites a test is related to or changes to a suite’s tests invisible or difficult to track. You could have 100 tests in a suite one day and 100 the next day, but be totally unaware that tests have been removed & added.

Testing our Suite API

Here is our query, notice the change at line 4 which points to the atfTestSuite object type rather than atfTest object type.

Here is our result.

200 result from ATFTestSuite query


You may have noticed that the history array is empty because that test has no result. ServiceNow purges results after a certain amount of time.

In Summary

Congratulations! We now have two GraphQL API queries for getting our Test Suites, tests, and the test results. You should now have a fundamental grasp on Queries with GraphQL APIs.

Next Steps

In two follow up articles, I will cover additional GraphQL concepts that are available in ServiceNow: Mutations and Type Resolvers. I will also provide an update set containing a production ready ATF GraphQL API.

There are also some concepts I won’t cover yet because ServiceNow does not support them: Subscriptions.

Additionally, I will also cover how GraphQL significantly changes frontend development in an article on Reactjs in ServiceNow.

I like helping people with Code & Math. I try to make everything I do a version of that.