If you are not familiar with GraphQL, no worries. It's easy to learn. If you have experience working with SQL you'll feel right at home. The syntax also looks similar to a JSON document if you are used to working with responses from REST endpoints. In fact, the structure of the query is simply the format of the JSON response you'd like to receive back.
Another nice feature of GraphQL is schema introspection. That's a fancy way of saying that the fields are self documenting. This allows us to build things like the Insomnia GraphQL Schema that provides autocompletion and field documentation that is always up to date with the latest API changes. (๐ปcheers to no documentation lag!)
With GraphQL, you're still issuing a request to an HTTP endpoint just like you would for a REST API. The big difference is there is a single endpoint that services all requests.
For Fast-Weigh, the endpoint looks like this:
https://<your-servername-here>.fast-weigh.dev/v1/graphql
If you calling the GraphQL endpoint directly, all requests must be POST requests with a JSON encoded query as the body of the request (Content-Type: application/json)
Here's a simple query that requests a few fields from the Customer GraphQL type:
query customers { Customer { CustomerID CustomerName ContactName ContactPhone } }
Let's break that syntax into its parts:
<ActionType> <QueryName> { <Type> { <TypeField1> <TypeField2> } }
The response back from this query will be a JSON response matching the format requested.
If a requested field has no value, it will be omitted from the response. This compresses the payload size and increases performance.
GraphQL introspection powers autocomplete within our recommended tooling.
This tool also allows you to browse the schema, as explained here.
Relationships in GraphQL can be of 2 types:
Object relationships will embed a single subtype in the response.
For example, the Customer type has an Object Relationship with a TaxCode.
So a query like this:
query customers_with_taxcode { Customer { CustomerID CustomerName TaxCode { Code } } }
Will result in a response that looks like this:
{ "data": { "Customer": [ { "CustomerID": "", "CustomerName": "", "TaxCode": { "Code": "" } } ] } }
Array relationships will nest an list of objects in the response.
As an example, a Customer may have many Orders.
So the Customer type has an array relationship with orders. And querying for the Orders would look some like this:
query customers_and_orders { Customer { CustomerID CustomerName Orders { OrderNumber Description } } }
And result in:
{ "data": { "Customer": [ { "CustomerID": "", "CustomerName": "", "Orders": [ { "OrderNumber": 1, "Description": "", }, { "OrderNumber": 2, "Description": "", } ] } ] } }
Until now, the queries we've written would have returned all results from the Customer table.
To filter the results, GraphQL supplies us with a few syntax options.
This one is pretty easy. Within the Type your are querying, supply the limit argument with an integer specifying the number of results you'd like returned.
query customers_and_orders { Customer(limit: 10) { CustomerID CustomerName Orders { OrderNumber Description PONumber } } }
Note that this only limits the Customer type to 10 results. So the nested Orders array may contain many more. You can limit on the nested Orders type in the same way.
query customers_and_orders { Customer(limit: 10) { CustomerID CustomerName Orders(limit: 10) { OrderNumber Description PONumber } } }
If you want to limit each response but still cycle through to retrieve all of the filtered data, you can use an offset loop to go through the next set of 10 records.
In practice, you'd want to increment this number on each call by whatever your limit is. So the first call would be offset of 0, second call an offset of 10, then 20, 30, and so on. Once you get a payload with fewer records than your limit, you'll know there are no more records to retrieve and can stop issuing queries.
query customers_and_orders { Customer(limit: 10, offset: 10) { CustomerID CustomerName Orders { OrderNumber Description PONumber } } }
Building on the query above, you can order the results by adding an "order_by" field to your arguments.
query customers_and_orders { Customer( limit: 10, order_by: {CustomerID: asc} ) { CustomerID CustomerName Orders { OrderNumber Description PONumber } } }
Notice you must pass an object to the "order_by" argument with the field you'd like to order, as well as the sort direction.
The following sort directions are permitted:
Let's modify the query once again and supply a "where" argument.
The "where" argument can apply a number of different filters to a given field.
Here's an example of querying for just Customers in the State of Tennessee:
query customers_with_taxcode { Customer( where: {State: {_eq: "TN"}} ) { CustomerID CustomerName Orders { OrderNumber Description PONumber } } }
And here's an example of passing an array of States to the filter using the "_in" clause:
query customers_with_taxcode { Customer( where: {State: {_in: ["TN","KY","GA"]}} ) { CustomerID CustomerName Orders { OrderNumber Description PONumber } } }
You can use the _and / _or operators along with the other "where" filters to create more complex queries.
These operators open up an array using "[]" brackets that contain all the filters you wish to apply.
This query will return a Customer only if it is within the provided States, and also has a Country set to "US".
query customers_with_taxcode { Customer( where: {_and: [ {State: {_in: ["TN","KY","GA"]}}, {Country: {_eq: {_eq: "US"}}} ]} ) { CustomerID CustomerName TaxCode { Code } } }
This query will return a Customer if it's within the given States, OR if its CustomerID field contains "100".
query customers_with_taxcode { Customer( where: {_or: [ {State: {_in: ["TN","KY","GA"]}}, {CustomerID: {_like: "100"}} ]} ) { CustomerID CustomerName TaxCode { Code } } }
Remember that filters can also be used on nested Types as well. But there is a difference for "where" filters in Object vs Array relations.
For Object relationships, you'll apply the filter on the parent. For example, a Customer can only have a single TaxCode. So to filter the Customers TaxCode you could write:
query customers_with_taxcode { Customer( where: {TaxCode: {Code: {_eq: "EXEMPT"}}} ) { CustomerID CustomerName TaxCode { Code } } }
For Array relationships, you'll typically apply the filter on the nested array itself. For example, a Customer can have many Orders. So to filter the Orders returned you would write:
query customers_and_orders { Customer( where: {State: {_in: ["TN","KY","GA"]}} ) { CustomerID CustomerName Orders( where: {Status: {_eq: "A"}} ) { OrderNumber Description PONumber } } }
Completely separate requests can also be merged within a single query. This is useful if you need to bring in data from multiple tables and only want to make a single HTTP call to do so.
Typically compound queries would only be used to stitch together the responses of smaller queries. You would not want to combine multiple large payload queries in the same request/response as this can be quite taxing to fetch and parse due to query complexity and payload size.
Here's an example request for some base resources within Fast-Weigh. These lists are generally quite short and are good candidates for compound queries if applicable to your use case.
query sample_compound_query { Region { RegionName RegionDescription } Salesperson { Name Email } TaxCode { Code MaterialPercent FreightPercent SurchargePercent } Product { ProductID ProductDescription } }
Results in:
{ "data": { "Region": [ { "RegionName": "", "RegionDescription": "" }, ], "Salesperson": [ { "Name": "", "Email": "" }, ], "TaxCode": [ { "Code": "", "MaterialPercent": 0.0000000, "FreightPercent": 0.0000000, "SurchargePercent": 0.0000000 }, ], "Product": [ { "ProductID": "", "ProductDescription": "" }, ] } }
Once you've got the query basics down, it's time to start experimenting and using them for your own use case in Insomnia or start quering via HTTP and code in your programming language of choice.