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
Reacting to Change - API Days 2014
Search
Kyle Fuller
December 02, 2014
Technology
220
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Reacting to Change - API Days 2014
Kyle Fuller
December 02, 2014
More Decks by Kyle Fuller
See All by Kyle Fuller
Design APIs and deliver what you promised
kylef
0
150
Preparing for the future of API Description Languages
kylef
0
130
Resilient API Design
kylef
1
360
Building a Swift Web API and Application Together
kylef
2
2.2k
Testing without Xcode - CMD+U 2016
kylef
0
300
End-to-end: Building a Web Service in Swift (MCE 2016)
kylef
2
510
Designing APIs for Humans - Write the Docs 2016
kylef
0
360
Embracing Change - MBLTDev 2015
kylef
3
710
Practical Declarative Programming (360 iDev 2015)
kylef
3
560
Other Decks in Technology
See All in Technology
しくみを学んで使いこなそう GitHub Copilot app
torumakabe
2
240
Zoom2Youtube.Claude
kawaguti
PRO
3
490
KiCAD講習会②
tutcreators
0
110
インフラ寄りSREでも 開発に踏み出せる〜境界を越えてユーザー体験に向き合いたい〜
sansantech
PRO
2
3.8k
ゼロをイチにする仕事が終わったあと
smasato
0
340
地域 SRE コミュニティ最前線 / SRE NEXT 2026 Discussion Night Track C
muziyoshiz
0
210
証券システムを10年Scalaで作り続けるということ - 関数型まつり2026
krrrr38
3
840
CIで使うClaude
iwatatomoya
0
250
実装だけじゃない! CCA-F取得エンジニアが教えるClaude Code開発プロセス活用術
diggymo
2
670
事業価値を⽣み出すSREへ SREが担うべき意思決定の5層
kenta_hi
2
3.6k
型は壁、Rustでもバグを直すな、表現できなくせよ
nwiizo
13
2k
“それは自分の仕事じゃない"を越えて行け
yuukiyo
0
150
Featured
See All Featured
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
260
GraphQLとの向き合い方2022年版
quramy
50
15k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
My Coaching Mixtape
mlcsv
0
170
Between Models and Reality
mayunak
4
370
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
330
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
340
A Tale of Four Properties
chriscoyier
163
24k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
Transcript
REACTING TO CHANGE ! KYLE FULLER
BUILDING better API CLIENTS
None
None
DOZENS of APPS
HTTP / APIS
THERE’S AN API
COMPONENTS OF AN API CLIENT
perform(.RetrieveArticles) { result in switch result { case .Success(let representor):
/* We've got some articles */ case .Error(let error): /* We hit an error */ } }
FOUNDATION
NSURLSession OR NSURLConnection
NSURLRequest
NSURLHTTPResponse
SIMPLE...
HTTP SEMANTICS
SERIALISATION (JSON)
CONVERT to VALUE-MODELS
struct Article { let title:String let body:String let author:Author let
image:[NSURL] } struct Author { let name:String let twitter:String }
articles[0]["author"]["name"]
"/articles"
"name"
"nme"
- age + birthday
None
None
MAPPING
HTTP JSON MODEL MAPPING TESTS (STUBS & FIXTURES) DOMAIN LOGIC?
$ cloc **Model**.{h,m} **API**.{h,m} 95 text files. 83 unique files.
0 files ignored. ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Objective C 51 1420 499 5598 C/C++ Header 32 258 480 479 ------------------------------------------------------------------------------- SUM: 83 1678 979 6077 -------------------------------------------------------------------------------
6077 LINES OF CODE
ANDROID
15,000 LINES OF CODE
WINDOWS PHONE
20,000 LINES OF CODE
...
30,000 LINES OF CODE
BUILDING an API CLIENT
DOMAIN-SPECIFIC
INTEGRATED
MIXING PROTOCOL SEMANTICS
WHAT DOES THE API LOOK LIKE?
None
None
None
GETTING STARTED
TESTS
MOCK
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/sessions"]);
} withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"sessions.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }];
{ "created_at": "2014-04-21 23:08:00 +0200", "email": "
[email protected]
", "name": "Kyle Fuller",
"sessions": [ { "created_at": "2014-05-04 15:20:19 +0200", "valid_until": "2014-09-09 15:20:19 +0200", "verified": false }, { "created_at": "2014-05-04 15:20:20 +0200", "valid_until": "2014-09-09 15:20:20 +0200", "verified": true } ] }
AND REPEAT
None
CAN’T WE USE THIS?
UNDERSTANDABLE by machines
POWERING YOUR TESTS with API BLUEPRINT
pod `CCLRequestReplay`
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/sessions"]);
} withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"sessions.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/pods"]); } withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"pods.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/pod/../owners"]); } withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"pod-querykit-owners.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; ...
{ "created_at": "2014-04-21 23:08:00 +0200", "email": "
[email protected]
", "name": "Kyle Fuller",
"sessions": [ { "created_at": "2014-05-04 15:20:19 +0200", "valid_until": "2014-09-09 15:20:19 +0200", "verified": false }, { "created_at": "2014-05-04 15:20:20 +0200", "valid_until": "2014-09-09 15:20:20 +0200", "verified": true } ] } { "name": "QueryKit", "version": "0.8.3", "authors": { "Kyle Fuller": "
[email protected]
" }, "social_media_url": "http://twitter.com/kylefuller", "source": { "git": "https://github.com/QueryKit/QueryKit.git", "tag": "0.8.3" }, "source_files": [ "QueryKit/*.{h}", "QueryKit/ObjectiveC/*.{h,m}" ], "requires_arc": true } ...
let URL = NSBundle.mainBundle().URIForResource("palaver", withExtension:"apib.json") requestReplayManager.addRecordings(fromBlueprintURL:URL, error:nil)
GET /sessions
None
EMBRACE CHANGE
None
WE MADE A MISTAKE.
WE WANT TO CHANGE... ENDPOINT URI.
NEGOTIATE AT RUN-TIME
WE WANT TO CHANGE... PAGINATION LOGIC.
FOLLOWING LINKS
TRANSITION BETWEEN STATES
"Paging is achieved with the published_before parameter."
published_before
[ { "published_date": "2013-04-30T16:05:45+00:00", ... } ]
GET /articles?published_before=2013-04-30T16:05:45+00:00
SERVER LOGIC
Next Page = <some URI>
Prevent BREAKING CHANGES
BUILDING BETTER CLIENTS from THE API
FLEXIBILITY
CASCADED REQUESTS
GET /categories GET /authors GET /articles
BANDWIDTH
LARGE DATA
$ curl -I http://api.something.com/articles HTTP/1.1 200 OK Content-Type: application/json Content-Length:
12524998
BREAK IT UP
PAGINATION
/list VS /list/detail
USE Last-Modified
USE Cache-Control
USE STANDARDS
LET’S BUILD BETTER APIS TOGETHER
KYLE FULLER