Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Great APIs (Midwest PHP 2016)

Building Great APIs (Midwest PHP 2016)

Much focus has been spent discussing the merits of RESTful APIs. Sure, REST is important, but how do we put these concepts into practice and build great APIs? How RESTful do we need to be, and where do we draw the line with a pragmatic approach to ship code and make our users happy? In this talk, I'll show how to build APIs that put into practice the concepts of REST, while showing that it's okay to bend or break the rules. Along the way, we'll cover the Richardson Maturity Model, hypermedia, when and where to use content negotiation, and API versioning pitfalls


Ben Ramsey

March 04, 2016


  1. Building Great APIs Ben Ramsey • Midwest PHP • 4

    March 2016
  2. HI, I’M BEN. I’m a web craftsman, author, and speaker.

    I build a platform for professional photographers at ShootProof. I enjoy APIs, open source software, organizing user groups, good beer, and spending time with my family. Nashville, TN is my home. ▸Zend PHP Certification Study Guide ▸Nashville PHP & Atlanta PHP user groups ▸array_column() ▸ramsey/uuid ▸league/oauth2-client
  3. None
  4. None
  5. An API enthusiast? What’s that? Eliza BUILDING GREAT APIs

  6. It’d be like saying I’m an OOP enthusiast. Eliza

  7. Somewhere along the way, we became pedantic about APIs.

  8. It’s as if we wanted to invent a way for

    there to be someone wrong on the Internet.
  9. APIs are important to what we do, and there’s no

    escaping them.
  10. How long does this API need to last? QUESTION ONE:

  11. BUILDING GREAT APIs Scenario One: “Our client has an ad

    campaign, and they want to capture email addresses and postal codes and report on that information. We need to give them an endpoint to post data and another endpoint to pull the list. “The campaign will only last a month. We won’t need it after that.”
  12. BUILDING GREAT APIs Scenario One: Break Down ▸ Action 1:

    collect email address and postal code ▸ Action 2: return list of email addresses and postal codes ▸ Only needed for a month
  13. BUILDING GREAT APIs Possible Solution ▸ RPC (remote procedure call)

    ▸ saveCampaignData ▸ email: foo@example.com ▸ postalCode: 12345 ▸ getCampaignData
  14. POST /rpc.php HTTP/1.1 Host: api.example.com Content-Length: 125 { "method": "saveCampaignData",

    "params": { "email": "foo@example.com", "postalCode": "12345" } } HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 05:26:24 GMT Content-Length: 23 { "status": "success" }
  15. Short-lived projects
 are rarely short-lived.

  16. BUILDING GREAT APIs Scenario Two: “Our client has decided they

    want to run the campaign a bit longer, and they also want to run a second campaign like it, but this time, they want to collect first and last names. “Let’s just take that API you wrote and re-purpose it.”
  17. BUILDING GREAT APIs Scenario Two: Caveats ▸ It was specific

    to a single campaign ▸ It only collected email address and postal code ▸ The code is simple enough to make these changes and not break the existing campaign, but will we need to support more changes?
  18. Level 0: The Swamp of Pox Richardson Maturity Model

  19. Great APIs Use Resources AXIOM ONE:

  20. BUILDING GREAT APIs Better Solution Using Resources ▸ Each submission

    creates a new record in the campaign ▸ Resources: campaign, record ▸ Ability to create a record in a campaign ▸ Ability to fetch all records in a campaign
  21. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 16:31:57 GMT

    Content-Length: 23 { "status": "success" } GET /record/create?campaignID=o0zJeEcJ &email=foo@example.com &postalCode=12345 HTTP/1.1 Host: api.example.com
  22. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 16:43:23 GMT

    Content-Length: 120 { "records": [{ "email": "foo@example.com", "postalCode": "12345" }] } GET /campaign/o0zJeEcJ HTTP/1.1 Host: api.example.com
  23. Level 0: The Swamp of Pox Level 1: Resources Richardson

    Maturity Model
  24. Great Web APIs Use HTTP Verbs AXIOM TWO:

  25. BUILDING GREAT APIs Better Solution Using HTTP Verbs ▸ Use

    proper HTTP semantics for resources ▸ POST to create a new record, GET to fetch a campaign ▸ What else can we do with that campaign resource? ▸ Create new campaigns with POST ▸ Update campaigns with PUT ▸ What about the record? ▸ Fetch an individual record with GET
  26. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:14:11 GMT

    Content-Length: 67 { "campaignId": "o0zJeEcJ", "name": "My Awesome Campaign" } POST /campaign HTTP/1.1 Host: api.example.com Content-Length: 37 { "name": "My Awesome Campaign" }
  27. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:22:04 GMT

    Content-Length: 173 { "campaignId": "o0zJeEcJ", "name": "My Awesome Campaign", "startDate": "2015-09-06T18:12:47+00:00", "endDate": "2015-10-06T18:13:06+00:00" } PUT /campaign/o0zJeEcJ HTTP/1.1 Host: api.example.com Content-Length: 143 { "name": "My Awesome Campaign", "startDate": "2015-09-06T18:12:47+00:00", "endDate": "2015-10-06T18:13:06+00:00" }
  28. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:28:45 GMT

    Content-Length: 119 { "recordId": "5s7ytJlT", "campaignID": "o0zJeEcJ", "email": "foo@example.com", "postalCode": "12345" } POST /record HTTP/1.1 Host: api.example.com Content-Length: 91 { "campaignID": "o0zJeEcJ", "email": "foo@example.com", "postalCode": "12345" }
  29. HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:29:31 GMT

    Content-Length: 119 { "recordId": "5s7ytJlT", "campaignID": "o0zJeEcJ", "email": "foo@example.com", "postalCode": "12345" } GET /record/5s7ytJlT HTTP/1.1 Host: api.example.com
  30. Now, our API is starting to look RESTful!

  31. Level 0: The Swamp of Pox Level 1: Resources Level

    2: HTTP Verbs Richardson Maturity Model
  32. Who Will Use This API? QUESTION TWO:

  33. BUILDING GREAT APIs Scenario Three: “Our client likes using our

    API. They want to know if they can use it to create and manage an indefinite amount of campaigns. Oh—I almost forgot—we have another client who wants to use our API to manage campaigns, too. Can we do that?” Why, yes! We can.
  34. BUILDING GREAT APIs Scenario Three: Considerations ▸ Authentication ▸ Stateless

    authentication with keys, tokens, & shared secrets ▸ As more clients want access to your API, how do they know what the endpoints are? ▸ Up until now, it’s through documentation ▸ Is there a better way?
  35. Great Web APIs Use Hypermedia AXIOM THREE:

  36. BUILDING GREAT APIs Better Solution Using Hypermedia ▸ Use links

    to provide relationships between resources ▸ Use media types to describe how to process a representation
  37. HTTP/1.1 201 Created Date: Sat, 10 Oct 2015 17:45:27 GMT

    Content-Type: application/hal+json Content-Length: 838 Location: /campaign/o0zJeEcJ POST /campaign HTTP/1.1 Host: api.example.com Accept: application/hal+json Content-Type: application/json Content-Length: 37 { "name": "My Awesome Campaign" }
  38. { "_links": { "self": { "href": "https://api.example.com/campaign/o0zJeEcJ" }, "curies": [{

    "name": "ex", "href": "https://api.example.com/docs/rels/{rel}", "templated": true }], "ex:campaigns": { "href": "https://api.example.com/campaign/" }, "ex:records": { "href": "https://api.example.com/record/" } }, "name": "My Awesome Campaign", "_embedded": { "ex:record": [{ "_links": { "self": { "href": "https://api.example.com/record/5s7ytJlT" }, "ex:campaign": { "href": "https://api.example.com/campaign/o0zJeEcJ" } }, "email": "foo@example.com", "postalCode": "12345" }] } }
  39. Level 0: The Swamp of Pox Level 1: Resources Level

    2: HTTP Verbs Level 3: Hypermedia Richardson Maturity Model
  40. Hypermedia as the engine of application state.

  41. Will the API Ever Need to Change? QUESTION THREE:

  42. Yes. Your API will change.

  43. Great APIs Are Built For Change AXIOM FOUR:

  44. BUILDING GREAT APIs Evolvability ▸ If we change the URLs,

    will we break clients? ▸ If we add or remove properties, will we break clients? ▸ If we change the input values we accept, will we break clients?
  45. Versioning in APIs exists because we’re afraid of breaking clients.

  46. https://api.example.com/v2/campaign

  47. Hypermedia resolves the versioning problem.

  48. BUILDING GREAT APIs Versioning and Content Negotiation ▸ I can

    change URLs and nothing breaks ▸ I can add properties and nothing breaks ▸ If I remove properties or require new inputs, I can use content- negotiation to version the API: application/vnd.example+json;version=2 ▸ Everybody’s happy
  49. REST is essentially concerned with the evolvability of a network-based

  50. Great APIs Are Built to Last AXIOM FIN:

  51. Roy T. Fielding If you think you have control over

    the system or aren’t interested in evolvability, don’t waste your time arguing about REST. BUILDING GREAT APIs
  52. THANK YOU. ANY QUESTIONS? If you want to talk more,

    feel free to contact me. benramsey.com @ramsey github.com/ramsey ben@benramsey.com Building Great APIs Copyright © 2016 Ben Ramsey This work is licensed under Creative Commons Attribution- ShareAlike 4.0 International. For uses not covered under this license, please contact the author. Ramsey, Ben. “Building Great APIs.” Midwest PHP Conference. Hilton Minneapolis, Minneapolis. 4 Mar. 2016. Conference presentation. This presentation was created using Keynote. The text is set in Chunk Five and Helvetica Neue. The source code is set in Menlo. The iconography is provided by Font Awesome. Unless otherwise noted, all photographs are used by permission under a Creative Commons license. Please refer to the Photo Credits slide for more information. joind.in/talk/de72a Ŏ
  53. PHOTO CREDITS 1. “Sunset at Mid State Fair” by Howard

    Ignatius 2. Photo of Ben Ramsey by Eli White 3. “Magical Merry Go Round” by Floyd Stewart 4. “Dusk Lemonade Stand” by Kellar Wilson 5. “Duty Calls” by Randall Monroe, XKCD 6. “The Wheel” by Aristocrats-hat 7. “Playing With My New Camera” by Mark Walley 8. “Merry-go-round” by mafleen 9. “Caramel Apples” by m01229 10.“Elephant Ears + spinning ride” by m01229 11.“Garden of Unearthly Delights - sideshows” by Heather 12.“The Magnificent Machines of Yeserday (House on the Rock)” by Justin Kern 13.“Mr. Dark’s Ticket Booth (House on the Rock)” by Justin Kern 14.“The Ultimate Carnival” by Trey Ratcliff 15.“Marenghi Orchestrion at the Great Dorset Steam Fair” by Anguskirk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15