Introducing GraphQL
GraphQL is an open specification for an API query language (thus the “QL” reference) that lets you make your integrations more responsive than ever by providing you with the ability to request the data you need and nothing more. GraphQL can also reduce the number of calls and associated round trips you are making by fetching all of the resources you need in a single, or fewer calls.
In short, GraphQL will measurably impact your integrations by reducing bandwidth overhead due to response payload sizes and also the number of calls being made.
API filters continue to be a key feature that should still be used to reduce resource usage when making REST API GET calls.
OIDC/OAuth2
Coupa’s GraphQL query endpoint is
authenticated using OIDC and authorization is handled using OAuth2 scopes. Go to Setup
> Integrations > Oauth2/OpenID Connect Clients and click the Create
button (or go to /oauth2/clients/new
) to create an OIDC client with the
appropriate OAuth2 read scopes for your queries (writing is not yet supported). We currently
only support client credentials grant types.
After creating an OIDC client, use the OIDC client information to retrieve an access token. We won’t go into discussing how to obtain an access token. Contact your Coupa Administrator or Coupa Support for steps on how to retrieve an access token with your OIDC client information.
GraphQL clients
After you have an access token, there are many tools available that can be used to make GraphQL requests to Coupa. These tools include curl, Postman, and GraphiQL.
In the following examples, we will be using GraphQL. One of the main advantages to using GraphQL is that it fetches the schema and allows you to explore the schema in a user friendly way.
Download GraphiQL from https://www.electronjs.org/apps/graphiql or, if you're a Mac user with Homebrew installed, use the following command:
brew install --cask graphiql
After installing GraphiQL, launch it and you’ll see something similar to the image below:
Things to note in the above image:
-
Provide your instance URL and append it with
/api/graphql
in the GraphQL Endpoint address bar. -
Edit the HTTP headers to add your access token to the Authorization header (See image below).
-
Click on Docs to expand to show the schema.
When editing the header, add an authorization header by clicking the Add Header button and the following information:
- Header name: Authorization
- Header value: bearer {your access token}
Make sure to prefix the Header value with bearer
or you won't be able
to authorize.
Schema
Expanding the Docs in GraphiQL will allow you to explore the schema. With the schema, you can find out what is queryable and the return types for the queries.
Mutations are not supported and should not be used.
Queries
With GraphQL, you can query specifically for the data you need. You can query for data for a single object, or a collection of objects.
Single objects
To query for a single object, use the singular form of the object name in lower camel case and specify the object ID. For example, user(id: 1), expenseReport(id: 1).
Data is returned in JSON format with the data for the fields queried.
Collections
To query for a collection of objects, use the plural form of the object name in lower camel case. For example, users, expenseReports.
Associations
GraphQL allows you to query for nested data:
The image above shows a query for a single expense report (this also works for a collection of objects). In the expense report, we are querying for who created it and additional information on the report’s expense lines. Notice that even within expense lines we are querying for nested data.
Custom Fields
Objects may have custom fields that can be queried by the field name customFields:
Custom fields can be any of a number of types (eg: text, datetime, user, etc…). The customFields field can reveal what the type is for that specific custom field, which will allow you to query for those types specifically using fragments.
Fragments
Fragments let you construct sets of fields,
and then include them in queries. For customFields
, you can specify
fragments at the type level:
In the above example, we specify two fragments: … on User { … } and … on StringValue { … }. The fragments tell Coupa to attempt to convert the custom fields to the specified types (if applicable). If it can be converted to that given type, then the fields requested will be returned.
Advanced filtering and querying options
Collection queries have the additional option parameter called query that further filters out responses. The format for the query option is in the form of a query string parameter that’s also used to filter out values in our REST APIs.
The above example uses "id[lt_or_eq]=5" for the query parameter which filters out ID values to less than 5. This can also be dropped straight into our REST APIs in the form of /api/invoices?id[lt_or_eq]=5.
Errors
When checking responses, you should always check to see if the JSON response has any errors. If there are errors, a message and details of the error are shown:
Introspection
You can also query GraphQL for the schema itself. This is what GraphQL does to generate the schema for its clients. Here is an example of typical introspection query:
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
args {
...InputValue
}query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation
onFragment
onField
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
onOperation
onFragment
onField
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
GraphQL using Postman
All GraphQL examples mentioned above can also be run in Postman by following these steps:
-
Generate an OIDC token in Coupa. See aaaaaaaaaaaaaaaaaaaaaaaa for details.
-
Once the access token is generated, use it to initiate GraphQL request with the following POSTMAN configuration:
Important notes
-
All requests are POST requests
-
All responses are in JSON only
-
Always check the response body for errors, even if the response code of 200 was returned