Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Building Great APIs (True North PHP 2015)

Ben Ramsey
November 08, 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.

Ben Ramsey

November 08, 2015
Tweet

More Decks by Ben Ramsey

Other Decks in Programming

Transcript

  1. 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
  2. It’s as if we wanted to invent a way for

    there to be someone wrong on the Internet.
  3. 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.”
  4. 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
  5. BUILDING GREAT APIs POSSIBLE SOLUTION ▸ RPC (remote procedure call)

    ▸ saveCampaignData ▸ email: [email protected] ▸ postalCode: 12345 ▸ getCampaignData
  6. POST /rpc.php HTTP/1.1 Host: api.example.com Content-Length: 125 { "method": "saveCampaignData",

    "params": { "email": "[email protected]", "postalCode": "12345" } } HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 05:26:24 GMT Content-Length: 23 { "status": "success" }
  7. 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.”
  8. 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?
  9. 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
  10. 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": "[email protected]", "postalCode": "12345" } ] }
  11. 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
  12. 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" }
  13. 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" }
  14. POST /record HTTP/1.1 Host: api.example.com Content-Length: 91 { "campaignID": "o0zJeEcJ",

    "email": "[email protected]", "postalCode": "12345" } HTTP/1.1 200 OK Date: Sat, 10 Oct 2015 17:28:45 GMT Content-Length: 119 { "recordId": "5s7ytJlT", "campaignID": "o0zJeEcJ", "email": "[email protected]", "postalCode": "12345" }
  15. 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": "[email protected]", "postalCode": "12345" }
  16. LEVEL 0: THE SWAMP OF POX LEVEL 1: RESOURCES LEVEL

    2: HTTP VERBS Richardson Maturity Model
  17. 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.
  18. 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?
  19. BUILDING GREAT APIs BETTER SOLUTION USING HYPERMEDIA ▸ Use links

    to provide relationships between resources ▸ Use media types to describe how to process a representation
  20. 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
  21. { "_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": "[email protected]", "postalCode": "12345" }] } }
  22. LEVEL 0: THE SWAMP OF POX LEVEL 1: RESOURCES LEVEL

    2: HTTP VERBS LEVEL 3: HYPERMEDIA Richardson Maturity Model
  23. 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?
  24. 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
  25. 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
  26. THANK YOU. ANY QUESTIONS? If you want to talk more,

    feel free to contact me. benramsey.com @ramsey github.com/ramsey [email protected] 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.
  27. 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
  28. 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