1. REST & GraphQL Comparison
12 minute read
Introduction
This section is really just a quick primer for those readers that may have a background in REST but have not worked much (if at all with GraphQL). The main aim of the section is to get the reader up to speed on the main differences (and indeed similarities) between the 2 patterns.
We also discuss the reasons why Marketplacer has chosen to focus on GraphQL.
If you are conversant in both approaches then this section can be skipped.
REST
Much has already been written about REST, or Representational State Transfer, so we won’t seek to repeat that in full here, but to summarize the key points for this playbook, RESTful APIs:
- Respond with the representation of a Resource
- Think of a resource as an object, for example: an Order, Product, Shipment, User etc.
- They are stateless
- Vast majority of REST APIs work over HTTP, (although they don’t have to), with endpoints mapping to HTTP verbs: Get, Post, Put etc.
- Strict adherence to the REST principles is quite onerous, with most implementations violating them in some way
- Payloads are typically returned as JSON (although again this does not need to be the case)
Worked Example
Looking at an example from the Marketplacer Legacy Seller API, the following example (using cURL) shows you how you would return a single product resource for a Seller, (note that Products in Marketplacer are referred to as Adverts).
curl
-XGET
-H 'marketplacer-api-key: 11111111111111111'
'https://example.marketplacer.com/api/v2/client/adverts/100070734'
You can see this (http) request has the following properties:
- We are specifying the
GET
http verb (in REST,GET
requests typically relate to the reading of resources) - We are specifying a HTTP header (
-H
switch) calledmarketplacer-api-key
the value of which authenticates and authorizes the Seller making the API call - The resource location, (essentially a URL), you’ll note that we are requesting:
adverts
- a specific advert resource with an Id of
100070734
In this example, the request would result in the return of a JSON payload relating to a single product resource as follows:
{
"links": {
"self": "https://www.example.marketplacer.com/api/v2/client/adverts/100070734"
},
"data": {
"type": "adverts",
"id": "100070734",
"attributes": {
"title": "Venovo 5G Tablet",
"description": "Powerful tabler",
"sale_type": "buy_online_only",
"published": true,
"code": "custom code",
"sale_price": null,
"price": "349.99",
"condition": "new",
"year": null,
"external_id": null,
"notes": null,
"specifications": null,
"domestic_shipping_cost": "0.0",
"international_shipping_cost": "0.0",
"youtube_video_url": null,
"origin_country_code": null,
"tax_code": "au-21",
"therapeutic_goods_labelling_enabled": false,
"max_purchase_quantity": null,
"min_purchase_quantity": null,
"item_tax_rate": "0.25",
"item_tax_rate_percentage": "25.0",
"vetted": false,
"vetting_rejected_reason": null,
"features": {
"product_features": [],
"product_details": {
"Cellular Material": "Plastic"
}
}
},
"relationships": {
"taxon": {
"data": {
"type": "taxons",
"id": "279"
}
},
"brand": {
"data": {
"type": "brands",
"id": "4"
}
}
}
},
"included": [
{
"type": "taxons",
"id": "279",
"attributes": {
"name": "Cellular Tablets",
"tree_name": "Cellular Technology - Cellular Tablets",
"slug": "cellular-tablets",
"taxon_type": "product"
}
},
{
"type": "brands",
"id": "4",
"attributes": {
"name": "Advocate 1",
"slug": "advocate-1"
}
}
]
}
Points to note about this response:
- We get the associated resources
brand
andtaxon
for the advert, (even though we did not actually request this), which more than likely violates strict RESTful principles… - We get a set of fields for the advert that we did not specify, (it’s just the default set), and in all likelihood we probably don’t need all of these…
- It’s also conceivable there are additional fields available on the advert that we did not get
We can add a number of query string parameters to this request, as documented here, to extend the data set that we get back. While this does provide some flexibility on requesting additional data you should note:
- This probably violates REST!
- You can only specify addition associated resources (e.g. Product Variants), but you cannot specify the individual fields that are returned
- The query string parameters that you can supply are constrained by the API provider that designed and built the API
- I.e. you do not automatically get the ability to provide query string parameters for all resources associated to an advert, these have to be explicitly implemented by the API provider
REST Summary
- The most popular Web API design pattern
- Returns individual resources (or a collection of the same resource-type, e.g. a list of Products)
- Can return associated resource types but that is dependant on the API implementation, and more than likely violates RESTful principles
- Usually runs over HTTP with the following HTTP Verb mappings:
GET
: read resourcePOST
: Create a resourcePUT
: Update an entire resourcePATCH
: Update part of a resourceDELETE
: Deletes a resource
- Usually returns the payload as JSON (although can return in other formats, e.g. XML)
GraphQL
GraphQL is a much newer API design pattern, (certainly when compared with REST), and was created at Facebook to solve the twin problems of over-fetching and under-fetching. Before we look at those in more detail, some other points to note about GraphQL APIs are:
- GraphQL is both an API design pattern and a query language
- The vast majority of GraphQL APIs work over HTTP, (although they don’t have to), with most relying only on a single HTTP POST endpoint to service requests
- The body of the POST request will contain some GraphQL code that will tell the endpoint what you are attempting to do (query or mutate data)
- Payloads are typically returned as JSON (although that need not be the case)
Before we look at a worked example, let’s return to the 2 main problems Facebook were trying to solve when they created GraphQL.
Over-fetching
Over-fetching describes the issue where you make a request for a resource (e.g. a Product) and obtain way more data than you need. In our example REST request for an Advert, we may only have needed the title
, price
, sale_price
fields, instead we got a whole lot of data that we didn’t need. Extrapolate that across many requests and you end up with an inefficient pattern.
Under-fetching
Under-fetching relates to not getting enough data and having to make repeat calls to the (REST) API to obtain that data. In the above example we would need to make subsequent (multiple) calls to either the Advert or Variant endpoints to get all the variants for that product. Again this is inefficient.
But we could get related resources?
As mentioned when discussing the Marketplacer REST Advert endpoint, you can append query parameters to bring in further related resources, e.g.:
/api/v2/client/adverts/100070734?include=variants
So you may argue that the under-fetching point isn’t valid…
While it is technically true that you can bring in other related resources in this way, you should be aware that:
- It is not technically REST if you do this
- It probably just magnifies the problem of over-fetching (you’d get all the variant attributes now too)
- You are still at the mercy of each API developer, i.e. they may choose not to implement this behavior
In short, GraphQL aims to give you exactly the data you need, no more, no less in as few calls as possible. This is probably best demonstrated through the use of an example.
Worked Example
In this example we are going to use a GraphQL Query to bring back the same Advert (Product) as we requested with our REST API, however:
- We will limit the Advert fields just to those that we need (avoid over-fetching)
- We will bring in additional Variant information for this Advert (avoid under-fetching)
- In the REST paradigm a
variant
would be considered a separate resource
- In the REST paradigm a
Again we will use cURL to make a request:
curl
-XPOST
-H 'marketplacer-api-key: 11111111111111111'
-d '{"query":"query{node(id:\"QWR2ZXJ0LTEwMDA3MDczNA==\"){... on Advert{id legacyId title lowestOriginalPrice lowestPrice variants{nodes{legacyId sku label countOnHand}}}}}","operationName":"Operations","variables":{}}'
'https://example.marketplacer.com/graphql'
There are some similarities, and some marked differences with REST:
- We are still using HTTP, this time specifying the
POST
http verb- GraphQL APIs only have the 1 endpoint, responding to 1 verb (usually POST), as opposed to REST that will have separate routes for each resource.
- We are still specifying a HTTP header (
-H
switch) calledmarketplacer-api-key
the value of which authenticates and authorizes the Seller making the API call - We are providing a body to the POST request, this contains a JSON payload that wraps around our GraphQL query (more detail on this below)
- The endpoint location. As mentioned previously, unlike REST where you have unique routes to each resource type, GraphQL APIs rely on only 1 endpoint.
Executing this request would return the following:
{
"data": {
"node": {
"id": "QWR2ZXJ0LTEwMDA3MDczNA==",
"legacyId": 100070734,
"title": "Venovo 5G Tablet",
"lowestOriginalPrice": "349.99",
"lowestPrice": "349.99",
"variants": {
"nodes": [
{
"legacyId": 78028,
"sku": null,
"label": "128Gb / USB-C & 3.5 mm Headphone",
"countOnHand": 1000
}
]
}
}
}
}
As you can see a much more concise payload with only the information we need, including associated objects, in this case variants
.
A closer look at the body
Circling back to the request we just made, lets extract just the body of the request and take a closer look at it formatted as JSON:
{
"query": "query{node(id:\"QWR2ZXJ0LTEwMDA3MDczNA==\"){... on Advert{id legacyId title lowestOriginalPrice lowestPrice variants{nodes{legacyId sku label countOnHand}}}}}",
"operationName": "Operations",
"variables": {}
}
The body is sent as JSON comprising 3 attributes:
query
: This contains the GraphQL code we want to execute - this is predominantly what we’ll focus on in the rest of the playbook.operationName
: This just describes any name that we choose to give our query, we will not be referring back to this again in this playbook, so you can ignore for nowvariables
: This would contain, (no surprises!) any variables that we wanted to inject into thequery
. In this example we’ve kept it simple and “hard-coded the Advert Id into the query, (this is theQWR2ZXJ0LTEwMDA3MDczNA==
value). In production type environments you should not do this and instead pass that value over as a variable, which would be contained in this section.
Looking now at just the contents of the query
attribute we reveal the GraphQL code we are using, (this is formatted as GraphQL):
query {
node(id: "QWR2ZXJ0LTEwMDA3MDczNA==") {
... on Advert {
id
legacyId
title
lowestOriginalPrice
lowestPrice
variants {
nodes {
legacyId
sku
label
countOnHand
}
}
}
}
}
If you are starting out with GraphQL, be careful not to confuse it with JSON. The 2 can look quite similar, but are obviously completely different!
Picking the query apart we can break it into the following sections:
query
: while not always mandatory, it is useful to distinguish between queries (reading data) and mutations (creating, updating or deleting data). This leading expression just clarifies that we are running a querynode
: this is the name of our query. As you will find out, Marketplacer has provided a number of different queries and mutations for you to pick from, we’ll explore many of these throughout the rest of the playbook. In this case anode
query represents a single (generic) object within the GraphQL schema.(id: "QWR2ZXJ0LTEwMDA3MDczNA==")
: Many queries and mutations (in fact most of them) will take some type of input parameters (usually provided as variables). In this case thenode
query just takes a single input, which is the unique identifier (ID) of the object.... on Advert
: This is an inline fragment, specifying the type of object we are interested in. As mentioned above, thenode
query is generic, so can work with all object types that support the node construct. However in order to get to the specific data you need for an object you will need to tell thenode
query the concrete object type that you want - in this caseAdvert
- You can use the
node
query to return data for most object types in Marketplacer, e.g.Order
,Invoice
,Shipment
etc. - Note that many of the other queries we will look at do not require this as they only work with 1 concrete object type.
- You can use the
id legacyId title...
: These are the selection ofAdvert
fields that you want to bring back. You’ll note that we can query associated objects (in this casevariants
) too. This field selection is what shapes the return payload.
ID's in GraphQL
One of the main differences between the REST and GraphQL APIs are the IDs that you will be using. In the Marketplacer REST API, IDs are represented as integers unique to the resource type. E.g. All Adverts will have a unique ID, as will all Variants, however it’s conceivable that an Advert and Variant could have the same numeric ID value, (even though of course those values relate to 2 separate resources). IDs are unique to the resource, but not necessarily unique across resources. This is not an issue given the way REST works, i.e. by using separate resource endpoints.
As GraphQL does not have separate endpoints for each object type, the object IDs need to be globally unique, i.e. the should uniquely identify an specific object and cannot be repeated across objects,
You can read more about IDs in GraphQL in this article.
Side by Side
The point of the last section was to cover off the fundamentals of both API design patterns, ultimately with a view to comparing them. In the table below we compare the Marketplacer implementations of both the REST and GraphQL variants of the Seller API.
REST | GraphQL | |
---|---|---|
Transport | HTTPS | HTTPS |
API Key | Yes | Yes |
HTTP Verbs | GET , POST , PUT & DELETE | POST |
# Endpoints | Multiple, 1 for each resource type and action | One |
Object Selection | Partial, via query string selectors | Full via GraphQL Query syntax - this “out the box” functionality with GraphQL |
Field Selection | No | Yes - this “out the box” functionality with GraphQL |
Return Payload | JSON | JSON |
Ultimately the main difference between the 2 approaches is:
- The way you construct your requests
- The way you can shape the return payload
Why GraphQL?
Both API design patterns have their advocates and their detractors, and as mentioned in the introduction, attempting to claim one approach is better than the other ends up being a non constructive exercise…
Marketplacer has chosen to focus the majority of its development effort on expanding and developing the GraphQL API surface simply because we believe it satisfies the integration use-cases our customers need more closely than the REST paradigm.
An example of this difference lies in the way Products are created using both APIs:
REST | GraphQL | |
---|---|---|
Number of API Calls Required | - 1x to create Advert - 1x for each variant - 1x to attach images - 1x to Publish (min 4x calls) | - 1x |
Because of this we encourage both existing and new seller integrations to be built using the GraphQL variant of the Seller API.
In the sections that follow we outline how the REST API endpoints map to their GraphQL equivalents.
Next Up: Endpoint Mapping