Developer Resources > GraphQL Primer

Core concepts

The idea behind this section is to explain some of the core concepts behind our GraphQL API.

Identifiers

The resources exposed through the API have new identifiers (id). These ones are like uuids, which makes them opaque and lot different than the ones you are used to getting from the REST API (legacy_id).

In order to help migrating from REST to GraphQL, legacy_id will still be valid for a period of time, so both id and legacy_id can be used when making requests.

If you have an existing legacy_id and want to obtain the corresponding uuid, you can also do that, by calling the query uuid and passing the legacy_id along with the name of the entity:

query { 
    uuid(legacy_id:100579698, entity: Order) { 
        data { 
            legacy_id 
            id
        }
    }
}

Pagination:

In the new GraphQL API, we use cursor based pagination, which means that when using connections, you will need to include the first or last argument, specifying the number of items you want to be returned (this is equivalent to limit). By doing this, you’ll be able to optimize your query and consume fewer credits.

Requesting the cursor field lets you get a reference to the position of a node within the connections. You can use that reference to obtain the next set of items (similar to  page, but more flexible)

Then, by requesting the pageInfo field we can determine if there are any more pages to request. The fields hasNextPage and hasPreviousPage are boolean fields that indicate if we’ll be able to paginate forward or backward. If both arefalse, there are no more results or pages.

Example: Let’s suppose you wanted to get the id and SKUs of all of the products on the account. And let’s also suppose you had more than 10000 products.
The query for this would be the following:

 

query {
  products {
    request_id
    complexity
    data {
      pageInfo {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      edges {
        node {
          id
          account_id
          name
          sku
        }
        cursor
      }
    }
  }
}

The response would look something like this:

{
  "data": {
    "products": {
      "request_id": "5d3a155d1ca7d3313fe21331",
      "complexity": 101,
      "data": {
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
          "endCursor": "YXJyYXljb25uZWN0aW9uOjk5"
        },
        "edges": [
          {
            "node": {
              "id": "UHJvZHVjdEluZm86MjYzMDAzNjY2",
              "account_id": "QWNjb3VudDo0Mjg1",
              "name": "Gift Cards $10.00 USD",
              "sku": "13828573298740"
            },
            "cursor": "YXJyYXljb25uZWN0aW9uOjA="
          },
          {
            "node": {
              "id": "UHJvZHVjdEluZm86MjYzMDAzNjY4",
              "account_id": "QWNjb3VudDo0Mjg1",
              "name": "Gift Cards $25.00 USD",
              "sku": "13828573331508"
            },
            "cursor": "YXJyYXljb25uZWN0aW9uOjE="
          },
...

The response for that will not contain the whole 10000 products, but the first 100.
For the next 100, you will have to use the cursor, indicating from what product to start.
As the response shows, we know that hasNextPage: true and endCursor: "YXJyYXljb25uZWN0aW9uOjk5". So the query for the next 100 products should be something like this:

query {
  products {
    request_id
    complexity
    data(after:"YXJyYXljb25uZWN0aW9uOjk5") {
      pageInfo {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      edges {
        node {
          id
          account_id
          name
          sku
        }
        cursor
      }
    }
  }
}

Note: For a brief but excelent explanation on how Pagination or Edges and Nodes work check graphql.org or check the following videos:

3PLs

When executing queries, you might want to fetch only data for a particular customer. You have two options for this:

  1. Requesting a token using your customer credentials and use it in any further operations made on behalf of that customer.
  2. Use your 3PL user and specify the customer_account_id parameter on the desired queries, when available.

When executing mutations that will modify your customer’s data you have two ways to achieve that:

  1. Requesting a token using your customer credentials and making the requests with that token
  2. Use your 3PL user and specify the customer account you are operating on behalf of by using the customer_account_id in each mutation’s input.

3PL customers

You can always check your customers list by doing:

query { 
    account { 
        customers { 
            edges { 
                node { 
                    id
                }
            }
        }
    }
}

or:

 query { 
    me {
    	id 
        email 
        account { 
            customers { 
                edges { 
                    node { 
			id
                    }
                }
            }
        }
    }
}

Error Handling & Support

When errors occur, they are reported on the errors section of the response, where an error code is shown.

Error Codes

Although not every error gets a specific code, the most common errors are the following:

Code

Error

3 Invalid Argument
5 Not Found
6 Already exists
7 Permission denied
9 Failed Precondition
14 Unavailable
20 FormatError
21 Service Not Available
22 Unexpected Error
30 Throttling Error

Always make sure to grab the request_id from the error’s detail. You can share it with our customer support and this will help us univocally identify your request along with its original payload.

There might be the case where there was no error but for some reason, you feel something didn’t go as expected. You can still get a request_id from any operation, just require the field request_id on every query/mutation and feel free to contact support with it.

For support, email developer@shiphero.com or join our Community

Consuming the API with different clients

Python

The API can be used from any client that supports GraphQL, here we’ll show examples to make some requests in python

Using gql

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
_transport = RequestsHTTPTransport( url='https://public-api.shiphero.com/graphql', use_json=True, )
_transport.headers = { "User-Agent": "Mozilla/5.0 (X11; buntu; " + "Linux x86_64; rv:58.0) Gecko/0100101 Firefox/58.0", "Authorization": "Bearer {}".format(YOUR_TOKEN), "content-type": "application/json", }
client = Client( transport=_transport, fetch_schema_from_transport=True, )
query = gql(""" { products { request_id complexity data(first: 10) { edges { node { id sku } } } } } """)
print(client.execute(query))

Using sgqlc

Using sgqlc

First, you need to generate the schema:

shell python3 -m sgqlc.introspection \
 --exclude-deprecated \
 --exclude-description \
 -H "Authorization: Bearer YOUR_TOKEN" \ 
http://public-api.shiphero.com/graphql \ 
sh_public_api.json

Now you can autogenerate the types:

shell sgqlc-codegen sh_public_api.json sh_public_api_schema.py

Now start making requests:

from sgqlc.operation import Operation 
from sgqlc.endpoint.http import HTTPEndpoint 
from sh_public_api_schema import sh_public_api_schema as schema

endpoint = HTTPEndpoint( 'http://http://public-api.shiphero.com/graphql', 
base_headers={'Authorization': 'Bearer YOUR_TOKEN'} 
)

# Build the query
op = Operation(schema.Query)

# Building the products query
ps = op.products()

# Make sure to request the complexity and request_id
ps.complexity() 
ps.request_id()

# Get the first 10 and define the selections
p_data = ps.data(first=10) 
p_data.edges.node.sku() 
p_data.edges.node.id()

# Executing the call
data = endpoint(op)

# Converting results to entities
for p in (op + data).edges: 
	 print(p.node) # will return a ProductInfo entity

Javascript

The API can be used from any client that supports GraphQL, here we’ll show examples to make some requests in javascript

Using graphql-request

import {  GraphQLClient} from 'graphql-request'

async function main() {
  const endpoint = 'https://public-api.shiphero.com/graphql'

  const graphQLClient = new GraphQLClient(endpoint, {
    headers: {
      authorization: 'Bearer TOKEN',
    },
  })

  const query = /* GraphQL */ `
{
  products {
    request_id
    complexity
    data(first: 10) {
      edges {
        node {
          id
          sku
        }
      }
    }
  }
}
  `

  const data = await graphQLClient.request(query)
  console.log(JSON.stringify(data, undefined, 2))
}

main().catch(error => console.error(error))