Chris St. Pierre - @chris_st_pierre
REpresentational
State
Transfer
But why?
import sandwiches
sandwiches.make_me_a_sandwich("ham")
curl -X POST -d '{"type": "ham"}' \
https://sandwiches.example.com/sandwiches
GET http://example.com/v1/pets/ HTTP/1.0{ "pets": [ { "id": "a8533344-6371-4982-a86b-722331839514", "links": [ { "href": "/v1/pets/a8533344-6371-4982-a86b-722331839514", "rel": "self" } ] }, { "id": "00182d56-9981-402b-821e-7b1c2906533c", "links": [ { "href": "/v1/pets/00182d56-9981-402b-821e-7b1c2906533c", "rel": "self" } ] }, { "id": "18e1588a-090e-4ace-94c2-f289617de0cb", "links": [...] }, { "id": "4dc4d7b0-a386-4919-9878-47d4eb8f49ec", "links": [...] }, { "id": "bf61616c-010e-48f2-8173-78b330804cd6", "links": [...] }, { "id": "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940", "links": [...] }, { "id": "72f12e94-33ca-4537-9897-205ffe42350e", "links": [...] } ] }
$ curl http://example.com/v1/owners/{ "owners": [ { "id": "511363b2-8693-4b30-ae4c-09964a4cebe0", "links": [ { "href": "/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0", "rel": "self" } ] }, { "id": "ee5479d6-7070-4109-bfb2-d44e3c42782b", "links": [...] }, { "id": "35a85559-dc03-4aa2-85d2-947e17e310e5", "links": [...] }, { "id": "5e174bc4-4135-4e7b-b957-9705bacc903d", "links": [...] } ] }
$ curl http://example.com/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0{ "id": "511363b2-8693-4b30-ae4c-09964a4cebe0", "name": "Goran Shain", "birthday": "1981-10-27", "shoe_size": 10.5, "pets": [ "a8533344-6371-4982-a86b-722331839514", "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940" ], "links": [ { "href": "/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0", "rel": "self" }, { "href": "/v1/pets/a8533344-6371-4982-a86b-722331839514", "rel": "pet" }, { "href": "/v1/pets/d25dcdd0-28f4-4fd3-9e5f-da1d9d224940", "rel": "pet" } ] }
$ curl http://example.com/v1/owners/\?limit=1\&detail=1{ "owners": [ { "id": "511363b2-8693-4b30-ae4c-09964a4cebe0", "name": "Goran Shain", "birthday": "1981-10-27", "shoe_size": 10.5, "pets": [ "a8533344-6371-4982-a86b-722331839514", "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940" ], "links": [ { "href": "/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0", "rel": "self" }, { "href": "/v1/pets/a8533344-6371-4982-a86b-722331839514", "rel": "pet" }, { "href": "/v1/pets/d25dcdd0-28f4-4fd3-9e5f-da1d9d224940", "rel": "pet" } ] } ] }
/v1/owners
/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0
/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0/pets/?limit=5
/v1/servers/b8060d42-8a2c-4f38-8ffb-b1b749e7a50a/reboot
$ curl http://example.com/v1/owners/{ "owners": [ { "id": "511363b2-8693-4b30-ae4c-09964a4cebe0", "links": [ { "href": "/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0", "rel": "self" } ] }, { "id": "ee5479d6-7070-4109-bfb2-d44e3c42782b", "links": [ { "href": "/v1/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b", "rel": "self" } ] }, { "id": "35a85559-dc03-4aa2-85d2-947e17e310e5", "links": [ { "href": "/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5", "rel": "self" } ] }, { "id": "5e174bc4-4135-4e7b-b957-9705bacc903d", "links": [ { "href": "/v1/owners/5e174bc4-4135-4e7b-b957-9705bacc903d", "rel": "self" } ] } ] }
$ curl http://example.com/v1/owners/{ "owners": [ "511363b2-8693-4b30-ae4c-09964a4cebe0", "ee5479d6-7070-4109-bfb2-d44e3c42782b", "35a85559-dc03-4aa2-85d2-947e17e310e5", "5e174bc4-4135-4e7b-b957-9705bacc903d" ] }
$ curl http://example.com/v1/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b{ "id": "ee5479d6-7070-4109-bfb2-d44e3c42782b", "name": "Remus Bergfalk", "birthday": "1977-05-08", "shoe_size": 9.5, "pets": [ "00182d56-9981-402b-821e-7b1c2906533c", "72f12e94-33ca-4537-9897-205ffe42350e" ], "links": [ { "href": "/v1/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b", "rel": "self" }, { "href": "/v1/pets/00182d56-9981-402b-821e-7b1c2906533c", "rel": "pet" }, { "href": "/v1/pets/72f12e94-33ca-4537-9897-205ffe42350e", "rel": "pet" } ] }
$ curl http://example.com/v2/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b{ "owner": { "id": "ee5479d6-7070-4109-bfb2-d44e3c42782b", "name": "Remus Bergfalk", "birthday": "1977-05-08", "shoe_size": 9.5, "pets": [ "00182d56-9981-402b-821e-7b1c2906533c", "72f12e94-33ca-4537-9897-205ffe42350e" ], "links": [ { "href": "/v2/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b", "method": "GET", "rel": "self" }, { "href": "/v2/pets/00182d56-9981-402b-821e-7b1c2906533c", "method": "GET", "rel": "pet" }, { "href": "/v2/pets/72f12e94-33ca-4537-9897-205ffe42350e", "method": "GET", "rel": "pet" }, { "href": "/v2/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b", "method": "PUT", "rel": "edit" }, { "href": "/v2/owners/ee5479d6-7070-4109-bfb2-d44e3c42782b", "method": "DELETE", "rel": "delete" } ], }, "links": [ { "href": "/v2/owners/", "method": "GET", "rel": "list" }, { "href": "/v2/owners/", "method": "POST", "rel": "create" } ] }
Hypertext
As
The
Engine
Of
Application
State
$ curl -X HEAD \ http://example.com/v1/owners/511363b2-8693-4b30-ae4c-09964a4cebe0# HTTP code: 200
$ curl -X HEAD \ http://example.com/v1/owners/totally-bogus-owner-uuid# HTTP code: 404
$ curl -X POST -d '{"name":"Jovka Garver","birthday":"1983-02-26"}' \ -H 'Content-Type: application/json' http://example.com/v1/owners/{ "id": "b90a363c-d694-4e8c-be68-f53eeece2b99", "name": "Jovka Garver", "birthday": "1983-02-26", "pets": [] }# HTTP code: 201
$ curl http://example.com/v1/owners/{ "owners": [ "511363b2-8693-4b30-ae4c-09964a4cebe0", "ee5479d6-7070-4109-bfb2-d44e3c42782b", "35a85559-dc03-4aa2-85d2-947e17e310e5", "5e174bc4-4135-4e7b-b957-9705bacc903d", "b90a363c-d694-4e8c-be68-f53eeece2b99" ] }
$ curl http://someapp.example.com/v1/books/1{"title": "Moby Dick", "text": "Call me Ishmael. Some years ago...."}
$ curl -H "Accept: text/plain" http://someapp.example.com/v1/books/1Moby Dick Call me Ishmael. Some years ago....
$ curl -H "Accept-Encoding: gzip" http://someapp.example.com/v1/books/1 | \ gunzip -c{"title": "Moby Dick", "text": "Call me Ishmael. Some years ago...."}
$ curl -X POST -d '' \ http://example.com/v1/servers/335b53b4-7b86-4a3b-976e-75f13a0a9e78/reboot# HTTP code: 204
$ curl http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5{ "id": "35a85559-dc03-4aa2-85d2-947e17e310e5", "name": "Jolanda Seaver", "birthday": "1996-08-29", "shoe_size": 8.5, "pets": [ "18e1588a-090e-4ace-94c2-f289617de0cb", "4dc4d7b0-a386-4919-9878-47d4eb8f49ec" ] }
$ curl -X PUT \ -d '{"name":"Jolanda Seaver","birthday":"1996-08-29","shoe_size":9}' \ -H 'Content-Type: application/json' \ http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5{ "id": "35a85559-dc03-4aa2-85d2-947e17e310e5", "name": "Jolanda Seaver", "birthday": "1996-08-29", "shoe_size": 9.0, "pets": [ "18e1588a-090e-4ace-94c2-f289617de0cb", "4dc4d7b0-a386-4919-9878-47d4eb8f49ec" ] }
$ curl -X PUT -d '{"shoe_size":9}' -H 'Content-Type: application/json' \ http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5{"message": {"name": "Missing required parameter in the JSON body or the post body or the query string"}}# HTTP code: 400
$ curl \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262{ "id": "3cfbb699-d2f2-45cd-b932-be5171887262", "name": "Seong-Min Park", "patients": [ "00182d56-9981-402b-821e-7b1c2906533c", ] }
$ curl \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262/patients/{ "patients": [ "00182d56-9981-402b-821e-7b1c2906533c", ] }
$ curl -X PUT \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262/patients/d25dcdd0-28f4-4fd3-9e5f-da1d9d224940{ "patient": { "id": "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940" }, "veterinarian": { "id": "3cfbb699-d2f2-45cd-b932-be5171887262" } }# HTTP code: 201
$ curl \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262/patients/{ "patients": [ "00182d56-9981-402b-821e-7b1c2906533c", "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940" ] }
$ curl -X PUT \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262/patients/d25dcdd0-28f4-4fd3-9e5f-da1d9d224940{ "patient": { "id": "d25dcdd0-28f4-4fd3-9e5f-da1d9d224940" }, "veterinarian": { "id": "3cfbb699-d2f2-45cd-b932-be5171887262" } }# HTTP code: 200
$ curl -X POST -d '{"pet_id":"d25dcdd0-28f4-4fd3-9e5f-da1d9d224940"}' \ -H 'Content-Type: application/json' \ http://example.com/v1/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262/patients/{"message": "Pet d25dcdd0-28f4-4fd3-9e5f-da1d9d224940 is already assigned to veterinarian 3cfbb699-d2f2-45cd-b932-be5171887262"}# HTTP code: 409
$ curl http://example.com/v1/pets/72f12e94-33ca-4537-9897-205ffe42350e{ "id": "72f12e94-33ca-4537-9897-205ffe42350e", "name": "Hellbringer the Befouler", "breed": "domestic shorthair", "veterinarian": "fd87a620-466d-41ae-a6b0-1527b5126c58", "species": "cat", "owners": [ "ee5479d6-7070-4109-bfb2-d44e3c42782b" ] }
$ curl \ http://example.com/v1/pets/72f12e94-33ca-4537-9897-205ffe42350e/veterinarian/{ "veterinarian": "fd87a620-466d-41ae-a6b0-1527b5126c58" }
$ curl \ http://example.com/v1/pets/72f12e94-33ca-4537-9897-205ffe42350e/veterinarian/\?detail=1{ "veterinarian": { "id": "fd87a620-466d-41ae-a6b0-1527b5126c58", "name": "Colobert Bannerman", "specialty": "cats" } }
$ curl http://example.com/v1/pets/4dc4d7b0-a386-4919-9878-47d4eb8f49ec{ "id": "4dc4d7b0-a386-4919-9878-47d4eb8f49ec", "name": "Scarface", "breed": "English Bulldog", "veterinarian": "f5df1cc0-4fa2-4605-af57-4da6479e8afa", "species": "dog", "owners": [ "35a85559-dc03-4aa2-85d2-947e17e310e5", "5e174bc4-4135-4e7b-b957-9705bacc903d" ] }
$ curl \ http://example.com/v1/pets/4dc4d7b0-a386-4919-9878-47d4eb8f49ec/owners/{ "owners": [ "35a85559-dc03-4aa2-85d2-947e17e310e5", "5e174bc4-4135-4e7b-b957-9705bacc903d" ] }
$ curl http://example.com/v2/ownership/5f5c1ded-32e7-4fb2-9b6f-b0aa05616d67{ "id": "5f5c1ded-32e7-4fb2-9b6f-b0aa05616d67", "owner": "511363b2-8693-4b30-ae4c-09964a4cebe0", "pet": "a8533344-6371-4982-a86b-722331839514", }
$ curl -X PATCH -d '{"shoe_size":9.5}' -H 'Content-Type: application/json' \ http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5{ "id": "35a85559-dc03-4aa2-85d2-947e17e310e5", "name": "Jolanda Seaver", "birthday": "1996-08-29", "shoe_size": 9.5, "pets": [ "18e1588a-090e-4ace-94c2-f289617de0cb", "4dc4d7b0-a386-4919-9878-47d4eb8f49ec" ] }
$ curl http://example.com/v1/pets/bf61616c-010e-48f2-8173-78b330804cd6{ "id": "bf61616c-010e-48f2-8173-78b330804cd6", "name": "Kisses", "species": "Hissing Cockroach", "veterinarian": null, "owners": [] }
$ curl -X PATCH \ -d '{"veterinarian":"3cfbb699-d2f2-45cd-b932-be5171887262","owners":["ee5479d6-7070-4109-bfb2-d44e3c42782b"]}' \ -H 'Content-Type: application/json' \ http://example.com/v1/pets/bf61616c-010e-48f2-8173-78b330804cd6{ "id": "bf61616c-010e-48f2-8173-78b330804cd6", "name": "Kisses", "species": "Hissing Cockroach", "veterinarian": "3cfbb699-d2f2-45cd-b932-be5171887262", "owners": [ "ee5479d6-7070-4109-bfb2-d44e3c42782b" ] }# HTTP code: 200
$ data='[ {"node": "veterinarian", "op": "change", "value": "f5df1cc0-4fa2-4605-af57-4da6479e8afa"}, {"node": "owner", "op": "delete", "value": "ee5479d6-7070-4109-bfb2-d44e3c42782b"} ]' $ curl -X PATCH -d "$data" -H 'Content-Type: application/json' \ http://example.com/v2/pets/bf61616c-010e-48f2-8173-78b330804cd6{ "id": "bf61616c-010e-48f2-8173-78b330804cd6", "name": "Kisses", "species": "Hissing Cockroach", "veterinarian": "f5df1cc0-4fa2-4605-af57-4da6479e8afa", "owners": [] }
$ curl -X DELETE \ http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5# HTTP code: 204
$ curl -X DELETE \ http://example.com/v1/owners/35a85559-dc03-4aa2-85d2-947e17e310e5{"message": "No such owner 35a85559-dc03-4aa2-85d2-947e17e310e5"}# HTTP code: 404
$ curl -X POST -d '{"pet_id":"a8533344-6371-4982-a86b-722331839514"}' \ -H 'Content-Type: application/json' \ http://example.com/v1/veterinarians/f5df1cc0-4fa2-4605-af57-4da6479e8afa/patients/{"message": "Pet a8533344-6371-4982-a86b-722331839514 is already assigned to veterinarian f5df1cc0-4fa2-4605-af57-4da6479e8afa"}# HTTP code: 409
| Verb | Collection operation | Record operation |
|---|---|---|
| GET | List | Read |
| HEAD | N/A | Check existence |
| POST | Create | N/A |
| PUT | N/A | Create/replace (idempotent) |
| PATCH | N/A | Partial update |
| DELETE | Delete (rarely) | Delete |
$ curl -X DELETE \ http://example.com/v2/owners/5e174bc4-4135-4e7b-b957-9705bacc903d{"headers": {"WWW-Authenticate": "Basic realm=\"Login Required\""}, "message": "Request requires authentication"}# HTTP code: 401
$ curl -X DELETE -u stpierre:hunter2 \ http://example.com/v2/owners/5e174bc4-4135-4e7b-b957-9705bacc903d# HTTP code: 204
$ curl -u stpierre:hunter2 http://example.com/v2/token/{ "token": "6158a4ff-8d50-47bd-8316-c0008b7cbc88" }
$ curl -X DELETE -H 'X-Token: 6158a4ff-8d50-47bd-8316-c0008b7cbc88' \ http://example.com/v2/pets/18e1588a-090e-4ace-94c2-f289617de0cb# HTTP code: 204
$ curl -u stpierre:hunter2 -b cookies -c cookies \
http://example.com/v2/token/
$ cat cookies
localhost FALSE / FALSE 0 token 6158a4ff-8d50-47bd-8316-c0008b7cbc88
$ curl -X DELETE -b cookies -c cookies \ http://example.com/v2/veterinarians/3cfbb699-d2f2-45cd-b932-be5171887262# HTTP code: 204
$ curl http://example.com/v2/get-nth-prime/?n=2{"n": 2, "prime": 2}
$ curl http://example.com/v2/get-nth-prime/?n=22389047890
$ curl http://example.com/v2/get-nth-prime/?n=22389047890{ "task": { "id": "e8a09eb7-a83e-4d4d-8d31-1208bdb7f990", "status": "QUEUED" } }# HTTP code: 202
$ curl http://example.com/v2/tasks/e8a09eb7-a83e-4d4d-8d31-1208bdb7f990{ "task": { "id": "e8a09eb7-a83e-4d4d-8d31-1208bdb7f990", "status": "PENDING" } }# HTTP code: 200
$ curl http://example.com/v2/tasks/e8a09eb7-a83e-4d4d-8d31-1208bdb7f990{ "task": { "id": "e8a09eb7-a83e-4d4d-8d31-1208bdb7f990", "status": "COMPLETE",, "result": {"n": 22389047890, "prime": 583235838259} } }# HTTP code: 200
$ curl http://example.com/v2/tasks/e8a09eb7-a83e-4d4d-8d31-1208bdb7f990{ "task": { "id": "e8a09eb7-a83e-4d4d-8d31-1208bdb7f990", "status": "ERROR", "error": { "message": "Ran out of numbers, contact your sysadmin to add more" } } }# HTTP code: 200
$ curl -X POST -d '{"image": "myimage", "flavor": "m1.tiny"}' \ http://example.com/v2/servers{ "instance": { "id": "3a7ab55d-83cd-4cc0-aca0-e8feab4fb62b", "hostname": "i-908af05e", "image": "myimage", ... "status": "CREATING" } }# HTTP code: 202