Brad Tilton

8 minute read

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.

  1. 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.

  2. 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);
        }
    
  3. 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);
    
  4. 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);
    
  5. 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:

    2020-10-19-21-20-09.png

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.

  1. 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.

  2. 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
        });
    
  3. 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.

  4. Now we’re all set so I can run now-cli develop --o and see the output of this.

    2020-10-21-10-11-38.png

    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.

  1. 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.

  2. 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);
    
  3. Lastly, I need to add a few resolver mappings:

    2020-10-22-08-53-01.png

  4. 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
                                }
                            }
                        }
                    }
                }
            }
        `;
    
  5. 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
        });
    
  6. And after saving and rendering we have success!

    2020-10-22-08-57-59.png

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 1027 and talk to an expert!


Comments