Serverless APIs in Swift
Rob Allen, Nineteen Feet
November 2017 ~ @akrabat
Slide 2
Slide 2 text
I write APIs
Rob Allen ~ @akrabat
Slide 3
Slide 3 text
Deployment options
1. Physical servers
2. Virtual machines
3. Containers
Rob Allen ~ @akrabat
Slide 4
Slide 4 text
Container deployments
1. Platform (e.g. Kubernetes)
2. Application (e.g. Cloud Foundry)
3. Serverless (e.g. OpenWhisk)
Rob Allen ~ @akrabat
Slide 5
Slide 5 text
Serverless?
"The first thing to know about serverless computing is that
"serverless" is a pretty bad name to call it.""
Brandon Butler, Network World
Rob Allen ~ @akrabat
Slide 6
Slide 6 text
AKA: Functions as a Service
A runtime to execute your functions
No capacity planning or load balancing
Pay for execution, not when idle
Rob Allen ~ @akrabat
Slide 7
Slide 7 text
ThoughtWorks: Technology Radar
"Our teams like the
serverless approach;
it's working well for us
and we consider it a
valid architectural
choice."
2017 Technology Radar
Rob Allen ~ @akrabat
Slide 8
Slide 8 text
ThoughtWorks Technology Radar
Our teams like the
serverless approach;
it's working well for us
and we consider it a
valid architectural
choice.
2017 Technology Radar
Rob Allen ~ @akrabat
Slide 9
Slide 9 text
Use-cases
Synchronous
Service is invoked and provides immediate response
(HTTP requests: APIs, chat bots)
Asynchronous
Push a message which drives an action later
(web hooks, timed events, database changes)
Rob Allen ~ @akrabat
Slide 10
Slide 10 text
Benefits
• No need to think about servers
• Concentrate on application code
• Pay only for what you use, when you use it
• Language agnostic: NodeJS, Swift, Python, PHP Java, C#, etc
Rob Allen ~ @akrabat
Slide 11
Slide 11 text
Challenges
• Start up latency
• Time limit
• State is external
• DevOps is still a thing
Rob Allen ~ @akrabat
Slide 12
Slide 12 text
It's about value
Rob Allen ~ @akrabat
Slide 13
Slide 13 text
You are charged by duration of execution &
memory used
Rob Allen ~ @akrabat
Slide 14
Slide 14 text
Serverless providers
Rob Allen ~ @akrabat
Slide 15
Slide 15 text
OpenWhisk
Rob Allen ~ @akrabat
Slide 16
Slide 16 text
Open source
Multiple providers:
IBM
Adobe
RedHat
Rob Allen ~ @akrabat
Slide 17
Slide 17 text
Many runtimes:
Swift
NodeJS
PHP
Java
Python
Docker
Rob Allen ~ @akrabat
Slide 18
Slide 18 text
Swift
Rob Allen ~ @akrabat
Slide 19
Slide 19 text
Swift is a general-purpose programming
language built using a modern approach
to safety, performance, and software
design patterns.
-- swift.org
Rob Allen ~ @akrabat
Slide 20
Slide 20 text
Modern
Safe
Fast
Rob Allen ~ @akrabat
Slide 21
Slide 21 text
Major features
Strong typing Custom operators
Type inference Tuples
Optionals Generics
Closures Serialisation
ARC Interoperable with C
Rob Allen ~ @akrabat
Slide 22
Slide 22 text
Rock-Paper-Scissors
1 import Foundation
2
3 let shapes = ["rock", "paper", "scissors"]
4
5 for count in 1...3 {
6 print(count)
7 sleep(1)
8 }
9
10 srandom(UInt32(Date().timeIntervalSince1970))
11 let chosenShape = random() % shapes.count
12 print(shapes[chosenShape]);
Rob Allen ~ @akrabat
Slide 23
Slide 23 text
Result
$ swift rock-paper-scissors.swift
1
2
3
scissors
Rob Allen ~ @akrabat
Slide 24
Slide 24 text
Learn the language
Rob Allen ~ @akrabat
Slide 25
Slide 25 text
Remember this?
You are charged by duration of execution &
memory used
Rob Allen ~ @akrabat
Slide 26
Slide 26 text
Performance
Rob Allen ~ @akrabat
Slide 27
Slide 27 text
Memory
Rob Allen ~ @akrabat
Slide 28
Slide 28 text
Hello world in OpenWhisk
Rob Allen ~ @akrabat
Slide 29
Slide 29 text
Hello world in OpenWhisk
Rob Allen ~ @akrabat
Slide 30
Slide 30 text
Running your action
$ wsk action update ping ping.swift
ok: updated action ping
$ wsk action invoke ping --result
{
"ack": "2017-11-06 10:15:00"
}
Rob Allen ~ @akrabat
Slide 31
Slide 31 text
Public HTTP access
Rob Allen ~ @akrabat
Slide 32
Slide 32 text
Web actions
$ wsk action update ping ping.swift --web true
$ curl https://openwhisk.eu-gb.bluemix.net/api/v1/ \
web/19FT_demo/default/ping.json
{
"ack": "2017-11-06 10:15:00"
}
Rob Allen ~ @akrabat
Slide 33
Slide 33 text
What about…
API routing?
Rate limiting?
Authentication?
Custom domains?
Rob Allen ~ @akrabat
Slide 34
Slide 34 text
API Gateway
Rob Allen ~ @akrabat
Slide 35
Slide 35 text
API Gateway
$ wsk api create /myapp /ping GET ping
$ curl https://service.eu.apiconnect.ibmcloud.com/gws/apigateway/api/ \
d12f3ba5e86077e6f0c8...830db1e5a05c88edfb12/ping
{
"ack": "2017-11-06 10:15:00"
}
Rob Allen ~ @akrabat
Slide 36
Slide 36 text
Let's talk about HTTP APIs
Rob Allen ~ @akrabat
Slide 37
Slide 37 text
HTTP APIs
Just because it's serverless doesn't mean we can ignore the basics!
• Status codes
• HTTP method negotiation
• Content-type handling
• Error handling
• Media type format
Rob Allen ~ @akrabat
Slide 38
Slide 38 text
Status codes
Send the right one for the right situation!
1xx Informational
2xx Success
3xx Redirection
4xx Client error
5xx Server error
Rob Allen ~ @akrabat
Slide 39
Slide 39 text
Full control over response
Return a dictionary with the keys: statusCode, headers, body
Do not use an extension on web actions:
curl https://openwhisk.eu-gb.bluemix.net/api/v1/ \
web/19FT_demo/default/ping
Enable the http response on API Gateway:
wsk api create /myapp /ping GET hello --response-type http
Rob Allen ~ @akrabat
Slide 40
Slide 40 text
Full control over response
1 func main(args: [String:Any]) -> [String:Any]
2 {
3 // our code here does something
4
5 return [
6 "statusCode" : 201,
7 "headers": [
8 "Content-Type": "application/json",
9 ],
10 "body" : ["result": "Item created"],
11 ]
12 }
Rob Allen ~ @akrabat
Slide 41
Slide 41 text
HTTP verbs
Method Used for Idempotent?
GET Retrieve data Yes
PUT Change data Yes
DELETE Delete data Yes
POST Change data No
PATCH Update data No
In Serverless, prefer Idempotent
Rob Allen ~ @akrabat
Slide 42
Slide 42 text
Handling incoming data
Parameters arrive in args
1 func main(args: [String:Any]) -> [String:Any] {
2 let formatter = DateFormatter()
3 formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
4 let now = formatter.string(from: Date())
5
6 var data = ["ack": now]
7 if let state = args["state"] as? String {
8 data["state"] = state
9 }
10
11 return ["body": data]
12 }
Rob Allen ~ @akrabat
Slide 43
Slide 43 text
Handling incoming data
GET:
$ curl https://openwhisk.eu-gb.bluemix.net/api/.../ping?state=ABC
{ "ack": "2017-11-06 10:15:00", "state": "ABC" }
POST:
$ curl -X POST -d '{"state": "123"}' \
https://openwhisk.eu-gb.bluemix.net/api/.../ping
{ "ack": "2017-11-06 10:15:00", "state": "123" }
Rob Allen ~ @akrabat
Slide 44
Slide 44 text
HTTP request information
1 func main(args: [String:Any]) -> [String:Any]
2 {
3 guard
4 let method = args["__ow_method"] as? String,
5 let path = args["__ow_path"] as? String,
6 let headers = args["__ow_headers"] as? [String:Any]
7 else {
8 print("Error. Unable to unwrap HTTP request info.")
9 return ["statusCode": 500, "error": "Internal Server Error"]
10 }
11
12 // `method`, `path` and `headers` are valid
Rob Allen ~ @akrabat
Slide 45
Slide 45 text
Content negotiation
Correctly parse the request
• Read the Content-Type header
• Raise "415 Unsupported media type" status if unsupported
Create the correct response
• Read the Accept header
• Set the Content-Type header
Rob Allen ~ @akrabat
Slide 46
Slide 46 text
Reading the HTTP body
JSON is parsed automatically, for everything else read __ow_body
1 import AEXML;
2
3 func main(args: [String:Any]) -> [String:Any]
4 {
5 let headers = args["__ow_headers"] as? [String:Any]
6 let body = args["__ow_body"] ?? ""
7 let xmlDoc = try AEXMLDocument(xml: body)
8
9 // work with `xmlDoc`
10 }
Rob Allen ~ @akrabat
Slide 47
Slide 47 text
Summary
Rob Allen ~ @akrabat
Slide 48
Slide 48 text
Resources
• https://github.com/SwiftOnTheServer/flashcards
• https://akrabat.com/talks/serverless-apis-in-swift-mucon/
• https://swift.org
• http://openwhisk.incubator.apache.org
• https://medium.com/openwhisk
Rob Allen ~ @akrabat