GraphQL
Yes, I couldn't resist to post that one! Atomium from Belgium.
GraphQL seems to bring some inquietude among data architects who believe this is a leaky abstraction.
REST is easier to understand at first glance. REST stand for representational state transfer. Server should not hold any information specific to the client, client should not have awareness of what is behind the representation.
Usually a REST request looks like this:
POST /user HTTP/1.1
Authorization: Bearer ****
Content-Type application/json
Accept: application/json
{ "callname": "Jhon" }
And respond like this
HTTP/1.1 201 created
Content-Type: application/json
Location: /user/42
{"callname":"Jhon","id":42}
In that example, the client would be able to query the user:
GET /user/42 HTTP/1.1
Authorization: Bearer ****
Accept: application/json
Very easy to understand. Each resource has his own URL and respond to HTTP verbs.
Now comes the classic problem. What if I need to have many views on an object ? Let say, an order, with its lines.
Here are possible GET requests:
GET /order/31 -> Full order
GET /order/31/item -> Order items
GET /order/31/header -> Order metadata (client, shipping)
GET /client/72 -> A client
- Should the client be returned in order header OR should we return a resource location ?
- Should we allow field digging by using paths ?
Some will create views for the ui-layer. Some kind of meta resources linked to application screen. This permits to expose specific views.
I also saw JsonPath used as XPaht to query the document.
Designing a REST API can be tough and one can quickly end up binding it to the client side. It's leaky abstraction where server is aware of client needs before they are expressed.
Here come GRaphQL
In GraphQL, one design an abstraction layer through a schema. The schema describes what is available, not HOW.
type Query {
order(id:Number): Order
}
type Order {
client: Client!
items: [Item]!
total: Number!
}
type Item {
price: Number!
name: String!
}
Now, one can query exactly what he needs.
{
order(id:31) {
total
}
}
As you can see, like REST, I do not have to be aware of what is behind that total field. Is it calculated ? Extracted from a database ? Maybe there is a web service behind ?
The only think I know is that I can access orders by their ID and for each, I can ask exactly what I need.
The server do not need to know how it will be used either. He just expose what clients are allowed to access!
The other problem is mutations. In GraphQL, those are defined as...mutations.
type Mutation {
createOrder(order:OrderInput): Order
updateOrder(id:Int,orderInput): Order
addOrderLine(id:Int,Item): Order
}
That's how a mutation is declared. Then we define the OrderInput type:
input OrderInput {
clientId: Int!,
items:[Int]!
}
As one can see, it's well defined.
GraphQL also provides a mean to pass structured data in parameters through variables:
query: mutation createOrder($order:OrderInput) {
createOrder(order:$order) { id }
}
variables: { "$order" : { "clientId":42,"items": [ 7,3,9,2 ] } }
The above query create an order and retrieves only the id
And if you like being sloppy (can looks good in simple cases though):
mutation {
createOrder({ clientId:42,items:[7,3,9,2] })
}
Take away ?
GraphQL permits a good separation of client and server. It can also lower payload and time consumed making specific endpoints.
GraphQL permits flexibility both on the server side (implementation can change freely) and client side (client can be very flexible on input).
GraphQL standardise how to define schemas. It exposes the schema too. This makes it easy to create tooling.
Misconceptions
- GraphQL is a layer on top of a database which permits to query it like a graph.
GraphQL is more of an integration platform. GraphQL abstract and combine varied datasources. GraphQL permit a natural aggregation.
- GraphQL permits to transform output like XQuery/XSLT would do
No, but it permits to retrieve a subset of a predefined output. (e.g. an order without its lines)
Some extensions permits to lift up fields by using directives. Directives are something to dig in if evaluating GraphQL. Look at builtin "@include" or "@skip" (must be supported by compliant servers)