Usable Rest APIs by Javier Ramirez at London Ruby User Group

3,164 views
3,050 views

Published on

With the adoption of REST, the proliferation of smartphones and tablets, and the second coming of JavaScript, exposing our applications as a service is now more important than ever.

Rails or Sinatra make really easy to create a (kinda) RESTful API but, in many occassions, these APIs are designed without really thinking on the developers that will have to use them.

I want to talk about some of the points that can help making your API more developer-friendly. Some of the areas I’ll cover will be discoverability, authentication, headers, formats, parameters, documentation and tools.

Talk delivered at London Ruby User Group on 12/12/2011

Published in: Technology
2 Comments
6 Likes
Statistics
Notes
No Downloads
Views
Total views
3,164
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
19
Comments
2
Likes
6
Embeds 0
No embeds

No notes for slide

Usable Rest APIs by Javier Ramirez at London Ruby User Group

  1. 1. usable REST APIs{"links":[ {"rel":"author", "uri":"http://javier-ramirez.com"}, {"rel":"work", "uri":"http://aspgems.com"}, {"rel":"blog", "uri":"http://formatinternet.com"}, {"rel":"twittEr", "uri":"http//twitter.com/supercoco9"}]}
  2. 2. 1996
  3. 3. 1995
  4. 4. 1996
  5. 5. 1994
  6. 6. 2001
  7. 7. 1999
  8. 8. 2004
  9. 9. Web usability is anapproach to make web sites easy to use for an end-user, without the requirement that any specialized training be undertaken.[
  10. 10. LearnabilityEfficIeNcyMemorabiliTyErrorsSatisfActiOn
  11. 11. I want YOU to makea (REST) API
  12. 12. REST in a nutshellclient server stateless layered and cacheableResources Resource Identifiers Resource metadataUniform interface operations Representations Representation metadataOptionally: code on demand
  13. 13. making a REST API with Rails? easy
  14. 14. BASIC WEB/apifunctionality IN RAILS
  15. 15. CohesionpleasE
  16. 16. separation of concerns
  17. 17. SUCCESS consistently fail consistently
  18. 18. expose ONLY WHAT IS Strictlynecessary
  19. 19. resources are not models
  20. 20. Aggregation/ composition Multiple representations
  21. 21. Multiple consumers
  22. 22. All your FORMATare belong to us
  23. 23. > curl -d "login=ficticious_user@invoicefu.com&password=keepdreaming" https://invoicefu.com/api/session?format=json{"user":{"id":108,"name":"Nicolas Carroll","email":"ficticioususer@invoicefu.com","locale":"en","twitter_nickname":null,"facebook_uid":null,"facebook_nickname":null,"api_key":"dbd349b30b6d9fde97b01b827e6be5ed1e4fbe72","links":[{"rel":"session","uri":"https://invoicefu.com/api/session","methods":"GET,POST,DESTROY"},"rel":"account","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake","methods":"GET,PUT"},"rel":"clients","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/clients","methods":"GET,POST"},{"rel":"new_client","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/clients/new","methods":"GET"},{"rel":"invoices","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices","methods":"GET,POST"},{"rel":"new_invoice","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices/new","methods":"GET"},{"rel":"proformas","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/proformas","methods":"GET,POST"},{"rel":"new_proforma","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/proformas/new","methods":"GET"}]}}
  24. 24. can I haz cat readable anzwa> curl -d "login=ficticioususer@invoicefu.com&password=yeahyeah" "https://invoicefu.com/api/session?&format=xml<?xml version="1.0" encoding="UTF-8"?><user> <id>108</id> <name>Nicolas Carroll</name> <email>user000007@invoicefu.com</email> <locale>en</locale> <twitter-nickname nil="true"></twitter-nickname> <facebook-uid nil="true"></facebook-uid> <facebook-nickname nil="true"></facebook-nickname> <api-key>dbd349b30b6d9fde97b01b827e6be5ed1e4fbe72</api-key> <links> <link> <rel>session</rel> <uri>https://invoicefu-localhost.com/api/session</uri> <methods>GET,POST,DESTROY</methods> </link> <link> <rel>account</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake</uri> <methods>GET,PUT</methods> </link> <link> <rel>clients</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/clients</uri> <methods>GET,POST</methods> </link> <link> <rel>new_client</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/clients/new</uri> <methods>GET</methods> </link> (…) </links></user>
  25. 25. Accept: application/vnd.aspgems.invoicefu.v1.xml THE ACCEPT HEADERHTTP/REST Standard Not everyone Less obviousUnambiguous supports headers Harder to useResources != or custom types Non standard content-Representations typesVersion as you need it Skips HTTP server logs
  26. 26. templates for new resources> curl "https://invoicefu.com/api/v1/accounts/108-cole-mertz-fake/invoices/new?api_key=ddd349b30b6d9fde97b01b827e6be5ed1e4fbe72&format=json"{"invoice":{"number":"2011/30","issued_on":"2011-12-12","proforma_id":null,"notes":null,"footer":null,"locale":"en","currency_code":"USD","currency_symbol":"$","ac_name":"Cole-Mertz#FAKE","ac_company_number_name":"Companynumber","ac_company_number":"25465828K","ac_tax_number_name":"VATNumber","ac_tax_number":"ES25464828k","ac_address":"234 brecknockroad","ac_city":"london","ac_province":null,"ac_postal_code":"n18 5bq","ac_country_name":"UnitedKingdom","cl_email":null,"cl_name":null,"cl_company_number_name":null,"cl_company_number":null,"cl_tax_number_name":null,"cl_tax_number":null,"cl_address":null,"cl_city":null,"cl_province":null,"cl_postal_code":null,"cl_country_name":null,"invoice_lines":[],"discount_percent":null,"tax_lines":[{"name":"TVA","signed_percent":"19.6"}],"paid":"0.0","links":[{"rel":"payments","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices//payments","methods":"POST"},{"rel":"account","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake","methods":"GET,PUT"},{"rel":"client","uri":null,"methods":"GET,PUT,DELETE"},{"rel":"proforma","uri":null,"methods":"GET,PUT,DELETE"},{"rel":"pdf","uri":null,"methods":"GET"},{"rel":"invoices","uri":"https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices","methods":"GET,POST"}]}}j
  27. 27. EASy To FIND
  28. 28. > curl https://invoicefu.com?format=xml (or curl -H "Accept: application/xml" "https://invoicefu.com")<?xml version="1.0" encoding="UTF-8"?><invoicefu> <links> <link> <rel>session</rel> <uri>https://invoicefu.com/api/session</uri> <methods>POST.DELETE</methods> </link> <link> <rel>countries</rel> <uri>https://invoicefu.com/api/countries</uri> <methods>GET</methods> </link> <link> <rel>api_v1</rel> <uri>https://invoicefu.com/api/session?api_version=1</uri> <methods>POST.DELETE</methods> </link> <link> <rel>xml_representation</rel> <uri>https://invoicefu.com/api/session?format=xml</uri> <methods>POST.DELETE</methods> </link> <link> <rel>json_representation</rel> <uri>https://invoicefu.com/api/session?format=json</uri> <methods>POST.DELETE</methods> </link> <link> <rel>strict_parameters</rel> <uri>https://invoicefu.com/api/session?strict=true</uri> <methods>POST.DELETE</methods> </link> </links>
  29. 29. > curl -d "login=ficticioususer@invoicefu.com&password=yeahyeah" "https://invoicefu.com/api/session?&format=xml<?xml version="1.0" encoding="UTF-8"?><user> <id>108</id> <name>Nicolas Carroll</name> <email>user000007@invoicefu.com</email> <locale>en</locale> <twitter-nickname nil="true"></twitter-nickname> <facebook-uid nil="true"></facebook-uid> <facebook-nickname nil="true"></facebook-nickname> <api-key>dbd349b30b6d9fde97b01b827e6be5ed1e4fbe72</api-key> <links> <link> <rel>session</rel> <uri>https://invoicefu-localhost.com/api/session</uri> <methods>GET,POST,DESTROY</methods> </link> <link> <rel>account</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake</uri> <methods>GET,PUT</methods> </link> <link> <rel>clients</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/clients</uri> <methods>GET,POST</methods> </link> <link> <rel>new_client</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/clients/new</uri> <methods>GET</methods> </link> <link> <rel>invoices</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/invoices</uri> <methods>GET,POST</methods> </link> (…) </links></user>
  30. 30. BASIC ACCESS AUTHENTICATION authenticate_or_request_with_http_basic do |login, password| User.find_by_login_and_password login, password endUser and password must be passed every timeTOKENDigest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )User.find_by_login_and_api_key( params[:login], params[:api_key] )Client can send it as a parameter or as a headerOAUTHDepends on third party librariesRequires initial registration of client and more integration
  31. 31. Performance
  32. 32. params & debug
  33. 33. > curl "https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices/new?api_key=ddd349b30b6d9fde97b01b827e6be5ed1e4fbe72&format=xml&debug=1"<?xml version="1.0" encoding="UTF-8"?><errors> <error>extra params found: debug. Allowed params are:account_id,client_id,invoice_id,proforma_id</error></errors>> curl "https://invoicefu.com/api/accounts/108-cole-mertz-fake/invoices/new?api_key=ddd349b30b6d9fde97b01b827e6be5ed1e4fbe72&format=xml&debug=1&strict=false"<?xml version="1.0" encoding="UTF-8"?><invoice> <number>2011/30</number> <issued-on>2011-12-11</issued-on> <proforma-id nil="true"></proforma-id> (...) <links> (...) <link> <rel>invoices</rel> <uri>https://invoicefu-localhost.com/api/accounts/108-cole-mertz-fake/invoices</uri> <methods>GET,POST</methods> </link> </links></invoice>
  34. 34. helping your users WADLjson schema
  35. 35. { <?xml version="1.0"?> "name":"Product", <application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" "properties":{ xsi:schemaLocation="http://wadl.dev.java.net/2009/02 wadl.xsd" "id":{ xmlns:tns="urn:yahoo:yn" "type":"number", xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:yn="urn:yahoo:yn" "description":"Product identifier", xmlns:ya="urn:yahoo:api" "required":true xmlns="http://wadl.dev.java.net/2009/02"> }, <grammars> "name":{ <include "description":"Name of the href="NewsSearchResponse.xsd"/>product", <include "type":"string", href="Error.xsd"/> "required":true </grammars> }, "price":{ <resources base="http://api.search.yahoo.com/NewsSearchService/V1/"> <resource path="newsSearch"> "required":true, <method name="GET" id="search"> "type": "number", <request> "minimum":0, <param name="appid" type="xsd:string" "required":true style="query" required="true"/> 22 }, <param name="type" style="query" default="all"> "tags":{ <option value="all"/> "type":"array", <option value="any"/> "items":{ <option value="phrase"/> "type":"string" </param> } <param name="start" style="query" type="xsd:int" default="1"/> <param name="language" style="query" type="xsd:string"/> } </request> }, <response status="200"> "links":[ <representation mediaType="application/xml" { element="yn:ResultSet"/> "rel":"full", </response> "href":"{id}" <response status="400"> }, <representation mediaType="application/xml" { element="ya:Error"/> "rel":"comments", </response> "href":"comments/?id={id}" </method> </resource> } </resources> ] } </application>
  36. 36. toolscurlHurlHttpartyrestclient
  37. 37. apigee

×