Home / Basics / Highnote API

Intro to GraphQL

Why GraphQL

GraphQL is a query language for APIs that allows clients to request the exact data they need and nothing more. The Highnote API uses GraphQL to provide a flexible, modern, and secure integration experience. Highnote’s GraphQL offers several benefits:

  • Reduced data transfer: Specify the exact data you need.
  • Simplified client code: Developers can simplify and easily maintain their code without relying on multiple endpoints or parsing logic.
  • Powerful querying: Use nested queries, filtering, sorting, and pagination to find the exact data you need.

To start using the Highnote API, we recommend using the Highnote GraphQL Explorer. You can also use one of our recommended clients and codegens. Using the Highnote API covers Highnote API basics like request URLs, authentication, and more.

The following snippet represents a basic GraphQL ping query:

A basic query
query HelloHighnote {
  ping
}

How it Works

At a high level, GraphQL starts with a defined type system that describes the types of data available on the server. The type system is used to handle queries and mutations from clients.

When a client sends a query to the server, the server parses it and validates it against the schema. The server then executes the query by resolving each field using a set of resolver functions. Resolver functions are responsible for retrieving the data from the underlying data sources. The resolver functions are executed in parallel, allowing the server to retrieve all required data efficiently.

Once all resolver functions have been completed, the server assembles the results into a single JSON object and sends it back to the client.

GraphQL Concepts

Operations

GraphQL provides three operations. Operations are actions you can take against the API. The GraphQL operations are as follows:

  • Query: Used to fetch data
  • Mutation: Used to write data
  • Subscription: Used to write data in real-time

The Highnote API does not currently use Subscriptions.

Types

GraphQL is a specification that provides a type system. The GraphQL type system provides five scalar types or primitive values:

  • Int: Represents a signed 32‐bit numeric non‐fractional value
  • Float: Represents signed double‐precision fractional values as specified by IEEE 754
  • String: Represents textual data, represented as UTF‐8 character sequences
  • Boolean: Represents true or false
  • ID: Represents a unique identifier

GraphQL also allows for custom scalar types.

Introspection

GraphQL APIs have an introspection system, meaning they publish information about their capabilities to clients. The Highnote API is fully introspectable to authenticated users in the Test Environment.

Tools like graphiql use introspection to render documentation, type hints, and client-side validations. You can try introspection with the Highnote GraphQL Explorer.

Node Queries and Global IDs

The Highnote API uses Node queries and Global IDs to look up individual objects.

In GraphQL, the Node query is a pattern that allows clients to pass a global ID without specifying the entity it represents. Instead of fetching objects by resource, any object in the system that implements the Node interface can be retrieved with one query. Refer to the following query for an example:

query NodeQuery {
  node(id: "PAYMENT_CARD_ID") {
    ... on CardProduct {
      __typename
      id
      name
    }

    ... on PaymentCard {
      __typename
      id
      last4
    }
  }
}

You must provide a fragment for each selection set for each type you may expect. A selection set represents the data you want to receive in the response.

In the previous example, if the ID resolved to a PaymentCard, the response would look like this:

{
  "data": {
    "node": {
      "__typename": "PaymentCard",
      "id": "PAYMENT_CARD_ID",
      "last4": "1234"
    }
  }
}

You can see which types in the Highnote API implement the Node interface by running this query:

{
  __type(name: "Node") {
    possibleTypes {
      name
      description
    }
  }
}

Pagination

When querying listed data, limiting the number of records returned in a response is helpful. Doing so allows for faster response times from the server and smaller payloads on the client.

The Highnote API automatically paginates data following the Relay Cursor Connections Specification. In this pagination specification, the edges field is used to obtain a list of items. Each item has a cursor specifying its position in the list and a node containing the requested fields.

Let’s look at a common use case using card products to understand better how pagination works in the Highnote API. Let's say you need a list of your organization's card products, and each card product in the list must include its id, name, usage, and vertical. Here is what the query would look like:

query ListCardProducts($first: Int, $after: String) {
  cardProducts(first: $first, after: $after) {
    edges {
      cursor
      node {
        __typename
        id
        name
        usage
        vertical
      }
    }
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
  }
}

Notice the variables we provide to the query: $first and $after. These variables let the Highnote API know which records to return. Most queries in the Highnote API will assume a default value of 20 for the first variable if one is not provided by the user.

For example, to return a list of the ten most recently created card products for an organization, provide the first variable in your query:

{
  "first": 10
}

Now that a first variable has been provided, the query returns the ten most recently created card products:

{
  "data": {
    "cardProducts": {
      "edges": [
        {
          "cursor": "dD0yMDIyLTEyLTIwVDE4JTNBMTUlM0E1NS4wOTUwMDAwMDBaJmk9cGRfY2YwNGNjYzc1YzlhNDNmYzhlOWViMTVlMmMxNGY5NzY",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 1",
            "usage": "MULTI_USE",
            "vertical": "COMMERCIAL_DEBIT"
          }
        },
        {
          "cursor": "dD0yMDIyLTEyLTA5VDE3JTNBMjAlM0EzOC41NzQwMDAwMDBaJmk9cGRfMThhZTE1YWJlZWMzNGMwN2E1ODdhMzVlNGIxNjA0Njk",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 2",
            "usage": "SINGLE_USE",
            "vertical": "AP_INVOICE_AUTOMATION"
          }
        },
        {
          "cursor": "dD0yMDIyLTEyLTA5VDE3JTNBMjAlM0EzMC43ODEwMDAwMDBaJmk9cGRfMzg3NTI3Y2ViMWY5NGYwNzg3NDE2ZTViNDAyZDU4NjM",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 3",
            "usage": "MULTI_USE",
            "vertical": "AP_INVOICE_AUTOMATION"
          }
        },
        {
          "cursor": "dD0yMDIyLTEyLTA5VDE3JTNBMTElM0EwNC4xNTAwMDAwMDBaJmk9cGRfMmM4N2JlNmViNGViNDMwYWFjZDU3Y2Y0NWI2ODI4NDE",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 4",
            "usage": "MULTI_USE",
            "vertical": "AP_INVOICE_AUTOMATION"
          }
        },
        {
          "cursor": "dD0yMDIyLTEwLTMxVDIwJTNBMTUlM0E0Ni40NDUwMDAwMDBaJmk9cGRfNjI0NDc3YjcyZDljNGYzZWI5NWU1N2Q0ZDM2NzFhOWE",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 5",
            "usage": "MULTI_USE",
            "vertical": "GENERAL_PURPOSE_RELOADABLE"
          }
        },
        {
          "cursor": "dD0yMDIyLTEwLTMxVDIwJTNBMTUlM0EyMC44MTEwMDAwMDBaJmk9cGRfY2NkMGQxZTE0NzBmNDkxMjlhZWI3YzY3NTQyODYwOWM",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 6",
            "usage": "MULTI_USE",
            "vertical": "GENERAL_PURPOSE_RELOADABLE"
          }
        },
        {
          "cursor": "dD0yMDIyLTEwLTMxVDE1JTNBNDMlM0EyNy45NjUwMDAwMDBaJmk9cGRfMDAyOTBmYzQxMWFlNDk0N2FmYTVhNjA5NDQ3ZTFiNzE",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 7",
            "usage": "MULTI_USE",
            "vertical": "GENERAL_PURPOSE_RELOADABLE"
          }
        },
        {
          "cursor": "dD0yMDIyLTA5LTI5VDIwJTNBNDElM0ExOC45MTAwMDAwMDBaJmk9cGRfYmUyNTNjZDU2ZjAyNDkwMThjZTFjMjE2ODU5NmE4NmY",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 8",
            "usage": "MULTI_USE",
            "vertical": "GENERAL_PURPOSE_RELOADABLE"
          }
        },
        {
          "cursor": "dD0yMDIyLTA5LTIzVDE0JTNBMzElM0ExMy43NTgwMDAwMDBaJmk9cGRfZDAzYmZlMjcxOTY3NDRlMmJhMGQxMjRkYzJiN2I3ZDM",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 9",
            "usage": "MULTI_USE",
            "vertical": "SECURED_COMMERCIAL_CREDIT"
          }
        },
        {
          "cursor": "dD0yMDIyLTA5LTIzVDE0JTNBMzAlM0E1OS43ODcwMDAwMDBaJmk9cGRfMTgzNTI4NDgwNjhkNGFhMDg5Nzg5NTVlM2NkNmQzZGI",
          "node": {
            "id": "CARD_PRODUCT_ID",
            "name": "Card 10",
            "usage": "MULTI_USE",
            "vertical": "SECURED_COMMERCIAL_CREDIT"
          }
        }
      ],
      "pageInfo": {
        "hasPreviousPage": false,
        "hasNextPage": true,
        "startCursor": "dD0yMDIyLTEyLTIwVDE4JTNBMTUlM0E1NS4wOTUwMDAwMDBaJmk9cGRfY2YwNGNjYzc1YzlhNDNmYzhlOWViMTVlMmMxNGY5NzY",
        "endCursor": "dD0yMDIyLTA5LTIzVDE0JTNBMzAlM0E1OS43ODcwMDAwMDBaJmk9cGRfMTgzNTI4NDgwNjhkNGFhMDg5Nzg5NTVlM2NkNmQzZGI"
      }
    }
  }
}

You can add the after variable to your query to request the next ten card products. In this case, the after variable will be the cursor of the last item in the previously returned list.

This value is the endCursor value in pageInfo on the previous response. Updating the query variables, we now have the following:

{
  "first": 10,
  "after": "dD0yMDIyLTA5LTIzVDE0JTNBMzAlM0E1OS43ODcwMDAwMDBaJmk9cGRfMTgzNTI4NDgwNjhkNGFhMDg5Nzg5NTVlM2NkNmQzZGI"
}

Performing the query again with the updated variables will return the next ten items in the list after the item with a cursor value specified in after.

Making Requests

To learn more about making Highnote specific calls, see Using the API.

GraphQL is transport agnostic, and the Highnote API is served as JSON over HTTP(s). If you are familiar with making HTTP requests that send and receive JSON, a GraphQL call should feel familiar.

GraphQL does not use HTTP verbs (GET, PUT, POST, etc.) or have multiple endpoints per resource. Instead, you make POST requests to a single endpoint (/graphql) using specific queries or mutations.

Both GET and POST requests are valid, but it is recommended to use POST requests when interacting with the Highnote API.

Your request should contain a JSON body with the following:

  • query: The actual operation you want to run when making a request
  • variables: Used to pass dynamic data in a request (optional)
  • operationName: Used to specify an action in the case multiple queries are sent (optional)

In the following example, we want to call the ping query, just the Hello World call in the Highnote API. The ping query to the Highnote API will look like this:

curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic <API_KEY>' \
--data '{"query":"query Ping {\n  ping\n}"}' \
https://api.us.test.highnoteplatform.com/graphql

and will return a JSON payload with the following format:

{
  "data": {
    "ping": "pong"
  },
  "extensions": {
    "requestId": "REQUEST_ID"
  }
}

Clients and Codegen

The GraphQL ecosystem is dynamic and strongly enhances the experience of developers and integrators. As a result, GraphQL supports innovation and modern approaches to development.

Desktop Clients

When developing with GraphQL APIs, using a GUI tool that leverages introspection to provide documentation and type hints is helpful. Some of the tools we recommend using include:

Editor Plugins and Browser Extensions

Plugins are available for various IDEs to make your development experience more seamless:

Codegen Tools

There are several tools to generate code from GraphQL APIs. We recommend using GraphQL Code Generator to generate TypeScript definitions and SDKs for use with GraphQL.

Provide Feedback

Was this content helpful?