logo

NJP

Getting Started - GraphQL API framework

Import · Dec 27, 2021 · article

Resolves interfaces and unions into concrete GraphQL types.

These functions are available on the TypeResolutionEnvironment object.

Resolver Mapping

Maps resolvers to fields in the schema.

Hey, let's build something now

Usecase: Develop a GraphQL API to perform the below operations

  1. Get Incident Number, Short description, and Category using sys_id
  2. Close an active Incident using sys_id and add the given Resolution code, Resolution notes, and update the category if given

Solution:

Note: Please go through the documentation to become familiar with the GraphQL basics on the Now Platform

Create a new GraphQL API

  • Navigate to System Web Services -> GraphQL -> GraphQL APIs
  • Click on 'New' and fill in the below details in order to create a new GraphQL API
  • * Name - Name of the schema
    • Schema namespace - The namespace (unique identifier) for the schema in the current application
    • Select relevant Security configuration. For this usecase, let's go with only 'Requires authentication
  • Click on 'Submit'

image

Build Schema

  • We see two object types Query and Mutation in the schema by default. These objects are the entry point for the operations we want to perform via the API.
  • 'Query' type is required for retrieving data.
  • 'Mutation' type is required for updating or deleting the data

Let's add the below operation in Query

type Query {
   getIncidentDetails(sysId: ID!): GetIncidentDetailsResult!
}

Let's define an Error interface that needs to be implemented by all types of Error responses

interface Error {
    errorType: String!
    errorMessage: String!
}

Define GetIncidentDetailsResult type

union GetIncidentDetailsResult = GetIncidentDetailsSuccess | GetIncidentDetailsError

Define GetIncidentDetailsSuccess

type GetIncidentDetailsSuccess {
    number: String!
    shortDescription: String!
    category: String
}

Define GetIncidentDetailsError

type GetIncidentDetailsError implements Error {
    errorType: String!
    errorMessage: String!
}

So, we are done with creating Schema for the getIncidentDetails query.

Here is the complete Schema as shown below

image

Let's create a new GraphQL Scripted resolver for the getIncidentDetails query named "Get Incident Details Resolver"

(function process( /*ResolverEnvironment*/ env) {
    var args = env.getArguments();
    var sysId = args.sysId;
    var gr = new GlideRecordSecure("incident");
    if (gr.get(sysId)) {
        return {
            number: gr.getValue("number"),
            shortDescription: gr.getValue("short_description"),
            category: gr.getValue("category")
        };
    }
    return {
        errorType: "IncidentRetrievalError",
        errorMessage: "Record sys_id is not valid"
    };
})(env);

Add a new GraphQL Resolver mapping as shown below. It tells the framework which Resolver script to execute for which query.

image

Create a new GraphQL Type Resolver to resolve the type of object returned in union GetIncidentDetailsResult

(function process( /*TypeResolutionEnvironment*/ env) {
    var obj = env.getObject();
    return obj.hasOwnProperty('errorType') ? 'GetIncidentDetailsError' : 'GetIncidentDetailsSuccess';
})(env);

Create a new GraphQL Type Resolver to resolve the type of concrete implementation returned in interface Error

(function process( /*TypeResolutionEnvironment*/ env) {
    var obj = env.getObject();
    var error;
    switch (obj.errorType) {
        case "IncidentRetrievalError":
            error = "GetIncidentDetailsError";
            break;
    }
    return error;
})(env);

We are done with creating Scripted Resolvers, Type Resolvers, and Resolver mappings for our first query getIncidentDetails

Let's test it out

I am using Insomnia for the demo purpose. Please feel free to use any other tool.

Success:

image

Error:

image

Now, we will build our first mutation.

Let's add the below operation in Mutation

type Mutation {
    closeIncident(closureDetails: CloseIncidentInput!): CloseIncidentResult!
}

Define CloseIncidentInput type input

input CloseIncidentInput {
    sysId: ID!
    resolutionCode: String!
    resolutionNotes: String!
    category: String
}

Define CloseIncidentResult type

union CloseIncidentResult = CloseIncidentSuccess | CloseIncidentError

Define CloseIncidentSuccess

type CloseIncidentSuccess {
    number: String!
}

Define CloseIncidentError

type CloseIncidentError implements Error {
    errorType: String!
    errorMessage: String!
}

So, we are done with creating Schema for the closeIncident mutation.

Here is the complete updated Schema as shown below

image

Let's create a new GraphQL Scripted resolver for the closeIncident mutation named "Close Incident Resolver"

(function process( /*ResolverEnvironment*/ env) {
    var args = env.getArguments();
    var sysId = args.closureDetails.sysId;
    var resolutionCode = args.closureDetails.resolutionCode;
    var resolutionNotes = args.closureDetails.resolutionNotes;
    var category = args.closureDetails.category;
    var gr = new GlideRecordSecure('incident');
    if (gr.get(sysId)) {
        gr.setValue('state', 7);
        gr.setValue('close_code', resolutionCode);
        gr.setValue('close_notes', resolutionNotes);
        if (category) {
            gr.setValue('category', category);
        }
        gr.update();
        return {
            number: gr.getValue("number")
        };
    }
    return {
        errorType: "IncidentClosureError",
        errorMessage: "Record sys_id is not valid"
    };

})(env);

Add a new GraphQL Resolver mapping as shown below

image

Create a new GraphQL Type Resolver to resolve the type of object returned in union CloseIncidentResult

(function process(/*TypeResolutionEnvironment*/ env) {
    var obj = env.getObject();
    return obj.hasOwnProperty('errorType') ? 'CloseIncidentError' : 'CloseIncidentSuccess';
})(env);

Update the GraphQL Type Resolver of interface Error

(function process( /*TypeResolutionEnvironment*/ env) {
    var obj = env.getObject();
    var error;
    switch (obj.errorType) {
        case "IncidentRetrievalError":
            error = "GetIncidentDetailsError";
            break;
        case "IncidentClosureError":
            error = "CloseIncidentError";
            break;
    }
    return error;
})(env);

We are done with creating Scripted Resolvers, Type Resolvers, and Resolver mappings for our first mutation closeIncident

Let's test it out

Success:

image

Error:

image

I hope that you enjoyed the article. Please let me know if you have any suggestions or comments.

View original source

https://www.servicenow.com/community/developer-articles/getting-started-graphql-api-framework/ta-p/2312207