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

Building Great APIs (True North PHP 2015)

Building Great APIs (True North PHP 2015)

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.

0c217b9a7dd0aa31ed40bd0f453727e1?s=128

Ben Ramsey
PRO

November 08, 2015
Tweet

Transcript

  1. BUILDING GREAT APIs BEN RAMSEY True North PHP 2015

  2. bram.se/tnphp-great-apis

  3. 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
  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. GET /record/create?campaignID=o0zJeEcJ &email=foo@example.com&postalCode=12345 HTTP/1.1 Host: api.example.com HTTP/1.1 200 OK Date:

    Sat, 10 Oct 2015 16:31:57 GMT Content-Length: 23 { "status": "success" }
  22. GET /campaign/o0zJeEcJ HTTP/1.1 Host: api.example.com HTTP/1.1 200 OK Date: Sat,

    10 Oct 2015 16:43:23 GMT Content-Length: 120 { "records": [ { "email": "foo@example.com", "postalCode": "12345" } ] }
  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. POST /campaign HTTP/1.1 Host: api.example.com Content-Length: 37 { "name": "My

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

    Awesome Campaign", "startDate": "Sun, 06 Sep 2015 18:12:47 +0000", "endDate": "Tue, 06 Oct 2015 18:13:06 +0000" } HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:22:04 GMT Content-Length: 169 { "campaignId": "o0zJeEcJ", "name": "My Awesome Campaign", "startDate": "Sun, 06 Sep 2015 18:12:47 +0000", "endDate": "Tue, 06 Oct 2015 18:13:06 +0000" }
  28. POST /record HTTP/1.1 Host: api.example.com Content-Length: 91 { "campaignID": "o0zJeEcJ",

    "email": "foo@example.com", "postalCode": "12345" } 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" }
  29. GET /record/5s7ytJlT HTTP/1.1 Host: api.example.com 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" }
  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. POST /campaign HTTP/1.1 Host: api.example.com Accept: application/hal+json Content-Type: application/json Content-Length:

    37 { "name": "My Awesome Campaign" } HTTP/1.1 201 Created Date: Sat, 10 Oct 2015 17:45:27 GMT Content-Type: application/hal+json Content-Length: 838 Location: https://api.example.com/campaign/o0zJeEcJ
  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 & 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

    application.
  50. GREAT APIs ARE BUILT TO LAST AXIOM FIN:

  51. If you think you have control over the system or

    aren’t interested in evolvability, don’t waste your time arguing about REST. Roy T. Fielding 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 joind.in/15761 Ŏ This presentation was created using Keynote. The text is set in DIN Alternate and Avenir Next. The source code is set in Source Code Pro. 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. Building Great APIs Copyright © 2015 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” True North PHP. Microsoft Canada, Mississauga. 7 Nov. 2015. Conference presentation.
  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. “The Wheel” by Aristocrats-hat 6. “Playing With My New Camera” by Mark Walley 7. “Merry-go-round” by mafleen 8. “Caramel Apples” by m01229 9. “Elephant Ears + spinning ride” by m01229 10. “Garden of Unearthly Delights - sideshows” by Heather
  54. Photo Credits 11. “The Magnificent Machines of Yeserday (House on

    the Rock)” by Justin Kern 12. “Mr. Dark’s Ticket Booth (House on the Rock)” by Justin Kern 13. “The Ultimate Carnival” by Trey Ratcliff 14. “Marenghi Orchestrion at the Great Dorset Steam Fair” by Anguskirk