stripe's amber feng on api design

Post on 05-Dec-2014

176 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

Heavybit member and payment processing company Stripe's manages 106 endpoints, 65 versions and 6 API clients all without breaking things. This presentation was given by Stripe's API lead Amber Feng on designing APIs for engagement and ease-of-use. Video available here: http://www.heavybit.com/library/developer-technical/video/2014-09-30-amber-feng

TRANSCRIPT

MOVE FAST,DON'T BREAK YOUR API

AMBER FENG @amfeng

LET'S BUILDAN 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 }

WHAT NEXT?

WHAT NEXT?MORE ENDPOINTS

WHAT NEXT?MORE ENDPOINTSMORE FUNCTIONALITY

WHAT NEXT?MORE ENDPOINTSMORE FUNCTIONALITYMORE CHANGES

WHAT NEXT?MORE ENDPOINTSMORE FUNCTIONALITYMORE CHANGESMORE PROBLEMS

LARGE, TANGLEDCODEBASE

COPY-PASTA

ERROR-PRONEDEPENDENCIES

"CRAP, I FORGOT TO UPDATE THE DOCS!"

— Everyone ever

HOW DO WEMAKE IT BETTER?

DESIGN FORYOURSELF, TOO

SEPARATE DIFFERENT LAYERS OF LOGIC

AuthenticationValidationEndpoint-specific logicConstructing the responseError handling

Error handler

Authenticator

API logic

use ErrorHandleruse Authenticator

get '/v1/charges/:id' do user = env.user id = params[:id] unless user.get_charge(id) raise UserError.new("No charge #{id}!") endend

APIMethods &APIResources

class ChargeCreateMethod < AbstractAPIMethod required :amount, :integer required :card_number, :string

resource ChargeAPIResource def execute create_charge(amount, card_number) endend

class ChargeAPIResource < AbstractAPIResource required :id, :string required :amount, :integer required :card_number, :string required :success, :boolean

def describe_card_number charge.redacted_card_number endend

post '/v1/charges' do APIMethod::ChargeCreateMethod.invokeend

get '/v1/charges' do APIMethod::ChargeRetrieveMethod.invokeend

MAKE IT HARD TOMESS 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) endend

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) endend

COMPATIBILITYLAYERS

Request compatibility

API logic

Construct API response

Response compatibility

IN THEREAL WORLD

106 ENDPOINTS65 VERSIONS6 API CLIENTS

DESIGN FOR YOURSELF:SEPARATE LAYERS OF LOGICMAKE IT HARD TO MESS UPHIDE BACKWARDS COMPAT

WHAT ELSE?

NOT SURE.(WE'RE STILL FIGURING IT OUTAS WE GO.)

THANKS! (:@amfeng

top related