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.

Joseph Ruscio

September 21, 2011

More Decks by Joseph Ruscio

Other Decks in Programming


  1. 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
  2. metrics.librato.com Requirements Easy to implement and maintain Performant and scalable

    Easy to understand and use Wednesday, September 21, 2011
  3. 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
  4. metrics.librato.com Resources Anything we expose to the Web Resources have

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

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

    Basic HTTP Authentication over SSL Wednesday, September 21, 2011
  7. 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
  8. metrics.librato.com Test Driven Dev. Simulate requests against the API Assert

    expectations about the response test-unit + rack-test Wednesday, September 21, 2011
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. metrics.librato.com Custom HTTP Header X-Api-Version: 1 May not work with

    legacy clients Don’t do this! Wednesday, September 21, 2011
  21. 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
  22. metrics.librato.com CRUD Pagination { "query":{ "total":128, "found":128, "offset":0, "length":100 },

    "gauges":[ { "name":"gauge1" }, { "name":"gauge2" }, ... ] } Wednesday, September 21, 2011
  23. 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
  24. 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
  25. 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
  26. metrics.librato.com “be conservative in what you do, be liberal in

    what you accept from others” - Postel’s Law Wednesday, September 21, 2011