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

3f8fcddf7ab5d1bd90b0a0a9adfd6527?s=128

Christophe Philemotte

October 21, 2016
Tweet

Transcript

  1. Build a Web API with Hanami

  2. Bonjour, Je m'appelle Christophe.

  3. pullreview.com rubybelgium.be euranova.eu

  4. None
  5. toch _toch ibakesoftware.com

  6. Hanami?

  7. A Ruby Web Framework

  8. Light & Simple

  9. Modular

  10. Non Intrusive

  11. Test Friendly ✅

  12. Thread Safe ☂

  13. Why Hanami?

  14. What do we need?

  15. Sinatra?

  16. Rails?

  17. Hanami!

  18. A Web API

  19. GET /api/speakers

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

  21. . ├── apps ├── config ├── db ├── lib ├──

    public └── spec
  22. Gemfile

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

    '~> 0.6' gem 'pg'
  24. None
  25. Container Architecture

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

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

  28. Business logic

  29. lib ├── takeoffconf │ ├── entities │ ├── mailers │

    └── repositories └── takeoffconf.rb
  30. Test First

  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
  32. it 'is empty by default' do header 'Content-Type', 'application/json;' get

    '/api/speakers' expect(last_response.body).must_equal '[]' end end
  33. Default Content & Accept type

  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
  35. One Route

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

  37. One Action

  38. $ hanami generate action api speakers#list

  39. # apps/api/controllers/speakers/list.rb module Api::Controllers::Speakers class List include Api::Action accept :json

    def call(params) end end end
  40. No Template One View

  41. # apps/api/views/speakers/list.rb module Api::Views::Speakers class List include Api::View layout false

    def render "[]" end end end
  42. Test First

  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
  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
  45. Entity & Repository

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

    create_speakers
  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
  48. $ hanami db create $ hanami db migrate

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

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

  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 # ...
  52. Retrieve & Expose

  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
  54. Serialize & View

  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
  56. Lessons

  57. Not yet stable But play the game

  58. Read The Code Luke

  59. Ruby FTW

  60. Very Friendly Community

  61. More Code But Clear Intent

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