1. REST & GraphQL Comparison

Understand the differences (and similarities) between the REST and GraphQL API design patterns

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) called marketplacer-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 and taxon 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 resource
    • POST: Create a resource
    • PUT: Update an entire resource
    • PATCH: Update part of a resource
    • DELETE: 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.

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

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) called marketplacer-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 now
  • variables: This would contain, (no surprises!) any variables that we wanted to inject into the query. In this example we’ve kept it simple and “hard-coded the Advert Id into the query, (this is the QWR2ZXJ0LTEwMDA3MDczNA== 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 query
  • node: 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 a node 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 the node 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, the node 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 the node query the concrete object type that you want - in this case Advert
    • 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.
  • id legacyId title...: These are the selection of Advert fields that you want to bring back. You’ll note that we can query associated objects (in this case variants) too. This field selection is what shapes the return payload.

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.


 RESTGraphQL
TransportHTTPSHTTPS
API KeyYesYes
HTTP VerbsGET, POST, PUT & DELETEPOST
# EndpointsMultiple, 1 for each resource type and actionOne
Object SelectionPartial, via query string selectorsFull via GraphQL Query syntax - this “out the box” functionality with GraphQL
Field SelectionNoYes - this “out the box” functionality with GraphQL
Return PayloadJSONJSON

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:

 RESTGraphQL
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.