Developer Resources > GraphQL Primer
GraphQL Primer
What is covered on this page
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.
3PLs
When executing queries, you might want to fetch only data for a particular customer. You have two options for this:
- Requesting a token using your customer credentials and use it in any further operations made on behalf of that customer.
- 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:
- Requesting a token using your customer credentials and making the requests with that token
- 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 { data { customers { edges { node { id } } } } } }
or:
query { me { data { 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
- 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))