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

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

Ahmed Omran

August 27, 2014
Tweet

More Decks by Ahmed Omran

Other Decks in Programming

Transcript

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

    View Slide

  2. 1.What is an API and why build one?
    2.How to design & build an API
    3.Misc things to consider

    View Slide

  3. User Interface

    View Slide

  4. Application Programming Interface

    View Slide

  5. API
    “Outside” “Inside”

    View Slide

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

    View Slide

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

    View Slide

  8. Outside
    Client

    View Slide

  9. Multiple Clients

    View Slide

  10. Multiple Clients

    View Slide

  11. Multiple Clients

    View Slide

  12. Multiple Clients

    View Slide

  13. Multiple Clients

    View Slide

  14. Programmer == Customer

    View Slide

  15. Goal: happy & productive
    programmer

    View Slide

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

    View Slide

  17. Shared Solution

    View Slide

  18. Shared Solution Uniform

    View Slide

  19. Shared Solution Uniform Consistent

    View Slide

  20. Shared Solution Uniform Consistent
    Predictable

    View Slide

  21. Representational State Transfer
    Shared Solution Uniform Consistent
    Predictable

    View Slide

  22. Server
    Client
    Request
    Response

    View Slide

  23. Resources
    Client
    API
    Representations
    Server

    View Slide

  24. !
    Contact data
    in database
    Client
    !
    !
    API
    Contact Data !
    JSON over HTTP
    for our example…

    View Slide

  25. How does client find resource?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  30. Building the API
    $ gem install rails-api
    $ rails-api new easycontacts
    It’s rails without the stuff you don’t need…

    View Slide

  31. Respond to
    • What: Uniform Resource Identifier (URI)
    • How: HTTP Verb to specify action

    View Slide

  32. Rails.application.routes.draw do
    get "/contacts"
    end

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  38. Response =
    Status Code + JSON

    View Slide

  39. JSON

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. JSON Response Formatting
    gem “active_model_serializers”
    !
    Implementation of JSON-API.org standard

    View Slide

  44. Response =
    Status Code + JSON

    View Slide

  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

    View Slide

  46. Respond with JSON & Status Code
    class ContactsController < ApplicationController
    def index
    @contacts = Contact.all
    render json: @contacts, status: :ok
    end
    end
    200

    View Slide

  47. class ContactsController < ApplicationController
    def show
    render json: @contact
    end
    end
    200

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  52. Verbose Errors

    View Slide

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

    View Slide

  54. Flexible
    sweep complexity in the query string

    View Slide

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

    View Slide

  56. Filtering
    /contacts?relationship=friend

    View Slide

  57. Sorting
    /contacts?order=created_at:desc

    View Slide

  58. Search
    /contacts?q=ahmed

    View Slide

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

    View Slide

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

    View Slide

  61. Misc.

    View Slide

  62. Non-resource responses
    • /calculate, /translate, /convert ??
    • use verbs & make it clear in API docs
    • or POST /calculations, /translations, /conversions

    View Slide

  63. Use HTTPS.
    # config/environments/production.rb
    MyApp::Application.configure do
    config.force_ssl = true
    end

    View Slide

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

    View Slide

  65. Caching
    Set etag and last_modified response Headers

    View Slide

  66. Caching
    Set etag and last_modified response Headers
    When
    was the
    resource
    Last-
    Modified?

    View Slide

  67. Caching
    Set etag and last_modified response Headers
    Identifies
    a specific
    version of a
    resource

    View Slide

  68. Caching
    If both haven’t changed — 304 Not Modified

    View Slide

  69. class ContactsController < ApplicationController
    def show
    render json: @contact if stale?(@contact)
    end
    end

    View Slide

  70. gzip compression
    class Application < Rails::Application
    config.middleware.use Rack::Deflater
    end

    View Slide

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

    View Slide

  72. Authentication
    • Basic (username & password)
    • Token (Api Key)
    • OAuth (like Valet key)

    View Slide

  73. Documentation
    gem ‘rspec_api_documentation’
    gem 'apipie-rails'

    View Slide

  74. Test your APIs …
    • Command line: cURL or httpie
    • Browser: Postman, JSON-formatter
    • RSpec: Request Specs
    • Rails Minitest: Integration tests

    View Slide

  75. Thank you!
    Ahmed Omran
    @this_ahmed
    https://github.com/aomra015/api-presentation

    View Slide