Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building web-API without Rails, registration or...
Search
Andrey Savchenko
November 20, 2015
Programming
920
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Building web-API without Rails, registration or sms
Pivorak meetup #6, Lviv
Andrey Savchenko
November 20, 2015
More Decks by Andrey Savchenko
See All by Andrey Savchenko
The big, the small and the Redis
ptico
1
250
Zen TDD
ptico
2
220
The Application: An Unexpected Journey
ptico
1
320
The scary fairy tale about MVC or How to stop worrying and start to write ruby code
ptico
3
300
How to f*ck up the refactoring
ptico
11
470
Practical SOLID with Rails
ptico
5
640
Redis - little helper for big applications (rus)
ptico
3
180
Other Decks in Programming
See All in Programming
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
170
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.5k
광주소프트웨어마이스터고등학교 DevFest 특강 - 바이브 코딩 시대에서 주니어 개발자로 살아남는 방법
utilforever
1
140
yield再入門 #phpcon
o0h
PRO
0
550
symfony/aiとlaravel/boost
77web
0
130
Apache Hive: Toward a Cloud Native Lakehouse
okumin
0
140
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
460
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
130
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
920
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
440
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
130
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1.2k
Featured
See All Featured
How to Ace a Technical Interview
jacobian
281
24k
New Earth Scene 8
popppiees
3
2.4k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.4k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.6k
Rails Girls Zürich Keynote
gr2m
96
14k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
370
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
260
How to make the Groovebox
asonas
2
2.3k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
First, design no harm
axbom
PRO
2
1.2k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1k
Transcript
None
About me Andriy Savchenko /ptico CTO Aejis
[email protected]
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": "
[email protected]
", "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
[email protected]