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

Implementing a RESTful API with Ruby

Implementing a RESTful API with Ruby

APIs are often one the most powerful vectors for driving product adoption, but are often times bolted onto a SaaS offering as an after-thought. There are many different interpretations (some much looser than others) of what makes an API “RESTful”. At Librato we practice “API-first” development where our API’s are specified, tested, implemented, and finally used to add new features to our own products prior to providing them to our users.

D283895e908601709fd493caf8fe2699?s=128

Joseph Ruscio

September 21, 2011
Tweet

Transcript

  1. Implementing a RESTful API w/Ruby “The Right Way” Wednesday, September

    21, 2011
  2. metrics.librato.com Increase service flexibility/utility Free users’ data Build business value

    Why APIs? Wednesday, September 21, 2011
  3. metrics.librato.com Consumes arbitrary time series data Store it, visualize it,

    analyze it Query it back out from API Rich client interface Librato Metrics Wednesday, September 21, 2011
  4. metrics.librato.com Wednesday, September 21, 2011

  5. metrics.librato.com Requirements Easy to implement and maintain Performant and scalable

    Easy to understand and use Wednesday, September 21, 2011
  6. metrics.librato.com What is REST? Wednesday, September 21, 2011

  7. metrics.librato.com Wednesday, September 21, 2011

  8. metrics.librato.com L2 - CRUD URIs HTTP Methods Wednesday, September 21,

    2011
  9. metrics.librato.com L3 - REST REpresentational State Transfer HATEOAS Hypermedia Constraint

    Wednesday, September 21, 2011
  10. metrics.librato.com “[hypertext constraint is] most often violated within so-called REST

    APIs ... adhere to them or choose some other buzzword for your API.” - Roy Fielding Wednesday, September 21, 2011
  11. metrics.librato.com CRUD API Resources identified/addressed by URI URI Templates HTTP-based

    interactions Wednesday, September 21, 2011
  12. metrics.librato.com Resources Anything we expose to the Web Resources have

    representations JSON,XML,PNG Services exchange representations Wednesday, September 21, 2011
  13. metrics.librato.com URIs Uniform Resource Identifier Uniquely identifies a web resource

    Makes it addressable http://example.org/resource/123 Wednesday, September 21, 2011
  14. metrics.librato.com API Design Constraints JSON output JSON and x-form-urlencoded input

    Basic HTTP Authentication over SSL Wednesday, September 21, 2011
  15. metrics.librato.com Doc. Driven Design URI template Markdown file per-resource URI,

    parameters, examples Production ready before a single LOC! Wednesday, September 21, 2011
  16. metrics.librato.com Wednesday, September 21, 2011

  17. metrics.librato.com Test Driven Dev. Simulate requests against the API Assert

    expectations about the response test-unit + rack-test Wednesday, September 21, 2011
  18. metrics.librato.com Test Driven Dev. def test_create_gauge params = {} params[:name]

    = "my_gauge" post "/gauges", params assert_equal 201, last_response.status assert last_response.original_headers["Location"] =~ /^\/v1\/gauges\/#{params[:name]}$/ end Wednesday, September 21, 2011
  19. metrics.librato.com Wednesday, September 21, 2011

  20. metrics.librato.com Create Resource POST /resource Location: <URI> 201 - Created

    def test_create create_resource( :name => "some_gauge", :period => 60 ) create_resource( :code => 400, :name => "a" * 256 ) end Wednesday, September 21, 2011
  21. metrics.librato.com Read Resource GET /resource/:id 200 OK def test_read name

    = "my_gauge" gauge = read_resource(:id => name) assert_equal(name, gauge['name']) end Wednesday, September 21, 2011
  22. metrics.librato.com Update Resource PUT /resource/:id 204 - No Content def

    test_update name = "my_gauge" update_resource(:name => name, :period => 300) end Wednesday, September 21, 2011
  23. metrics.librato.com Delete Resource DELETE /resource/:id 204 No Content def test_delete

    name = "my_gauge" delete_resource(:id => name) read_resource(:code => 404, :id => name) end Wednesday, September 21, 2011
  24. metrics.librato.com Implementation Wednesday, September 21, 2011

  25. metrics.librato.com Implementation CRUD API is a thin layer over your

    models Sinatra + Sequel Resource per file to_hash method per-model Wednesday, September 21, 2011
  26. metrics.librato.com Implementation # CREATE post "/gauges" do unless params[:name] abort_params({:name

    => ["Must specify gauge name"]}) end if find_gauge(params[:name]) abort_unique({:name => ["Gauge name must be unique"]}) end gauge = create_gauge() halt_created("/gauges/#{gauge.name}") end Wednesday, September 21, 2011
  27. metrics.librato.com Wednesday, September 21, 2011

  28. metrics.librato.com Pagination Request offset - starting index into found length

    - # of resources requested orderby - attribute to sort on sort - ASC/DESC Wednesday, September 21, 2011
  29. metrics.librato.com Pagination Response total - # of resources found -

    # of resources that match the query offset - starting index into found length - # of resources in response Wednesday, September 21, 2011
  30. metrics.librato.com Authentication Secure access through SSL/TLS Block port 80 if

    possible 403 all HTTP requests otherwise Basic Authentication Wednesday, September 21, 2011
  31. metrics.librato.com API Versioning URI templates are static Prefer to maintain

    compatibility Failing that prefer to version your entire API URI? Media Type? Custom Header? Wednesday, September 21, 2011
  32. metrics.librato.com URI http://v1.example.org/resource/123 http://example.org/v1/resource/123 http://example.org/resource/123 ?v=1 Wednesday, September 21, 2011

  33. metrics.librato.com Media Type Negotiate through Accept header application/json;version=1 Wednesday, September

    21, 2011
  34. metrics.librato.com Custom HTTP Header X-Api-Version: 1 May not work with

    legacy clients Don’t do this! Wednesday, September 21, 2011
  35. metrics.librato.com Partial Updates PATCH - RFC5789 PATCH /resource/:id Wednesday, September

    21, 2011
  36. metrics.librato.com HATEOAS CRUD is great except when it isn’t Hypermedia

    models state transitions Inclusion of links in representation Wednesday, September 21, 2011
  37. metrics.librato.com Hypermedia Formats XHTML, ATOM, RSS Domain-specific formats! Add link

    elements to CRUD format Wednesday, September 21, 2011
  38. metrics.librato.com CRUD Pagination { "query":{ "total":128, "found":128, "offset":0, "length":100 },

    "gauges":[ { "name":"gauge1" }, { "name":"gauge2" }, ... ] } Wednesday, September 21, 2011
  39. metrics.librato.com REST Pagination { "next":{ "uri":"http://api.librato.com/gauges?offset=100”, "rel":"http://relations.librato.com/paginate", "media_type":"application/vnd.librato+json" }, "gauges":[

    { "name":"gauge1" }, { "name":"gauge2" }, ... ] } Wednesday, September 21, 2011
  40. metrics.librato.com Link Elements uri attribute describes resource rel attribute contains

    semantic markup link elements can be added/removed from representations Wednesday, September 21, 2011
  41. metrics.librato.com GLory of REST Single bookmarked URI Next states are

    discovered inline Series of representation exchanges transforms application state Wednesday, September 21, 2011
  42. metrics.librato.com Have an API and build it first At least

    use CRUD Docs -> Tests -> Code Make it DRY Dogfood It Learn more about REST Wednesday, September 21, 2011
  43. metrics.librato.com Wednesday, September 21, 2011

  44. metrics.librato.com Wednesday, September 21, 2011

  45. metrics.librato.com Wednesday, September 21, 2011

  46. metrics.librato.com “be conservative in what you do, be liberal in

    what you accept from others” - Postel’s Law Wednesday, September 21, 2011
  47. metrics.librato.com Q&A Wednesday, September 21, 2011