The Right API for the Job Rob Allen PHPDay, May 2022

Fit for Purpose Rob Allen ~ @akrabat

API Architecture Rob Allen ~ @akrabat

APIs can be realised in any style but, which makes the most sense? Rob Allen ~ @akrabat

RPC APIs Rob Allen ~ @akrabat

RPC APIs • Call a function on a remote server Rob Allen ~ @akrabat

RPC APIs • Call a function on a remote server • Common implementations: JSON-RPC, SOAP, gRPC Rob Allen ~ @akrabat

RPC APIs • Call a function on a remote server • Common implementations: JSON-RPC, SOAP, gRPC • Tends to require a schema (WSDL, ProtoBuf Defintion) Rob Allen ~ @akrabat

Ethereum JSON-RPC Request: POST / HTTP/1.1 Host: localhost:8545 { “jsonrpc”:”2.0”, “id”:1, “method”:”net_peerCount”, “params”:[] } Rob Allen ~ @akrabat

Ethereum JSON-RPC Response: { “id”:1, “jsonrpc”: “2.0”, “result”: “0x2” } Rob Allen ~ @akrabat

gRPC Interact via PHP library: $client = new RouteGuideClient(‘localhost:50051’); $p = new Routeguide\Point(); $p->setLatitude(409146138); $p->setLongitude(-746188906); list($feature, $status) = $client->GetFeature($p)->wait(); Rob Allen ~ @akrabat

RESTful APIs Rob Allen ~ @akrabat

RESTful APIs • Operate on a representation of the state of a resource though HTTP verbs Rob Allen ~ @akrabat

RESTful APIs • Operate on a representation of the state of a resource though HTTP verbs • HTTP native Rob Allen ~ @akrabat

RESTful APIs • Operate on a representation of the state of a resource though HTTP verbs • HTTP native • Uniform interface Rob Allen ~ @akrabat

RESTful APIs • Operate on a representation of the state of a resource though HTTP verbs • HTTP native • Uniform interface • Hypermedia controls Rob Allen ~ @akrabat

RESTful APIs PUT /users/ba60c99fd3 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com” } Rob Allen ~ @akrabat

RESTful APIs PUT /users/ba60c99fd3 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com” } Rob Allen ~ @akrabat

RESTful APIs PUT /users/ba60c99fd3 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com” } Rob Allen ~ @akrabat

RESTful APIs HTTP/1.1 201 Created ETag: dfb9f2ab35fe4d17bde2fb2b1cee88c1 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com”, “_links”: { “self”: “https://api.example.com/user/ba60c99fd3” } } Rob Allen ~ @akrabat

RESTful APIs HTTP/1.1 201 Created ETag: dfb9f2ab35fe4d17bde2fb2b1cee88c1 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com”, “_links”: { “self”: “https://api.example.com/user/ba60c99fd3” } } Rob Allen ~ @akrabat

RESTful APIs HTTP/1.1 201 Created ETag: dfb9f2ab35fe4d17bde2fb2b1cee88c1 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com”, “_links”: { “self”: “https://api.example.com/user/ba60c99fd3” } } Rob Allen ~ @akrabat

RESTful APIs HTTP/1.1 201 Created ETag: dfb9f2ab35fe4d17bde2fb2b1cee88c1 Content-Type: application/json { “name”: “Rob Allen” “email”: “rob@akrabat.com”, “_links”: { “self”: “https://api.example.com/user/ba60c99fd3” } } Rob Allen ~ @akrabat

GraphQL APIs Rob Allen ~ @akrabat

GraphQL APIs • Retrieve only the data you need on consumer side Rob Allen ~ @akrabat

GraphQL APIs • Retrieve only the data you need on consumer side • Reduce the number of calls to retrieve data with embedded resources Rob Allen ~ @akrabat

GraphQL APIs • Retrieve only the data you need on consumer side • Reduce the number of calls to retrieve data with embedded resources • Self-describing schema Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries query { author(name: “Ann McCaffrey”) { id, name books(first: 5) { totalCount edges { node { id, title, datePublished } } } } } Rob Allen ~ @akrabat

Queries “data”: { “author”: { “id”: “MXxBdXRob3J8ZjA”, “name”: “Ann McCaffrey”, “books”: { “totalCount”: 6, “edges”: [ { “node”: { “id”: “MXxCb29rfGYwNzU”, “title”: “Dragonflight” } }, Rob Allen ~ @akrabat

Queries “data”: { “author”: { “id”: “MXxBdXRob3J8ZjA”, “name”: “Ann McCaffrey”, “books”: { “totalCount”: 6, “edges”: [ { “node”: { “id”: “MXxCb29rfGYwNzU”, “title”: “Dragonflight” } }, Rob Allen ~ @akrabat

Queries “data”: { “author”: { “id”: “MXxBdXRob3J8ZjA”, “name”: “Ann McCaffrey”, “books”: { “totalCount”: 6, “edges”: [ { “node”: { “id”: “MXxCb29rfGYwNzU”, “title”: “Dragonflight” } }, Rob Allen ~ @akrabat

Queries “data”: { “author”: { “id”: “MXxBdXRob3J8ZjA”, “name”: “Ann McCaffrey”, “books”: { “totalCount”: 6, “edges”: [ { “node”: { “id”: “MXxCb29rfGYwNzU”, “title”: “Dragonflight” } }, Rob Allen ~ @akrabat

Mutations mutation { createAuthor( name:”Mary Shelley”, dateOfBirth: “1797-08-30” ) { returning { id, name } } } Rob Allen ~ @akrabat

Mutations mutation { createAuthor( name:”Mary Shelley”, dateOfBirth: “1797-08-30” ) { returning { id, name } } } Rob Allen ~ @akrabat

Mutations mutation { createAuthor( name:”Mary Shelley”, dateOfBirth: “1797-08-30” ) { returning { id, name } } } Rob Allen ~ @akrabat

Mutations mutation { createAuthor( name:”Mary Shelley”, dateOfBirth: “1797-08-30” ) { returning { id, name } } } Rob Allen ~ @akrabat

Mutations Response: “data”: { “createAuthor”: { “returning”: [ { “id”: “e3388cbea4e840a”, “name”: “Mary Shelly”, } ] } } Rob Allen ~ @akrabat

Which to pick? Rob Allen ~ @akrabat

Lamborghini or Ferrari? Rob Allen ~ @akrabat

Lamborghini or Truck? Rob Allen ~ @akrabat

Considerations • • • • What is it to be used for? Response customisation requirements HTTP interoperability requirements Binary protocol? Rob Allen ~ @akrabat

Response customisation • GraphQL is a query-first language • REST tends towards less customisation • With RPC you get what you’re given! (None will fix your database layer’s ability to efficiently retreive the data requested!) Rob Allen ~ @akrabat

Performance • REST and RPC puts server performance first • GraphQL puts client performance first Rob Allen ~ @akrabat

Caching • GraphQL and RPC can only cache at application layer • REST can additionally cache at HTTP layer Rob Allen ~ @akrabat

Data Transfer GraphQL: query { avatar(userId: “1234”) } { “data”: { “avatar”: “(base64 data)” “format”: “image/jpeg” } RPC: POST /api { “method”: “getAvatar”, “userId”: “1234” } { “result”: “(base64 data)” } }} Rob Allen ~ @akrabat

Data Transfer REST: REST: GET /user/1234/avatar Accept: image/jpeg GET /user/1234/avatar Accept: application/json HTTP/1.1 200 OK {jpg image data} HTTP/1.1 200 OK {“data”: “(base64 data)”} Rob Allen ~ @akrabat

Versioning • RPC, GraphQL and REST can all version via evolution as easily as each other Rob Allen ~ @akrabat

Versioning • RPC, GraphQL and REST can all version via evolution as easily as each other • GraphQL is very good for deprecation of specific fields Rob Allen ~ @akrabat

Design considerations It’s always hard! Rob Allen ~ @akrabat

Design considerations It’s always hard! Rob Allen ~ @akrabat

It’s your choice Rob Allen ~ @akrabat

Developer Experience Rob Allen ~ @akrabat

Correctness Rob Allen ~ @akrabat

Correctness RPC: Functions! Rob Allen ~ @akrabat

Correctness RPC: Functions! REST: HTTP matters! Rob Allen ~ @akrabat

Correctness RPC: Functions! REST: HTTP matters! GraphQL: Think in terms of relationships! Rob Allen ~ @akrabat

Correctness RPC: Functions! REST: HTTP matters! GraphQL: Think in terms of relationships! Rob Allen ~ @akrabat

Errors Rob Allen ~ @akrabat

Errors Error representations must be first class citizens Rob Allen ~ @akrabat

Errors Error representations must be first class citizens Rob Allen ~ @akrabat

Documentation Rob Allen ~ @akrabat

Documentation • API Reference Rob Allen ~ @akrabat

Documentation • API Reference • Tutorials Rob Allen ~ @akrabat

To sum up Rob Allen ~ @akrabat

If you suck at providing a REST API, you will suck at providing a GraphQL API Arnaud Lauret, API Handyman Rob Allen ~ @akrabat

Thank you! https://joind.in/talk/8cdd9 Rob Allen ~ @akrabat

Photo credits - Architecture: https://www.flickr.com/photos/shawnstilwell/4335732627 - Choose Pill: https://www.flickr.com/photos/eclib/4905907267 - Lamborghini & Ferrari: https://akrab.at/3w0yFmg - Lamborghini & Truck: https://akrab.at/3F4kAZk - ’50s Computer: https://www.flickr.com/photos/9479603@N02/49755349401 - Blackboard: https://www.flickr.com/photos/bryanalexander/17182506391 - Crash Test: https://www.flickr.com/photos/astrablog/4133302216 Rob Allen ~ @akrabat