41

Building Web-API without Rails, Registration or SMS

Embed Size (px)

Citation preview

Page 1: Building Web-API without Rails, Registration or SMS
Page 2: Building Web-API without Rails, Registration or SMS

About me

Andriy Savchenko ! " # $/ptico

CTO [email protected]

Page 3: Building Web-API without Rails, Registration or SMS

RubyMeditation

Page 4: Building Web-API without Rails, Registration or SMS

W A R N I N GTHIS TALK CONTAINSLOTS

OF CODETHIS IS YOUR LAST CHANCE TO LEAVE AUDITORY

Page 5: Building Web-API without Rails, Registration or SMS

Problems with rails

Page 6: Building Web-API without Rails, Registration or SMS

• Low latency

• Dependency hell

• MVC is only suitable for simple CRUD

• ActiveSupport

Page 7: Building Web-API without Rails, Registration or SMS

Other frameworks

Page 8: Building Web-API without Rails, Registration or SMS

• grape

• sinatra

• rum

• nyny

Page 9: Building Web-API without Rails, Registration or SMS

And…

Page 10: Building Web-API without Rails, Registration or SMS

Rack

Page 11: Building Web-API without Rails, Registration or SMS

run ->(env) { [ 200, # <= Response code {'Content-Type' => 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ]}

Page 12: Building Web-API without Rails, Registration or SMS

require 'json'

run ->(env) { [ 200, {'Content-Type' => 'application/json'}, [ JSON.dump({ a: 1 }) ] # <= Almost API ;) ]}

Page 13: Building Web-API without Rails, Registration or SMS

Add some OOP

Page 14: Building Web-API without Rails, Registration or SMS

run ->(env) { [ 200, # <= Response code {'Content-Type' => 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ]}

Page 15: Building Web-API without Rails, Registration or SMS

class Responder def response_code 200 end

def headers {'Content-Type' => 'application/json'} end

def body [ JSON.dump({ a: 1 }) ] endend

Page 16: Building Web-API without Rails, Registration or SMS

class Responder def response_code @code end

def headers @headers end

def body [ JSON.dump(@body) ] endend

Page 17: Building Web-API without Rails, Registration or SMS

class Example < Responder def initialize(env) @code = 200 @headers = {'Content-Type' => 'application/json'} @body = { a: 1 } endend

Page 18: Building Web-API without Rails, Registration or SMS

class ReadUsers < Responder def initialize(env) @code = 200 @headers = {'Content-Type' => 'application/json'} @body = DB[:users].all endend

Page 19: Building Web-API without Rails, Registration or SMS

run ->(env) { result = ReadUsers.new(env) [result.response_code, result.headers, result.body]}

Page 20: Building Web-API without Rails, Registration or SMS

result = ReadUsers.new(env)r = Nginx::Request.new

result.headers.each_pair { |k, v| r.headers_out[k] = v }Nginx.rputs result.body[0]Nginx.return result.response_code

Page 21: Building Web-API without Rails, Registration or SMS

Less generic example

Page 22: Building Web-API without Rails, Registration or SMS

Good API

• Proper status codes

• Compatibility (?suppress_response_code=true)

• Metadata

Page 23: Building Web-API without Rails, Registration or SMS

class Responder

class << self def call(env) req = ::Rack::Request.new(env) instance = new(req) instance.call instance.to_rack_array end end

attr_reader :request, :params, :headers

def initialize(req) @request = req @params = req.params @headers = default_response_headers end

def call; end

def to_rack_array [http_response_code, http_response_headers, http_response_body] end

end

Page 24: Building Web-API without Rails, Registration or SMS

class Responder

def response_code @response_code || default_response_code end

private

def default_response_code 200 end

def http_response_code params['suppress_response_codes'] ? 200 : response_code end

end

Page 25: Building Web-API without Rails, Registration or SMS

class Responder

def default_response_headers { 'Content-Type' => 'application/json' }.dup end

def http_response_headers @headers end

end

Page 26: Building Web-API without Rails, Registration or SMS

class Responder

def body @body end

private

def http_response_body [ JSON.dump(body) ] end

end

Page 27: Building Web-API without Rails, Registration or SMS

class ReadUsers < Responder def call @body = DB[:users].all endend

Page 28: Building Web-API without Rails, Registration or SMS

class Read < Responder def call @body = fetch endend

Page 29: Building Web-API without Rails, Registration or SMS

class ReadUsers < Read def fetch DB[:users].all endend

Page 30: Building Web-API without Rails, Registration or SMS

class Write < Responder def call @body = valid_params? ? success : failure end

private

def success; end def failure; end def valid_params? true endend

Page 31: Building Web-API without Rails, Registration or SMS

class CreateUser < Write def default_response_code 201 end

def valid_params? params['login'] && params['email'] end

def success DB[:users].insert(params) end

def failure @response_code = 400

{ error: 'Invalid params' } endend

Page 32: Building Web-API without Rails, Registration or SMS

class Responder

def body { code: http_response_code, result: @body, meta: meta } end

def meta { server_time: Time.now.to_i } end

end

Page 33: Building Web-API without Rails, Registration or SMS

{ "code": 200, "result": [ { "id": 1, "name": "Andriy Savchenko", "email": "[email protected]", "company": "Aejis", "hiring": true } ], "meta": { "server_time": 1447939835 }}

Page 34: Building Web-API without Rails, Registration or SMS

Awesome!

Page 35: Building Web-API without Rails, Registration or SMS

Routers

• Rack::Builder

• http_router (gh:joshbuddy/http_router)

• lotus-router (gh:lotus/router)

• signpost (gh:Ptico/signpost)

• journey (dead)

Page 36: Building Web-API without Rails, Registration or SMS

Advantages

Page 37: Building Web-API without Rails, Registration or SMS

Faster$ ruby -vruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin15]

$ puma -e production

$ ab -n 10000 -c 100 http://0.0.0.0:9292/users/

|======================|====Rails-API====|=====Sinatra=====|=====Rack API====||Time taken for tests: | 13.262 seconds | 6.858 seconds | 3.665 seconds ||Complete requests: | 10000 | 10000 | 10000 ||Failed requests: | 0 | 0 | 0 ||Requests per second: | 754.03 [#/sec] | 1458.20 [#/sec] | 2728.28 [#/sec] ||Time per request: | 132.620 [ms] | 68.578 [ms] | 36.653 [ms] ||Time per request (c): | 1.326 [ms] | 0.686 [ms] | 0.367 [ms] ||Transfer rate: | 301.91 [KB/sec] | 262.02 [KB/sec] | 402.31 [KB/sec] ||============================================================================|

Page 38: Building Web-API without Rails, Registration or SMS

Faster

• 4x faster then rails-api & 2x then sinatra

• Ready for further improvements

Page 39: Building Web-API without Rails, Registration or SMS

Magic-less

• Base responder takes ≈ 65LOC

• The only dependency is Rack (optional)

Page 40: Building Web-API without Rails, Registration or SMS

Maintainable

• Stable object interface

• Each responder can have its own file structure

• SOLID

• Test-friendly

Page 41: Building Web-API without Rails, Registration or SMS

Questions?

Credits and attributions:• Title illustration by Max Bohdanowski • Lobster Two font by Pablo Impallari & Igino Marini (OFL) • Font Awesome by Dave Gandy - http://fontawesome.io (OFL) • https://www.flickr.com/photos/mattsh/14194586111/ (CC BY-NC-SA 2.0)

Andriy Savchenko ! " # $/ptico

[email protected]