Comparative notes on GraphQL and REST API
REST has been the de-facto API standard for quite sometime since Roy Fielding introduced it in his thesis in 2000. GraphQL is the new kid on the block who wants to address some of the issues with REST, but in no way a replacement or improved version of REST.
REST is a standard/philosophy, GraphQL is a query language and has GraphQL engine implementations in different programming languages to be able to interpret and respond to those GraphQL queries.
Can we compare them, REST and GraphQL?
As is wont to happen, human minds do tend to compare things and do tend to learn and register and reinforce better by comparing things ( or else why am I taking these comparative notes? 😉). Straight answer to the above question is 'No'. A more sensible thing would be to compare a REST API unsupported by GraphQL vs one supported by GraphQL.
What is GraphQL?
Lets see what the official site graphql.org has to say:
"GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more..."
The below are noteworthy:
- It has a specific query language.
- It has a runtime (read GraphQL engine).
- It has schema (for understandable description of the data).
- It provides only the bare minimum i.e. what the client has exactly queried and nothing more.
Specific query language
The query is somewhat JSON-ish (at least that is how I tend to relate to it - a JSON blob minus the values.)
A simple example:
{ book { isbn title author publish_year publish_version kindle_version category price } }
A somewhat simple example: (with argument passing and data transformation)
{
book(isbn: '978-3-16-148410-0') {
title
author
publish_year
publish_version
kindle_version
category
price(unit: POUND)
}
}
A complex example:
// Example of alias
{
first_book: book(isbn: '548-3-66-198410-0') {
title
author
referenced_books {
name
}
}
second_book: book(isbn: '978-3-16-148410-0') {
title
author
referenced_books {
name
}
}
}
// Abbreviating the above query with "fragments"
{
leftComparison: book(isbn: '548-3-66-198410-0') {
...comparisonFields
}
rightComparison: book(isbn: '978-3-16-148410-0') {
...comparisonFields
}
}
fragment comparisonFields on ISBNObject {
title
author
referenced_books {
name
}
}
// If you are wondering about ISBNObject, it is the Type name of the query
// field 'isbn' - this'll be later shown in the schema section.
[ NSSC (Not-so-sensible-comparison) : Do REST APIs have a query language? No, they don't. HTTP has request parameters and values which get a little close to it in the sense that they will be used by the API endpoints but can't really launder them has query language, can we?]
GraphQL engine or library or query reference
A few key concepts of GraphQL engines are:
Resolver:
Resolvers define how a query field or attribute ends up pulling corresponding data. Example below in the form of resolve_<field> functions of Graphene Types
from graphene import ObjectType, String, Schema
from pprint import pprint
import random
import json
books = {
'548-3-66-198410-0': {
'isbn': '548-3-66-198410-0',
'title': 'Dummy_book_1',
'author' : 'Debashish',
'publish_year' : '2002',
'publish_version' : '15',
'kindle_version' : 'none',
'category' : 'Programming',
'price' : 'INR 150'},
'978-3-16-148410-0': {
'isbn': '978-3-16-148410-0',
'title': 'Dummy_book_2',
'author' : 'Debashish',
'publish_year' : '2002',
'publish_version' : '2',
'kindle_version' : 'none',
'category' : 'Design',
'price' : 'INR 200'}
}
class Query(ObjectType):
# field in our Schema with a single Argument `name`
book = String(isbn=String(default_value="978-3-16-148410-0"))
title = String(isbn=String(default_value='548-3-66-198410-0'))
author = String()
publish_year = String()
publish_version = String()
kindle_version = String()
category = String()
price = String()
# Resolver method takes the GraphQL context (root, info) as well as isbn argument
# This is just a simple example, without any backend
def resolve_book(root, info, isbn):
_book_obj = books.get(isbn)
return(json.dumps(_book_obj))
def resolve_title(root, info, isbn):
_book_obj = books.get(isbn)
return(json.dumps(_book_obj.get('title')))
schema = Schema(query=Query)
if __name__ == '__main__':
# we can query for our field (with the default argument)
query_string = '{ book(isbn: "548-3-66-198410-0") title }'
result = schema.execute(query_string)
pprint(result.data, indent=4)
Schema-first:
GraphQL schema needs to be explicitly define. Consider the following Apollo Server example:
const { ApolloServer, gql } = require('apollo-server');
// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.
const typeDefs = gql`
# Comments in GraphQL strings (such as this one) start with the hash (#) symbol.
# This "book" type defines the queryable fields for every book in our data source.
type book {
isbn: String
title: String
author: String
publish_year: String
publish_version: String
kindle_version: String
category: String
price: String
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "books" query returns an array of zero or more Books (defined above).
type Query {
books: [book]
}
`;
Code-first:
GraphQL SDL(Schema Definiton Language) is wrapped around with native language objects and hence schema need not be defined separately, rather it is part of the code. The Graphene resolver example given earlier has shown a code-first approach.
[ NSSC: REST APIs do not need a runtime as all languages inherently understand HTTP and any REST endpoints can do sort of resolving requests to different resources based on URL paths and parameters]
Schema
GraphQL has a Schema Definition Language(SDL) with interesting Object Types like Query, Mutation, Interface, Union in addition to basic types like ID, String, Float etc. This is a good resource from the official site.
[ NSSC: REST APIs may use JSON schema optionally but it is nowhere near the Type system seen in GraphQL]
Response
The responses are usually very simple 😌. They are nothing but JSON docs. The important thing to remember here is though - the client can control what fields would be returned and in what format in the response. We can draw a db SQL analogy - users get to see only the queried data fields in the query result.
Successful response format:
{
"data": { ... },
"extensions": { ... }
}
{
"errors": [ ... ],
}
Comments