Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
AlexHoffert
Associate
Associate
1,129

GraphQL

GraphQL is a query language for APIs that provides a type system to describe the data model in a structured way. It provides a strongly typed schema to describe the data model of an API in a structured way. Additionally, clients of an API use the query language to describe the data they want to request. GraphQL is similar to OData because they both provide a structured, typed schema for APIs and also enable clients to write powerful queries to request exactly the data they need using a single request.

The GraphQL adapter builds on the same metadata as the OData adapter.

Tutorial Setup

To keep things simple, we will be using the Graph sandbox endpoint. All you need to use this endpoint is your favorite GraphQL tool, for example the Altair GraphQL client, and your API key from SAP API Hub. To retrieve your API key, log into SAP API Business Hub, go to settings and copy your API key from the Show API Key button.

To make requests against the Graph sandbox, add your API key as an HTTP header in your requests: apiKey: <your API key>. Then you can use the Graph sandbox through the following endpoint:

 

https://sandbox.api.sap.com/sapgraph/graphql

 

Tutorial Specific Settings

Usually, GraphQL requests are done using POST requests. But, the sandbox is limited to GET requests, so we need to do GET requests for this tutorial. For most GraphQL clients this is just a setting and we are not creating too large request. A productive Graph instance is able to receive POST requests.

The API key is just used for the purposes of this sandbox endpoint and not relevant in the context of requests to productive Graph instances.

Also note that the URL of the Graph sandbox differs from the a productive URL of Graph.

Exploring the Schema

Every GraphQL service defines a schema that completely describes the set of types you can use for querying that service. GraphQL allows asking about what queries it supports using the introspection system

Most GraphQL clients automatically load and scan the complete schema automatically, so you might not need to do that on your own and use the client to explore the schema.

To see what types are available, you can query the __schema field:

 

{
  __schema {
    types {
      name
    }
  }
}

 

This will return all service specific and build-in types of the service:

 

{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "sap_c4c"
        },
        {
          "name": "sap_c4c_BusinessAttributeCollection_connection"
        },
        {
          "name": "sap_c4c_BusinessAttributeCollection"
        }
        ...
      ]
    }
  }
}

 

To get more information for a specific type, you can either extend the query above or do a separate query for the __type field. If you introspect the Query with

 

{
  __type(name: "Query"){
    name
    fields {
      name
    }
  }
}

 

you get all namespaces. These include Graph's build-in namespaces as well as user-defined custom namespaces for custom entities.

 

{
  "data": {
    "__type": {
      "name": "Query",
      "fields": [
        { "name": "sap_c4c" },
        { "name": "sap_graph" },
        { "name": "sap_hcm" },
        { "name": "sap_s4 "}
      ]
    }
  }
}

 

You can use a similar query to get the list of entities in a namespace, by using the namespace instead "Query" as the name.

To get more information in a single request, you can extend the query, for example to get all fields of sap_graph_SalesQuote with their name and kind, you simply query this:

 

{
  __type(name: "sap_graph_SalesQuote"){
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

 

returning all available properties of the sap_graph_SalesQuote:

 

{
  "data": {
    "__type": {
      "name": "sap_graph_SalesQuote",
      "fields": [
        {"name": "id", "type": {"name": "String", "kind": "SCALAR"}},
        {"name": "createdAt", "type": {"name": "DateTime", "kind": "SCALAR"}},
        {"name": "changedAt", "type": {"name": "DateTime", "kind": "SCALAR"}},
        {"name": "displayId", "type": {"name": "String", "kind": "SCALAR"}},
        {"name": "netAmount", "type": {"name": "Decimal", "kind": "SCALAR"}},
        {"name": "quoteType", "type": {"name": "String", "kind": "SCALAR"}},
        {"name": "soldToParty", "type": {"name": "String", "kind": "SCALAR"}},
        {"name": "_soldToParty", "type": {"name": "sap_graph_SalesDocumentReason", "kind": "OBJECT"}},
        {"name": "_soldToParty_id", "type": {"name": "String", "kind": "SCALAR"}},
        {"name": "items", "type": {"name": "sap_graph_SalesQuote_items_connection", "kind": "OBJECT"}},
        ...
      ]
    }
  }
}

 

We can see that a SalesQuote has several properties of different types, such as String or Decimal, but also more complex structured types like the items property, which is a collection of several sap_graph_SalesQuote_items, that are also defined in the same data model. The _connection types allow for retrieving data of entities of a to-many relation.

 

{
  "data": {
    "__type": {
      "name": "sap_graph_SalesQuote_items_connection",
      "fields": [
        {"name": "nodes", "type": {"name": null, "kind": "LIST", "ofType": {"name": "sap_graph_SalesQuote_items"}}},
        {"name": "totalCount", "type": {"name": "Int", "kind": "SCALAR", "ofType": null}}
      ]
    }
  }
}

 

Simple Example

Let us look at an example. We want to get a SalesQuote. For that, we create the following GraphQL request:

 

{
  sap_graph {
    SalesQuote (top: 1){
      nodes {
        id
        displayId
        netAmount
      }
    }
  }
}

 

In GraphQL, you always specify exactly what fields you would like to receive. In the example request above we selected the id, displayId and the netAmount of the SalesQuote. To make the query fast and have an easy to read result, we have added (top: 1) to get only one SalesQuote.

 

{
  "data": {
    "sap_graph": {
      "SalesQuote": {
        "nodes": [
          {
            "id": "c4c~1",
            "displayId": "1",
            "netAmount": "890"
          }
        ]
      }
    }
  }
}

 

Example With Nesting

We now want to get the items and the soldToParty of that SalesQuote as well, so we simply add these attributes to the query with the fields we would like to see.

Note that the attributes of the items attribute are encapsulated with nodes like it is done with the attributes of the SalesQuote as well. This has to be done for all list types. _soldToParty is a to-one association, so here the attributes are directly. The reason for this extra level is so that you can get the total amount of elements next to the actual elements of the list like here for the SalesQuote:

 

{
  sap_graph {
    SalesQuote (top: 1){
      totalCount
      nodes {
        id
        displayId
        netAmount
        _soldToParty {
          id
          name
        }
        items {
          nodes {
            itemId
            itemText
            product
            quantity
          }
        }
      }
    }
  }
}

 

This will result in the items of the SalesQuote and the associated soldToParty being returned. The total count of found SalesQuotes is now available as well:

 

{
  "data": {
    "sap_graph": {
      "SalesQuote": {
        "totalCount": 135,
        "nodes": [
          {
            "id": "c4c~1",
            "displayId": "1",
            "netAmount": "890",
            "_soldToParty": {
              "id": "c4c~10014",
              "name": "System Tec"
            },
            "items": {
              "nodes": [
                {
                  "itemId": "10",
                  "itemText": "Green Emission Calculator",
                  "product": "P300100",
                  "quantity": "1"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

 

Working with collections: filtering, ordering and pagination

When working with collections of entities, we typically want to filter the entities by some criteria or arrange them in a specific order. In Graph's GraphQL schema this is supported via the filter and order arguments. 

Let us continue with our example above. Say, we want to retrieve all SalesQuotes with a value greater than 100 U.S. Dollars. The value condition can be formulated with the filter rule netAmount: {ge: 100}. For the currency condition we need to compare it with a string for equality: netAmountCurrency: {eq: "USD"}. Filter rules that are in one filter object are combined with logical conjunction (and). If you want to combine rules with logical disjunction (or), you would pass multiple filters as a list in the filter argument.

In addition, we define an ascending ordering on the netAmount property: orderBy: {netAmount: asc}.

With top and skip we can additionally define a sliding window over the result to implement pagination. top specifies how many result entities should be returned: the page size. How many result entities should be skipped from the beginning of the ordering is defined with the skip argument.

 

{
  sap_graph {
    SalesQuote (
      top: 5,
      skip: 5,
      orderBy: {netAmount: asc},
      filter: {
        netAmount: {ge: 100},
        netAmountCurrency: {eq: "USD"}
      }
    ){
      totalCount
      nodes {
        id
        netAmount
        netAmountCurrency
      }
    }
  }
}

 

Summary

In this tutorial we had a look at using Graph with GraphQL. We covered all the features that you as a developer working with Graph need to know, like how to:

  • inspect the schema and structure of the business data graph
  • formulate complex queries
  • work with collections

GraphQL itself offers much more than what we showed in this tutorial. You can read more about it in the the GraphQL documentation and the Graph documentation .

You as a developer now have a second data protocol option you can use with Graph. The structure of all entities in the business data graph is exactly the same as if you access Graph with the OData protocol. You have one API to retrieve data in one unified format, no matter the source system.

________________________________________________________________
Alexander Hoffert, Senior Developer – Graph

Visit Graph on the SAP Community