$30 off During Our Annual Pro Sale. View Details »

Build a Web API with Hanami

Build a Web API with Hanami

Today, building a web app usually includes building a web API. In Ruby, you can pick one of common options, Rails or Sinatra, or a new interesting one: Hanami. I'll present in the talk the reasons that pushed me to pick Hanami to develop a web API, a quick tour of the framework and its basics, and how to use it to develop a web API.

code example: https://github.com/toch/takeoffconf2016-jsonapi

Christophe Philemotte

October 21, 2016
Tweet

More Decks by Christophe Philemotte

Other Decks in Programming

Transcript

  1. Build a Web API
    with Hanami

    View Slide

  2. Bonjour,
    Je m'appelle Christophe.

    View Slide

  3. pullreview.com
    rubybelgium.be
    euranova.eu

    View Slide

  4. View Slide

  5. toch
    _toch
    ibakesoftware.com

    View Slide

  6. Hanami?

    View Slide

  7. A Ruby Web
    Framework

    View Slide

  8. Light &
    Simple

    View Slide

  9. Modular

    View Slide

  10. Non Intrusive

    View Slide

  11. Test Friendly

    View Slide

  12. Thread Safe

    View Slide

  13. Why Hanami?

    View Slide

  14. What do we need?

    View Slide

  15. Sinatra?

    View Slide

  16. Rails?

    View Slide

  17. Hanami!

    View Slide

  18. A Web API

    View Slide

  19. GET /api/speakers

    View Slide

  20. $ hanami new takeoffconf \
    --database=postgres \
    --application-name=api

    View Slide

  21. .
    ├── apps
    ├── config
    ├── db
    ├── lib
    ├── public
    └── spec

    View Slide

  22. Gemfile

    View Slide

  23. gem 'bundler'
    gem 'rake'
    gem 'hanami', '~> 0.8'
    gem 'hanami-model', '~> 0.6'
    gem 'pg'

    View Slide

  24. View Slide

  25. Container Architecture

    View Slide

  26. apps
    └── api
    ├── application.rb
    ├── config
    ├── controllers
    ├── templates
    └── views

    View Slide

  27. # config/environment.rb
    Hanami::Container.configure do
    mount Api::Application, at: '/api'
    end

    View Slide

  28. Business logic

    View Slide

  29. lib
    ├── takeoffconf
    │ ├── entities
    │ ├── mailers
    │ └── repositories
    └── takeoffconf.rb

    View Slide

  30. Test First

    View Slide

  31. # spec/api/features/list_speakers_spec.rb
    require 'api_helper'
    describe 'List speakers' do
    it 'is successful' do
    header 'Content-Type', 'application/json;'
    get '/api/speakers'
    expect(last_response).must_be :ok?
    expect(last_response.content_type)
    .must_include "application/json"
    end

    View Slide

  32. it 'is empty by default' do
    header 'Content-Type', 'application/json;'
    get '/api/speakers'
    expect(last_response.body).must_equal '[]'
    end
    end

    View Slide

  33. Default Content &
    Accept type

    View Slide

  34. # api/application.rb
    module Api
    class Application < Hanami::Application
    configure do
    # ...
    default_request_format :json
    default_response_format :json
    body_parsers :json
    end
    end
    end

    View Slide

  35. One Route

    View Slide

  36. # apps/api/config/routes.rb
    get '/speakers', to: 'speakers#list'

    View Slide

  37. One Action

    View Slide

  38. $ hanami generate action api speakers#list

    View Slide

  39. # apps/api/controllers/speakers/list.rb
    module Api::Controllers::Speakers
    class List
    include Api::Action
    accept :json
    def call(params)
    end
    end
    end

    View Slide

  40. No Template
    One View

    View Slide

  41. # apps/api/views/speakers/list.rb
    module Api::Views::Speakers
    class List
    include Api::View
    layout false
    def render
    "[]"
    end
    end
    end

    View Slide

  42. Test First

    View Slide

  43. # spec/api/features/list_speakers_spec.rb
    #...
    describe 'List speakers' do
    # ...
    describe 'When speakers are recorded' do
    before do
    SpeakerRepository.clear
    SpeakerRepository.create(Speaker.new(name: 'Christophe Philemotte',
    twitter: '_toch',
    talk: 'Build a Web API with Hanami'))
    end

    View Slide

  44. it 'returns an array of those speakers' do
    header 'Content-Type', 'application/json;'
    get '/api/speakers'
    expect(last_response.body).must_include "\"name\":\"Christophe
    Philemotte\",\"twitter\":\"_toch\",\"talk\":\"Build a Web API with
    Hanami\""
    end
    end
    end

    View Slide

  45. Entity & Repository

    View Slide

  46. $ hanami generate model speaker
    $ hanami generate migration \
    create_speakers

    View Slide

  47. # db/migrations/20161019092946_create_speakers.rb
    Hanami::Model.migration do
    change do
    create_table :speakers do
    primary_key :id
    column :name, String, null: false
    column :twitter, String
    column :talk, String, null: false
    end
    end
    end

    View Slide

  48. $ hanami db create
    $ hanami db migrate

    View Slide

  49. # lib/takeoffconf/entities/speaker.rb
    class Speaker
    include Hanami::Entity
    attributes :name, :twitter, :talk
    end

    View Slide

  50. # lib/takeoffconf/entities/speaker_repository.rb
    class SpeakerRepository
    include Hanami::Repository
    end

    View Slide

  51. # lib/takeoffconf.rb
    # ...
    mapping do
    collection :speakers do
    entity Speaker
    repository SpeakerRepository
    attribute :id, Integer
    attribute :name, String
    attribute :twitter, String
    attribute :talk, String
    end
    end
    # ...

    View Slide

  52. Retrieve & Expose

    View Slide

  53. # apps/api/controllers/speakers/list.rb
    module Api::Controllers::Speakers
    class List
    include Api::Action
    accept :json
    expose :speakers
    def call(params)
    @speakers = SpeakerRepository.all
    end
    end
    end

    View Slide

  54. Serialize & View

    View Slide

  55. # apps/api/views/speakers/list.rb
    require 'json'
    module Api::Views::Speakers
    class List
    include Api::View
    layout false
    def render
    _raw JSON.dump(speakers.map{ |speaker| speaker.to_h })
    end
    end
    end

    View Slide

  56. Lessons

    View Slide

  57. Not yet stable
    But play the game

    View Slide

  58. Read
    The Code Luke

    View Slide

  59. Ruby FTW

    View Slide

  60. Very Friendly
    Community

    View Slide

  61. More Code But
    Clear Intent

    View Slide


  62. toch/takeoffconf2016-jsonapi
    toch
    _toch
    ibakesoftware.com

    View Slide