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
Move fast, don't break your API
Search
Amber Feng
September 30, 2014
Programming
18
53k
Move fast, don't break your API
Amber Feng
September 30, 2014
Tweet
Share
More Decks by Amber Feng
See All by Amber Feng
Pixels & Tactics
amfeng
1
9.5k
Building Stripe's API
amfeng
35
52k
Other Decks in Programming
See All in Programming
[DroidKaigi 2024] Android ViewからJetpack Composeへ 〜Jetpack Compose移行のすゝめ〜 / From Android View to Jetpack Compose: A Guide to Migration
syarihu
1
630
LangChainの現在とv0.3にむけて
os1ma
4
920
Securify_エンジニア採用資料
3shake
0
100
What you can do with Ruby on WebAssembly
kateinoigakukun
0
170
GenU導入でCDKに初挑戦し、悪戦苦闘した話
hideg
0
160
Patched fetch did not work
quramy
4
380
いまから追い上げる、Jetpack Compose トレーニング
nyafunta9858
0
590
rails_girls_is_my_gate_to_join_the_ruby_commuinty
maimux2x
0
200
GraphQLの魅力を引き出すAndroidクライアント実装
morux2
3
660
API Platform for Laravel
dunglas
0
320
unique パッケージから学ぶ interning と weak reference @ Asakusa.go#3
karamaru
2
810
GraphQL あるいは React における自律的なデータ取得について
quramy
11
3k
Featured
See All Featured
The Straight Up "How To Draw Better" Workshop
denniskardys
230
130k
WebSockets: Embracing the real-time Web
robhawkes
59
7.3k
Web development in the modern age
philhawksworth
205
10k
How To Stay Up To Date on Web Technology
chriscoyier
786
250k
No one is an island. Learnings from fostering a developers community.
thoeni
18
2.9k
Building a Scalable Design System with Sketch
lauravandoore
459
32k
Producing Creativity
orderedlist
PRO
340
39k
The Cult of Friendly URLs
andyhume
76
6k
Build The Right Thing And Hit Your Dates
maggiecrowley
30
2.3k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
0
120
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
25
3.9k
The Pragmatic Product Professional
lauravandoore
31
6.2k
Transcript
MOVE FAST, DON'T BREAK YOUR API AMBER FENG @amfeng
None
LET'S BUILD AN API!
post '/v1/charges' do card_number = params[:card_number] amount = params[:amount] charge
= create_charge(card_number, amount) json { id: charge.id, amount: charge.amount card_number: charge.redacted_card_number, success: charge.success } end
post '/v1/charges' do ... unless card_number.length == 16 return {error:
"Invalid card number."} end unless amount > 0 and amount <= CHARGE_MAX return {error: "Invalid amount."} end ... end
post '/v1/charges' do api_key = get_api_key user = User.find_by_key(api_key) unless
user return {error: "Invalid API key."} end ... end
curl https://stripe.com/v1/charges -u API_KEY -d card_number=4242424242424242 -d amount=100 => {
id: "ch_xxx", amount: 100, card_number: "*4242", success: true }
None
WHAT NEXT?
WHAT NEXT? MORE ENDPOINTS
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY MORE CHANGES
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY MORE CHANGES MORE PROBLEMS
LARGE, TANGLED CODEBASE
COPY-PASTA
ERROR-PRONE DEPENDENCIES
"CRAP, I FORGOT TO UPDATE THE DOCS!" — Everyone ever
HOW DO WE MAKE IT BETTER?
DESIGN FOR YOURSELF, TOO
SEPARATE DIFFERENT LAYERS OF LOGIC
Authentication Validation Endpoint-specific logic Constructing the response Error handling
Error handler Authenticator API logic
use ErrorHandler use Authenticator get '/v1/charges/:id' do user = env.user
id = params[:id] unless user.get_charge(id) raise UserError.new("No charge #{id}!") end end
APIMethods & APIResources
class ChargeCreateMethod < AbstractAPIMethod required :amount, :integer required :card_number, :string
resource ChargeAPIResource def execute create_charge(amount, card_number) end end
class ChargeAPIResource < AbstractAPIResource required :id, :string required :amount, :integer
required :card_number, :string required :success, :boolean def describe_card_number charge.redacted_card_number end end
post '/v1/charges' do APIMethod::ChargeCreateMethod.invoke end get '/v1/charges' do APIMethod::ChargeRetrieveMethod.invoke end
MAKE IT HARD TO MESS UP
class ChargeCreateMethod < AbstractAPIMethod required :amount, :integer required :card_number, :string
document :amount, "Amount, in cents." document :card_number, "The card number." ... end
HIDE BACKWARDS COMPATIBILITY
<todo: tweet>
def execute if !user.version_1? && params[:amount] raise UserError.new("Invalid param.") end
... if !user.version_1? response.delete(:amount) end end
GATES
- :version: 2014-09-24 :new_gates: - :gate: allows_amount :description: >- Sending
amount is now deprecated.
def execute if !user.gating(:allows_amount) && params[:amount] raise UserError.new("Invalid param.") end
... if !user.gating(:allows_amount) response.delete(:amount) end end
COMPATIBILITY LAYERS
Request compatibility API logic Construct API response Response compatibility
IN THE REAL WORLD
106 ENDPOINTS 65 VERSIONS 6 API CLIENTS
DESIGN FOR YOURSELF: SEPARATE LAYERS OF LOGIC MAKE IT HARD
TO MESS UP HIDE BACKWARDS COMPAT
WHAT ELSE?
NOT SURE. (WE'RE STILL FIGURING IT OUT AS WE GO.)
THANKS! (: @amfeng