Skip to content
GraphQL Primer

GraphQL Primer

What is covered on this page:


Core concepts

This section covers core concepts used throughout the GraphQL API.

Identifiers

Resources exposed through the API use opaque uuid identifiers.

If you have an existing legacy_id and need the corresponding uuid, call the uuid query with the legacy_id and the entity name:

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

Pagination

The GraphQL API uses cursor-based pagination. When querying connections, pass first or last to control how many items are returned. This helps optimize queries and reduce credit usage.

Request the cursor field to identify a node’s position within a connection. Use that cursor to fetch the next set of items.

Request the pageInfo field to determine whether additional pages are available. The hasNextPage and hasPreviousPage fields indicate whether you can paginate forward or backward. If both are false, no more pages are available.

Example: fetching product IDs and SKUs from an account with more than 10,000 products.

Query:

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="
          }
        ]
      }
    }
  }
}

This response contains the first 100 products, not the full result set.

To fetch the next 100 results, use the cursor to indicate where to start.

In this example, hasNextPage: true and endCursor: "YXJyYXljb25uZWN0aW9uOjk5", so the next request would look 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 excellent 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:

  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 {
    data {
      customers {
        edges {
          node {
            id
          }
        }
      }
    }
  }
}

or:

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

Customer warehouse relationship and lot settings

For 3PL accounts, account.data.customers can also return customer settings that are useful when deciding whether and how to operate for a customer at a specific warehouse.

Pass warehouse_id when you need warehouse-specific relationship data. Use the public API Warehouse.id value, such as the IDs returned by account.data.warehouses. When warehouse_id is omitted, warehouse_relationship returns null even if the customer has a relationship with one of your warehouses.

warehouse_relationship is scoped to the warehouse/customer relationship and includes:

  • from_name - the customer display name configured on that warehouse relationship.
  • pause_shipping - whether shipping is paused for that customer at that warehouse.

lot_tracking_settings is the customer’s lot tracking configuration. It is not scoped by warehouse_id, and it returns null when the customer does not have lot tracking settings configured.

query ThreePlCustomerSettings($warehouse_id: String!) {
  account {
    request_id
    complexity
    data {
      customers(warehouse_id: $warehouse_id, first: 10) {
        edges {
          node {
            id
            legacy_id
            username
            email
            warehouse_relationship {
              from_name
              pause_shipping
            }
            lot_tracking_settings {
              is_active
              priority
              picking_disabled_period_in_days
              verify_lot_when_packing
              exclude_expired_quantity_from_available
              lots_about_to_expire_email
            }
          }
        }
      }
    }
  }
}

Variables:

{
  "warehouse_id": "V2FyZWhvdXNlOjExNzkw"
}

Example response:

{
  "data": {
    "account": {
      "request_id": "664fa4c96a9d0b7c9f4d3b21",
      "complexity": 12,
      "data": {
        "customers": {
          "edges": [
            {
              "node": {
                "id": "QWNjb3VudDo0Mjg1",
                "legacy_id": 4285,
                "username": "demo-customer",
                "email": "ops@example.com",
                "warehouse_relationship": {
                  "from_name": "Demo Customer",
                  "pause_shipping": false
                },
                "lot_tracking_settings": {
                  "is_active": true,
                  "priority": "fefo",
                  "picking_disabled_period_in_days": 14,
                  "verify_lot_when_packing": true,
                  "exclude_expired_quantity_from_available": true,
                  "lots_about_to_expire_email": "lots@example.com"
                }
              }
            }
          ]
        }
      }
    }
  }
}

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:

CodeError
3Invalid Argument
5Not Found
6Already exists
7Permission denied
9Failed Precondition
14Unavailable
20FormatError
21Service Not Available
22Unexpected Error
30Throttling 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:

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:

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))