As part of the Now Platform Paris release, ServiceNow has added a new capability called the GraphQL API Framework. This API framework allows you to create a custom GraphQL API to query record data from a component or third-party system.
What is GraphQL and why should I use it?
GraphQL is a web query language originally created by Facebook, but now open-source, optimized for client-side development. It aims to solve problems like under and over fetching of data, versioning, and endpoint sprawl.
The main benefits of GraphQL in ServiceNow are:
- Use introspection to discover fields and objects available to query
- Query the exact data you need
- Manage multiple queries from a single endpoint
- Integrate with third-party systems by making the schema public
How do you use the Now Platform’s GraphQL API Framework?
There are four main pieces involved in setting up a GraphQL API on the Now Platform. and you can find those broken down in the GraphQL Overview.
In this post we’ll walk through an example here where I’m going to make a GraphQL query from a component to a simple GraphQL API I’ve setup. The query will fetch incident data, including related data from the caller and the configuration item associated with the incident.
First we need to create a GraphQL API. Mine is named Incident, which makes the schema namespace incident and the application namespace will be inherited from your application’s scope or the instance company code if you’re in the global scope. We’ll need the namespaces when we form our query later. In the Schema field I have:
schema { query: Query } type Query { findById(id: ID!): Incident } type Incident { sys_id: DisplayableString number: DisplayableString state: DisplayableString short_description: DisplayableString description: DisplayableString opened_by: User @source(value: "opened_by.value") caller_id: User @source(value: "caller_id.value") opened_at: DisplayableString cmdb_ci: CMDB @source(value: "cmdb_ci.value") } type User { sys_id: DisplayableString first_name: DisplayableString last_name: DisplayableString company: DisplayableString } type CMDB { sys_id: DisplayableString name: DisplayableString install_status: DisplayableString } type DisplayableString { value: String display_value: String }
This is going to let me query the incident table for the specified fields and get data from the sys_user and cmdb_ci tables as well.
After I’ve saved I’m going to scroll down to my GraphQL Scripted Resolvers related list and create three total scripted resolvers for my three tables, incident, sys_user, and cmdb_ci. I’ll start with the incident resolver.
Name: Incident Resolver
Script:
(function process( /*ResolverEnvironment*/ env) { var incident = new GlideRecord('incident'); if (env.getArguments().id != null) incident.addQuery('number', env.getArguments().id); incident.query(); return incident; })(env); }
Now I’ll create a User Resolver:
(function process( /*ResolverEnvironment*/ env) { var userId = env.getSource(); var user = new GlideRecord('sys_user'); user.addQuery('sys_id', userId); user.query(); return user; })(env);
And then I’ll create the CMDB Resolver
(function process( /*ResolverEnvironment*/ env) { var ciID = env.getSource(); var cmdbCI = new GlideRecord('cmdb_ci'); cmdbCI.addQuery('sys_id', ciID); cmdbCI.query(); return cmdbCI; })(env);
Now that I’ve created my scripted resolvers I need to create resolver mappings. These will map my queries and fields to my scripted resolvers. I’ll create the following four resolver mappings:
Consuming GraphQL from a Component
Now that we’ve created the GraphQL API we’ll need to consume it from a component using the GraphQLEffect. I’m not going to get into installing now-cli and scaffolding your component in this post, but if you need help you can check out Getting Started with the Now Experience UI Framework or the Build My First Now Experience UI Framework Custom Component developer training course.
Before using it in the component you’ll need to install the GraphQLEffect by running
npm install @servicenow/ui-effect-graphql
. If you’re not sure how to do that it’ll work similarly to the HttpEffect which we went through in the Using the Http Effect in a Now Experience Component.The main file in my component is the index.js where we have the view, createCustomElement, actions, and behaviors all together for the sake of readability, even though our recommended practice is to split those out into separate files. In this file I’m using the COMPONENT_BOOTSTRAPPED lifecycle action to kick off my GraphQL query, and then I’m displaying the stringified response in the view.
import {createCustomElement, actionTypes} from '@servicenow/ui-core'; const { COMPONENT_BOOTSTRAPPED } = actionTypes; import {createGraphQLEffect} from '@servicenow/ui-effect-graphql'; import styles from './styles.scss'; import { GRAPHQL_QUERY, INCIDENT_NUMBER } from '../constants'; const view = (state, {updateState}) => { const {payload} = state; return ( <div> <h3>GraphQL Test</h3> <pre>{payload}</pre> </div> ) }; createCustomElement('x-85636-graphql-component', { actionHandlers: { [COMPONENT_BOOTSTRAPPED]: (coeffects) => { const { dispatch } = coeffects; dispatch('FETCH_GRAPHQL_INCIDENT', { id: INCIDENT_NUMBER }); }, 'FETCH_GRAPHQL_INCIDENT': createGraphQLEffect(GRAPHQL_QUERY, { variableList: ['id'], successActionType: 'GRAPHQL_SUCCESS' }), 'GRAPHQL_SUCCESS': ({action, updateState}) => { var payload = JSON.stringify(action.payload, null, 4); updateState({ payload }); } }, view, initialState: { payload: '' }, styles });
You’ll notice at the top of that index file we’re importing GRAPHQL_QUERY and INCIDENT_NUMBER from the following constants.js file:
export const INCIDENT_NUMBER = 'INC0010004'; export const GRAPHQL_QUERY = ` query ($id: ID!) { x85636 { incident { findById(id: $id) { sys_id { value } number { value } state { value display_value } short_description { value } opened_by { first_name { value } last_name { value } } cmdb_ci { sys_id { value } name { value } install_status { display_value } } } } } } `;
You can see here that I’m getting values and display values for various fields on the incident table as well as values from the user and cmdb tables. If you’re following along, the x85636 in my query corresponds with my Application namespace field from my GraphQL API and yours will be different.
Now we’re all set so I can run
now-cli develop --o
and see the output of this.You can see that we received data back in the same structure as the query we sent. We received either values, display values, or both depending on what we specified in the query.
Add another query
So far we’ve seen some of the flexibility of GraphQL, how the way we structure the query determines the structure of the data we get back, and how we’ve done very minimal scripting to set this up. Now we’ll show how we can manage multiple queries from the same endpoint thereby reducing endpoint sprawl, and also how we can add to the API without affecting those already using the API.
Right now we’re just querying the incident table and getting an incident by its number, but I also want to be able to query the problem table by its number from my GraphQL API.
First, I’ll go to my GraphQL API record and add another query type
findProblem(id: ID!): Problem
directly under the findByID in the schema. Then, I’ll add a problem type below the incident type.type Problem { sys_id: DisplayableString number: DisplayableString state: DisplayableString short_description: DisplayableString description: DisplayableString opened_by: User @source(value: "opened_by.value") opened_at: DisplayableString cmdb_ci: CMDB @source(value: "cmdb_ci.value") }
While I’m editing the schema I’m going to add the problem_id field to the incident type so my incident query can pull problem data as well:
problem_id: Problem @source(value: "problem_id.value")
If you want to read about how this works check out GraphQL Schemas and Types in the GraphQL Docs.
Next, I’ll add new scripted resolver called Problem Resolver.
(function process( /*ResolverEnvironment*/ env) { var problem = new GlideRecord('problem'); if (env.getArguments().id != null) problem.addQuery('number', env.getArguments().id); problem.query(); return problem; })(env);
Lastly, I need to add a few resolver mappings:
Now we can test. After confirming that my additions haven’t broken anything in my current api I’m going to add a couple of constants to my constants.js file to use for my new query:
export const PROBLEM_NUMBER = 'PRB0007601'; export const PROBLEM_GRAPHQL_QUERY = ` query ($id: ID!) { x85636 { incident { findProblem(id: $id) { sys_id { value } number { value } opened_by { first_name { value } last_name { value } } } } } } `;
Then in my index.js I’ll add the constants to the import statement and replace the old constants in my actionHandlers.
import {createCustomElement, actionTypes} from '@servicenow/ui-core'; const { COMPONENT_BOOTSTRAPPED } = actionTypes; import {createGraphQLEffect} from '@servicenow/ui-effect-graphql'; import styles from './styles.scss'; import { GRAPHQL_QUERY, INCIDENT_NUMBER, PROBLEM_GRAPHQL_QUERY, PROBLEM_NUMBER } from '../constants'; const view = (state, {updateState}) => { const {payload} = state; return ( <div> <h3>GraphQL Test</h3> <pre>{payload}</pre> </div> ) }; createCustomElement('x-85636-graphql-component', { actionHandlers: { [COMPONENT_BOOTSTRAPPED]: (coeffects) => { const { dispatch } = coeffects; dispatch('FETCH_GRAPHQL_INCIDENT', { id: PROBLEM_NUMBER }); }, 'FETCH_GRAPHQL_INCIDENT': createGraphQLEffect(PROBLEM_GRAPHQL_QUERY, { variableList: ['id'], successActionType: 'GRAPHQL_SUCCESS' }), 'GRAPHQL_SUCCESS': ({action, updateState}) => { var payload = JSON.stringify(action.payload, null, 4); console.log('Payload: ' + payload); updateState({ payload }); } }, view, initialState: { payload: '' }, styles });
And after saving and rendering we have success!
We didn’t actually do anything with the data in the component in this example, but hopefully this gives you a good starting point for creating your GraphQL APIs and consuming them from components.
CreatorCon
Check out the Getting Started with the Now Experience UI Framework CreatorCon 2020 workshop where we’ve added a bonus lab on incorporating GraphQL into the Now Experience component.
Have more questions about using GraphQL on the Now Platform? Attend the LIVE Ask the experts session at CreatorCon on 10⁄27 and talk to an expert!
Share this post
Twitter
Facebook
Reddit
LinkedIn
Email