Designing & Building RESTful JSON APIs

Designing & Building RESTful JSON APIs

This is a talk I gave at Ruby Tuesday (Ottawa's Ruby meetup) on August 26 2014.

To see a recording please go to: http://youtu.be/36M2BSA2LYk
For the code go to: https://github.com/aomra015/api-presentation

Ca839bc293e4ca6f9fa327cf95a414a9?s=128

Ahmed Omran

August 27, 2014
Tweet

Transcript

  1. Designing & Building RESTful JSON APIs Ahmed Omran @this_ahmed github.com/aomra015

  2. 1.What is an API and why build one? 2.How to

    design & build an API 3.Misc things to consider
  3. User Interface

  4. Application Programming Interface

  5. API “Outside” “Inside”

  6. API “Outside” Server,! Device,! Software library, etc. “Inside”

  7. API “Outside” Server “Inside” Web APIs HTTP Client

  8. Outside Client

  9. Multiple Clients

  10. Multiple Clients

  11. Multiple Clients

  12. Multiple Clients

  13. Multiple Clients

  14. Programmer == Customer

  15. Goal: happy & productive programmer

  16. API Happiness • Shared Solution (REST) • Flexible

  17. Shared Solution

  18. Shared Solution Uniform

  19. Shared Solution Uniform Consistent

  20. Shared Solution Uniform Consistent Predictable

  21. Representational State Transfer Shared Solution Uniform Consistent Predictable

  22. Server Client Request Response

  23. Resources Client API Representations Server

  24. ! Contact data in database Client ! ! API Contact

    Data ! JSON over HTTP for our example…
  25. How does client find resource?

  26. URL = HTTP, hostname, URI e.g. http://www.easycontacts.com/contacts

  27. URL = HTTP, hostname, URI e.g. http://www.easycontacts.com/contacts Uniform Resource Identifier

  28. URL = HTTP, hostname, URI e.g. http://www.easycontacts.com/contacts Nouns identify resources

  29. collection: /contacts! item: /contacts/:id Two URIs per resource

  30. Building the API $ gem install rails-api $ rails-api new

    easycontacts It’s rails without the stuff you don’t need…
  31. Respond to • What: Uniform Resource Identifier (URI) • How:

    HTTP Verb to specify action
  32. Rails.application.routes.draw do get "/contacts" end

  33. Rails.application.routes.draw do get "/contacts" end URIs HTTP Verb

  34. Rails.application.routes.draw do get "/contacts" get "/contacts/:id" end Read Read

  35. Rails.application.routes.draw do get "/contacts" post "/contacts" get "/contacts/:id" end Read

    Read Create
  36. Rails.application.routes.draw do get "/contacts" post "/contacts" get "/contacts/:id" put "/contacts/:id"

    end Read Read Create Update
  37. Rails.application.routes.draw do get "/contacts" post "/contacts" get "/contacts/:id" put "/contacts/:id"

    delete "/contacts/:id" end Read Read Create Update Delete
  38. Response = Status Code + JSON

  39. JSON

  40. Rails.application.routes.draw do namespace :api, :defaults => {:format => :json} do

    get "/contacts" post "/contacts" get "/contacts/:id" put "/contacts/:id" delete "/contacts/:id" end end
  41. Rails.application.routes.draw do namespace :api, :defaults => {:format => :json} do

    get "/contacts" post "/contacts" get "/contacts/:id" put "/contacts/:id" delete "/contacts/:id" end end /api/URI
  42. Rails.application.routes.draw do namespace :api, :defaults => {:format => :json} do

    get "/contacts", to: "contacts#index" post "/contacts", to: "contacts#create" get "/contacts/:id", to: "contacts#show" put "/contacts/:id", to: "contacts#update" delete "/contacts/:id", to: "contacts#destroy" end end
  43. JSON Response Formatting gem “active_model_serializers” ! Implementation of JSON-API.org standard

  44. Response = Status Code + JSON

  45. Status Codes • 200, Ok => Successful GET • 204,

    No Content => Successful DELETE or PUT • 201, Created => Successful POST • 422, Unprocessable Entity => Well formed request but there’s a semantic error. i.e. problem creating or updating a resource. • 401, Unauthorized => Authentication credentials missing or incorrect
  46. Respond with JSON & Status Code class ContactsController < ApplicationController

    def index @contacts = Contact.all render json: @contacts, status: :ok end end 200
  47. class ContactsController < ApplicationController def show render json: @contact end

    end 200
  48. class ContactsController < ApplicationController def destroy @contact.destroy ! head :no_content

    end end 204
  49. class ContactsController < ApplicationController def update if @contact.update(contact_params) head :no_content

    else render json: @contact.errors, status: :unprocessable_entity end end end 204 422
  50. def create @contact = Contact.new(params[:contact]) ! if @contact.save render json:

    @contact, status: :created else render json: @contact.errors, status: :unprocessable_entity end end 201 422
  51. def create @contact = Contact.new(params[:contact]) ! if @contact.save render json:

    @contact, status: :created else render json: @contact.errors, status: :unprocessable_entity end end Verbose errors Tell client what went wrong
  52. Verbose Errors

  53. Consume APIs w/ REST Clients • Front-end: ngResource, Ember Data

    • Gems: ActiveResource, Rest-Client
  54. Flexible sweep complexity in the query string

  55. Flexible /contacts?key1=value1&key2=value2

  56. Filtering /contacts?relationship=friend

  57. Sorting /contacts?order=created_at:desc

  58. Search /contacts?q=ahmed

  59. Pagination /contacts?limit=25&offset=50 gem 'kaminari'

  60. Side-load Associated Resources /contacts/25?include=notes

  61. Misc.

  62. Non-resource responses • /calculate, /translate, /convert ?? • use verbs

    & make it clear in API docs • or POST /calculations, /translations, /conversions
  63. Use HTTPS. # config/environments/production.rb MyApp::Application.configure do config.force_ssl = true end

  64. Versioning Rails.application.routes.draw do namespace :api, :defaults => {:format => :json}

    do namespace :v1 do … routes … end end end
  65. Caching Set etag and last_modified response Headers

  66. Caching Set etag and last_modified response Headers When was the

    resource Last- Modified?
  67. Caching Set etag and last_modified response Headers Identifies a specific

    version of a resource
  68. Caching If both haven’t changed — 304 Not Modified

  69. class ContactsController < ApplicationController def show render json: @contact if

    stale?(@contact) end end
  70. gzip compression class Application < Rails::Application config.middleware.use Rack::Deflater end

  71. CORS Same-Origin Policy Problem gem 'rack-cors'

  72. Authentication • Basic (username & password) • Token (Api Key)

    • OAuth (like Valet key)
  73. Documentation gem ‘rspec_api_documentation’ gem 'apipie-rails'

  74. Test your APIs … • Command line: cURL or httpie

    • Browser: Postman, JSON-formatter • RSpec: Request Specs • Rails Minitest: Integration tests
  75. Thank you! Ahmed Omran @this_ahmed https://github.com/aomra015/api-presentation