Birds eye view on:API DEVELOPMENT
WHO ?
Frederick Vanbrabant Software engineermadewithlove
Dries Vints@driesvints
Freek Van der Herten@freekmurze
phpantwerp.be
{ "id": 1, "title": "7 Things You Should NEVER Do to a potato", "subtitle": "you will never believe nr 4", "body": "Some body copy that will go on in great detail", "comments": [{ "id": 1, "body": "woah this changed my life" }, { "id": 2, "body": "I will never look at stuff in the same way" }]}
GET /getblogpost/1
Why is this not so great ?
Url is kinda structure less
What if we get loads of articles?
What if we get loads of comments?
Damn, we need to write documentation
Is this the best way to pinpoint a resource
GET /getblogposts/1
Let's look at that endpoint
HTTP verb
Endpoint
HTTP verbs
HTTP verbs
HTTP != CRUD
HTTP verbs
Verb Return HTTP Code
POST 201
Sends a payload to the server,Returns an empty body.
HTTP verbs
Verb Return HTTP Code
POST 201
POST /posts
{ "title": "Why cheese is better then real friends", "body": "People only stand in the way of you and your cheese!"}
Post body
HTTP verbs
Verb Return HTTP Code
GET 200
Does not send a payloadReturns a collection of or a single resource
HTTP verbs
Verb Return HTTP Code
GET 200
GET /posts/2
{ "title": "How to fake your own death", "body": "You can buy cheese with the insurance money!"}
Return body
HTTP verbs
Verb Return HTTP Code
PUT 204
Updates a resource (needs the entire object)Returns an empty body.
HTTP verbs
Verb Return HTTP Code
PUT 204
PUT /posts/2
{ "title": "Help I'm stuck in a keynote making factory", "body": "Send cheese!"}
Put body
HTTP verbs
Verb Return HTTP Code
PATCH 200
Accepts instructions to update the resourceReturns the resource
Wait what?
Accepts instructions to update the resourcereturns the resource
PATCH /artists/kanye/[ { "op": "replace", "path": "artists/kanye/cars", "value": 7 },]
Wait what?
Accepts instructions to update the resourcereturns the resource
PATCH /artists/kanye[ {"cars" : 7}]
Content-Type: application/partial-update-json
HTTP verbs
Verb Return HTTP Code
DELETE 204
Removed the resourceReturns an empty body.
HTTP verbs
Verb Return HTTP Code
DELETE 204
DELETE /posts/1
HTTP CODE
HTTP CODE
Range 1XX - Information
Code What it means
100 continue
101 Switching Protocols
102 Processing
HTTP CODE
Range 2XX - Success
Code What it means
200 Ok
201 Created
202 Accepted
HTTP CODE
Range 3XX - Redirection
Code What it means
301 Moved
300 Multiple Choices
HTTP CODE
Range 4XX - Client error
Code What it means
400 Bad Request
401 Unauthorized
404 Not found
HTTP CODE
Range 5XX - Server error
Code What it means
500 Internal Server Error
503 Service Unavailable
504 Gateway Timeout
HTTP HEADERS
HTTP HEADERS
Send on each request and response
Meta data to the call
Can define custom headers
HTTP HEADERS
GET / HTTP/1.1
Host: www.google.comConnection: keep-alivePragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/X-Chrome-UMA-Enabled: 1X-Client-Data: CIS2yQEIpbbJAQjEtskBCOCUygEI+pjKAQjznMoBAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Encoding: gzip, deflate, sdchAccept-Language: nl,en;q=0.8Cookie: *Cookie data*
Request
HTTP HEADERS
Status: 200 (OK)
Date: Fri, 08 Jul 2016 13:59:05 GMTExpires: -1Cache-Control: private, max-age=0Content-Type: text/html; charset=ISO-8859-1X-Xss-Protection: 1; mode=blockX-Frame-Options: SAMEORIGINSet-Cookie: NID=81=x8aqWN0zsVHK2QcLEZca6603H8BYJxXP6bfjQl0cT82iM4j7fE3V9wH2qbpXW0DAAPL3G56n9ID-H_RCP-w9eVxA-mu9XV-oX-oE00qrVwRgzXV334c7XpsX2DDuTdcq; expires=Sat, 07-Jan-2017 13:59:05 GMT; path=/; domain=.google.be; HttpOnlyAccept-Ranges: noneVary: Accept-EncodingTransfer-Encoding: chunked
*HTML, JSON, XML OR WHATEVER*
Response
What about the endpoint
GET /getblogposts/1
What about the endpoint
GET /posts/1
What about the endpoint
GET /posts/1
Nouns are good, verbs are bad
Use plurals
What about the endpoint
GET /posts/1
GET /posts/1/comments
GET /posts/1/comments/15
OR
GET /comments/15
What about the endpoint
GET /posts
GET /posts/slugs-are-allowed
What about the endpoint
GET users/4b3403665fea6/posts/4b340550242239
What about the endpoint
Endpoint structure != database structure
We still 200 ?
Let's check out that body?
{ "id": 1, "title": "7 Things You Should NEVER Do to a potato", "subtitle": "you will never believe nr 4", "body": "Some body copy that will go on in great detail", "comments": [{ "id": 1, "body": "woah this changed my life" }, { "id": 2, "body": "I will never look at stuff in the same way" }]}
Let's use a standard
Battle tested and growing up to be the standard
{ "count": 87, "next": "http://swapi.co/api/people/?page=2", "previous": null, "results": [ { "name": "Luke Skywalker", "height": "172", "mass": "77", "hair_color": "blond", "skin_color": "fair", "eye_color": "blue", "birth_year": "19BBY", "gender": "male", "homeworld": "http://swapi.co/api/planets/1/", "films": [ "http://swapi.co/api/films/6/", "http://swapi.co/api/films/3/", "http://swapi.co/api/films/2/", "http://swapi.co/api/films/1/", "http://swapi.co/api/films/7/" ], "species": [ "http://swapi.co/api/species/1/" ], "vehicles": [ "http://swapi.co/api/vehicles/14/", "http://swapi.co/api/vehicles/30/" ], "starships": [ "http://swapi.co/api/starships/12/", "http://swapi.co/api/starships/22/" ], "created": "2014-12-09T13:50:51.644000Z", "edited": "2014-12-20T21:17:56.891000Z", "url": "http://swapi.co/api/people/1/"
{ "count": 87, "next": "http://swapi.co/api/people/?page=3", "previous": "http://swapi.co/api/people/?page=1", "results": [ .... ]}
Links ?
page-based
{ "count": 30, "next": "http://swapi.co/api/people/?offset=60&limit=30", "previous": "http://swapi.co/api/people/?offset=0&limit=30", "results": [ .... ]}
Links ?
offset-based
{ "count": 87, "next": "http://swapi.co/api/people/?cursor=1374004777531007833", "previous": "http://swapi.co/api/people/?cursor=2627305797328905258", "results": [ .... ]}
Links ?
cursor-based
{ "name": "Luke Skywalker", "height": "172", "mass": "77", "hair_color": "blond", "skin_color": "fair", "eye_color": "blue", "birth_year": "19BBY", "gender": "male", "homeworld": "http://swapi.co/api/planets/1/", "films": [ "http://swapi.co/api/films/6/", "http://swapi.co/api/films/3/", "http://swapi.co/api/films/2/", "http://swapi.co/api/films/1/", "http://swapi.co/api/films/7/" ], "species": [ "http://swapi.co/api/species/1/" ], "vehicles": [ "http://swapi.co/api/vehicles/14/", "http://swapi.co/api/vehicles/30/" ], "starships": [ "http://swapi.co/api/starships/12/", "http://swapi.co/api/starships/22/" ], "created": "2014-12-09T13:50:51.644000Z", "edited": "2014-12-20T21:17:56.891000Z", "url": "http://swapi.co/api/people/1/"}
Wait I can't have nested-resources?
Wait I can't have nested-resources?
This is not really HTTP now is it ?
Think about howyou surf the webThat's pretty nice right ?
HATEOASHypermedia as the Engine of Application State
HATEOAS Hypermedia as the Engine ofApplication State
{ "name": "Boba Fett", "height": "183", "mass": "78.2", "hair_color": "black", "skin_color": "fair", "eye_color": "brown", "birth_year": "31.5BBY", "gender": "male", "homeworld": "http://swapi.co/api/planets/10/", "films": [ "http://swapi.co/api/films/5/", "http://swapi.co/api/films/3/", "http://swapi.co/api/films/2/" ], "species": [ "http://swapi.co/api/species/1/" ], "vehicles": [], "starships": [ "http://swapi.co/api/starships/21/" ], "created": "2014-12-15T12:49:32.457000Z", "edited": "2014-12-20T21:17:50.349000Z", "url": "http://swapi.co/api/people/22/"
HATEOAS
"I'm not going to make 500 calls just to get somecomments"
HATEOAS
"I'm not going to make 500 calls just to get somecomments"
GET /films
GET /films/4,5,6
HATEOAS
"Can't you just add the resources to the url?"
GET /articles/1?include=comments,likes
QUERY PARAMETERS
Filter the response to what's valid on the query
GET /cheeses?type=brie,cheddar
GET /cheeses?type[]=brie&type[]=cheddar
AUTH
AUTH
AUTH
- HTTP Basic- JWT- OAuth 2.0
HTTP Basic
- Super easy- Not super safe
JWT (Json web tokens)
- No database hassle- Needs some knowledge
JWT (Json web tokens)
ServerClient
make new callswith the token
Return token
Send credentials
JWT (Json web tokens)
https://jwt.io/
OAuth2
- Very secure and very flexible- Can be daunting to setup
OAuth2
ServerClient
make new callswith the token
Return token +Refresh token
Send credentials
OAuth2
ServerClient
make new callswith the token
Return token +Refresh token
Send refresh token
API VERSIONING
API VERSIONING
YOU SHOULD NOTHAVE TO VERSION AN
API!
API VERSIONING
BUT YOU WILL HAVETO EVENTUALLY
API VERSIONING
GET /api/v1/artists/taylor-swift
API VERSIONING
GET /api/v1/artists/taylor-swift
The good The bad
Easy to implement Routes should not change
Easy to see what versionwe are on
Meta data should be in aheader
API VERSIONING
GET /api/artists/taylor-swiftx-api-version: 1
API VERSIONING
The good The bad
Meta data is in the header Custom header ... ?
Url does not change You need documentation
Url does not change
GET /api/artists/taylor-swiftx-api-version: 1
API VERSIONING
GET /api/artists/taylor-swiftAccept: application/vnd.website.v1+json
API VERSIONING
The good The bad
Meta data is in the header Harder to test
Standard http header You need documentation
GET /api/artists/taylor-swiftAccept: application/vnd.website.v1+json
Random TIPS
START WITH DOCUMENTATION
Random TIPS
LOOK AT THE USER FIRST, NOT THE TECH(A GOOD API DOES NOT NEED TO BE REST)
Random TIPS
TYPE CAST YOUR RETURN VALUES
Random TIPS
CONSISTENCY IS KING
Random TIPS
TEST YOUR ENDPOINTS
Random TIPS
NEVER TRUST YOUR USERS
Random TIPS
Use PHP?
Laravel: https://github.com/dingo/apiVanilla: https://thephpleague.com https://github.com/spatie
Use Python?
Django: http://www.django-rest-framework.org
Random TIPS
Want to know more?
This talks in text form:https://blog.madewithlove.be/post/birdseye-view-on-api
Phil Sturgeon's book:
https://leanpub.com/build-apis-you-wont-hate
Great O'Reily book RESTful Web APIs:
http://shop.oreilly.com/product/0636920028468.do
Questions!
Twitter: TheEdonian
THANKS !
Recommended