Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Building web-API without Rails, registration or sms
Andrey Savchenko
November 20, 2015
Programming
3
670
Building web-API without Rails, registration or sms
Pivorak meetup #6, Lviv
Andrey Savchenko
November 20, 2015
Tweet
Share
More Decks by Andrey Savchenko
See All by Andrey Savchenko
Kuzya the Hob vs. Alien
ptico
0
66
The big, the small and the Redis
ptico
1
200
Zen TDD
ptico
2
170
The Application: An Unexpected Journey
ptico
1
250
The scary fairy tale about MVC or How to stop worrying and start to write ruby code
ptico
3
240
How to f*ck up the refactoring
ptico
11
410
Practical SOLID with Rails
ptico
5
580
Redis - little helper for big applications (rus)
ptico
3
130
Other Decks in Programming
See All in Programming
Efficient UI testing in Android
alexzhukovich
2
130
Computer Vision Seminar 1/コンピュータビジョンセミナーvol.1 OpenCV活用
fixstars
0
170
Git操作編
smt7174
2
260
테라폼으로 ECR 관리하기 (How to Manage ECR with Terraform)
posquit0
0
530
それ全部エラーメッセージに書いてあるよ!〜独学でPHPプログラミングが上達するたった一つの方法〜
77web
1
160
回帰分析ではlm()ではなくestimatr::lm_robust()を使おう / TokyoR100
dropout009
0
4.6k
kintoneでランダム取得を作ってみた(imoniCamp 2022-07-27)
shokun1108
0
150
設計の考え方とやり方
masuda220
PRO
56
31k
サーバーレスパターンから学ぶデータ分析基盤構築 / devio2022
kasacchiful
0
510
Untangling Coroutine Testing (Droidcon Berlin 2022)
zsmb
2
490
Now in Android Overview
aosa4054
1
420
How GitHub Supports Vim License Detection, The Five Years Journey
othree
1
390
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
56
6.4k
Embracing the Ebb and Flow
colly
73
3.4k
The Brand Is Dead. Long Live the Brand.
mthomps
46
2.7k
Facilitating Awesome Meetings
lara
29
4.1k
StorybookのUI Testing Handbookを読んだ
zakiyama
6
2.5k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
7
1.1k
JazzCon 2018 Closing Keynote - Leadership for the Reluctant Leader
reverentgeek
173
8.6k
Writing Fast Ruby
sferik
612
57k
Keith and Marios Guide to Fast Websites
keithpitt
404
21k
Producing Creativity
orderedlist
PRO
334
37k
Clear Off the Table
cherdarchuk
79
290k
Debugging Ruby Performance
tmm1
65
10k
Transcript
None
About me Andriy Savchenko /ptico CTO Aejis andriy@aejis.eu
RubyMeditation
W A R N I N G THIS TALK CONTAINS
LOTS OF CODE THIS IS YOUR LAST CHANCE TO LEAVE AUDITORY
Problems with rails
• Low latency • Dependency hell • MVC is only
suitable for simple CRUD • ActiveSupport
Other frameworks
• grape • sinatra • rum • nyny
And…
Rack
run ->(env) { [ 200, # <= Response code {'Content-Type'
=> 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ] }
require 'json' run ->(env) { [ 200, {'Content-Type' => 'application/json'},
[ JSON.dump({ a: 1 }) ] # <= Almost API ;) ] }
Add some OOP
run ->(env) { [ 200, # <= Response code {'Content-Type'
=> 'application/json'}, # <= Headers [ '{"a": 1}' ] # <= Body ] }
class Responder def response_code 200 end def headers {'Content-Type' =>
'application/json'} end def body [ JSON.dump({ a: 1 }) ] end end
class Responder def response_code @code end def headers @headers end
def body [ JSON.dump(@body) ] end end
class Example < Responder def initialize(env) @code = 200 @headers
= {'Content-Type' => 'application/json'} @body = { a: 1 } end end
class ReadUsers < Responder def initialize(env) @code = 200 @headers
= {'Content-Type' => 'application/json'} @body = DB[:users].all end end
run ->(env) { result = ReadUsers.new(env) [result.response_code, result.headers, result.body] }
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
Less generic example
Good API • Proper status codes • Compatibility (?suppress_response_code=true) •
Metadata
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
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
class Responder def default_response_headers { 'Content-Type' => 'application/json' }.dup end
def http_response_headers @headers end end
class Responder def body @body end private def http_response_body [
JSON.dump(body) ] end end
class ReadUsers < Responder def call @body = DB[:users].all end
end
class Read < Responder def call @body = fetch end
end
class ReadUsers < Read def fetch DB[:users].all end end
class Write < Responder def call @body = valid_params? ?
success : failure end private def success; end def failure; end def valid_params? true end end
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' } end end
class Responder def body { code: http_response_code, result: @body, meta:
meta } end def meta { server_time: Time.now.to_i } end end
{ "code": 200, "result": [ { "id": 1, "name": "Andriy
Savchenko", "email": "andriy@aejis.eu", "company": "Aejis", "hiring": true } ], "meta": { "server_time": 1447939835 } }
Awesome!
Routers • Rack::Builder • http_router (gh:joshbuddy/http_router) • lotus-router (gh:lotus/router) •
signpost (gh:Ptico/signpost) • journey (dead)
Advantages
Faster $ ruby -v ruby 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] | |============================================================================|
Faster • 4x faster then rails-api & 2x then sinatra
• Ready for further improvements
Magic-less • Base responder takes ≈ 65LOC • The only
dependency is Rack (optional)
Maintainable • Stable object interface • Each responder can have
its own file structure • SOLID • Test-friendly
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 andriy@aejis.eu