Using the Swift Server API 0.1.0
The Swift Server APIs Project is an effort the provide a common HTTP implementation to be used in the various Swift application server frameworks (Kitura, Perfect and so on). The ARI shows you how to use it on either macOS or Linux.
2018-03-01 Breaking News: The Swift Server API has been sherlocked. Checkout our follow up article: A micro tutorial on Swift NIO.
Swift Server API Series: Part1 - Raw API ✭ Part2 µExpress ✭ Part3 µExpress/NIO
HTTP Server API 0.1.0
On October 2nd, 2017, Chris Bailey of IBM announced the Swift Server API v0.1.0:
HTTP server v0.1.0 from the Swift Server APIs project now available. Time to kick the tires and provide feedback!https://t.co/MfUm8oketx
— Chris Bailey (@Chris__Bailey) October 2, 2017
This is a small tutorial on how to use the API to write a small HTTP endpoint in Swift.
HTTP 0.1.0 is the first result of the Server API working group and is based on a proposal by Johannes Weiß of Apple. It provides a very basic API to implement an HTTP server in Swift. Don’t expect a full fledged web framework, it provides just the basics to handle HTTP requests.
This post tries to explain how to actually use it. Before we start, the project provides nice Jazzy generated API documentation, you may want to keep it open.
Step 0: Requirements
The HTTP 0.1.0 API is shipped as a Swift Package Manager 4 (SPM) package. This means you need to have Swift 4 installed, e.g. on macOS using Xcode 9. To play on Linux you can either download Swift 4 tarballs, or just use the official Docker image (you can also use my helje5/swift-dev image, which comes w/ Emacs, etc).
When using a Docker image, don’t forget to start it with a port exposed, like so:
docker run -p 1337:1337 -it --name sssfun helje5/swift-dev /bin/bash
We start out working in the shell (Terminal.app on macOS) - but if you want, you can switch to Xcode soon.
Step 1: Setup Hello World Package
OK, let’s starts with a very simple Hello World service, which you can hit using http://localhost:1337/hello.
Once you have Swift 4, you can start creating the Swift package. First create a directory, then initialize it:
mkdir sssfun && cd sssfun
swift package init --type executable
Creating executable package: sssfun
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/sssfun/main.swift
Creating Tests/
This creates a basic Hello World tool, you can build and run it:
swift build
Compile Swift Module 'sssfun' (1 sources)
Linking ./.build/x86_64-unknown-linux/debug/sssfun
.build/*/debug/sssfun
Hello, world!
OK, great. We have a working Swift package. If you want to use Xcode, Swift Package Manager can now generate an Xcode project for you:
swift package generate-xcodeproj
generated: ./sssfun.xcodeproj
open sssfun.xcodeproj # and Xcode should open
Within Xcode, make sure you select the right scheme when trying to build and run the tool!
Step 2: Add HTTP 0.1.0 as a Dependency
Within our sssfun
package, you’ll find the Package.swift
file. In there
we need to tell SPM that we want to use the HTTP API package.
Open the file in either Xcode or Emacs, and add the dependency lines as shown:
let package = Package(
name: "sssfun",
dependencies: [
.package(url: "https://github.com/swift-server/http",
from: "0.1.0")
],
targets: [
.target(
name: "sssfun",
dependencies: [ "HTTP" ])
]
)
This tells SPM that we want to use the HTTP-API (living on GitHub). And then
we tell it that our sssfun
tool, needs the HTTP
library within that package.
We also need to specify the API version (0.1.0 in this case).
Call swift build
again, it should now download the dependency and build it:
swift build
Fetching https://github.com/swift-server/http
Cloning https://github.com/swift-server/http
Resolving https://github.com/swift-server/http at 0.1.0
Compile CHTTPParser http_parser.c
Compile Swift Module 'HTTP' (11 sources)
Compile Swift Module 'sssfun' (1 sources)
Linking ./.build/x86_64-unknown-linux/debug/sssfun
Xcode users: Everytime you change the
Package.swift
you need to recreate the Xcode project using:swift package generate-xcodeproj
Excellent. Now we are getting somewhere!
Let’s create a new file - we can call it HelloHTTP.swift
- in the
Sources/sssfun
folder.
You can do this in Emacs/vi (emacs Sources/sssfun/HelloHTTP.swift
),
or in Xcode, however you like:
// File: Sources/sssfun/HelloHTTP.swift
import Foundation
import HTTP
func hello(request: HTTPRequest, response: HTTPResponseWriter )
-> HTTPBodyProcessing
{
response.writeHeader(status: .ok)
response.writeBody("Hello, World!\n")
response.done()
return .discardBody
}
This is our HTTP request handling function. It gets passed in the
HTTPRequest
and the
HTTPResponseWriter
.
It has to return instructions how the API is supposed to handle request
content (e.g. a file upload). In this case, we just drop any content that
is sent (we return .discardBody
).
The HTTPRequest
has properties for the method (e.g. GET or POST),
the target (the URL the request is working on, e.g. /hello
),
HTTP request headers (e.g. User-Agent
),
as well as the HTTP protocol version.
The HTTPResponseWriter
we get allows us to send data back to the client, i.e.
deliver the response content.
In our case we just say .ok
(HTTP 200
status code),
write out some text,
and then mark the response as .done()
.
Very nice. So this is the Hello World of HTTP API.
But there is one piece missing, we need to setup the server running our
handler function.
To do that, we are going to edit Sources/sssfun/main.swift
.
Right now, this contains the print("Hello World")
as generated by SPM.
Replace that code with the HTTP 0.1.0 sample server:
// File: Sources/sssfun/main.swift
import Foundation
import HTTP
let server = HTTPServer()
do {
try server.start(port: 1337, handler: hello)
}
catch {
print("failed to start server:", error)
exit(42)
}
RunLoop.current.run()
We create the server object, start it at port 1337
. Then we start the
Runloop, which is responsible for coordinating the access to the server
sockets. That run()
call will never complete (stop the server in Xcode,
or use Ctrl-C in the shell).
That’s it. Call swift build
and re-run the tool (or just press Run in Xcode):
swift build
Compile Swift Module 'sssfun' (2 sources)
Linking ./.build/x86_64-unknown-linux/debug/sssfun
.build/*/debug/sssfun
Started server on port 1337 with 4 serial queues
of each type and 8 accept sockets
The server is now running and accepting requests.
You can hit it using http://localhost:1337/hello
and it should display Hello World
:

Xcode users: If you see stops in the Xcode debugger related to
SIGPIPE
, create a file~/.lldbinit
with this content:
process handle SIGPIPE -n true -p true -s false
. Restart Xcode.
Step 3: Cows. Lots of them. 🐄🐂🐄
All this is nice, but pretty boring, isn’t it? Let’s add some beef! We are going to use the Swift cows module.
Reopen the Package.swift
file, and add the cows package and the cows
module (Xcode users rerun swift package generate-xcodeproj
after doing this):
let package = Package(
name: "sssfun",
dependencies: [
.package(url: "https://github.com/swift-server/http",
from: "0.1.0"),
.package(url: "https://github.com/AlwaysRightInstitute/cows.git",
from: "1.0.0")
],
targets: [
.target(
name: "sssfun",
dependencies: [ "HTTP", "cows" ]),
]
)
Next we’ll add beef to our HelloHTTP.swift
endpoint in the
Sources/sssfun
folder.
// File: Sources/sssfun/HelloHTTP.swift
import Foundation
import HTTP
import cows
func hello(request: HTTPRequest, response: HTTPResponseWriter )
-> HTTPBodyProcessing
{
response.writeHeader(status: .ok,
headers: [ "Content-Type": "text/html" ])
response.writeBody(
"""
<h1>cows</h1>
<center><pre>\(vaca().htmlEscaped)</pre></center>
"""
)
response.done()
return .discardBody
}
extension String {
var htmlEscaped : String {
let escapeMap : [ Character : String ] = [
"<" : "<", ">": ">", "&": "&", "\"": """
]
return map { escapeMap[$0] ?? String($0) }.reduce("", +)
}
}
This is a little more difficult and shows that HTTP API only provides very basic stuff. It doesn’t deal with HTML or anything - that would be the responsibility of a higher level framework (like Kitura, Vapor, etc). For example we need to escape some characters to make the cows render as proper HTML.
Build and re-run the thing, then hit http://localhost:1337/hello and reload the page as often as you like. You should get plenty of cows delivered to your browser.

Step 4: Reinventing Google
Sure, random cows are just great and always carry a surprise. But let’s add a way to search for specific cows. Again, you’ll see that you need to do some stuff manually, which a higher level framework would usually do for you. But we can get it done w/ a little code.
In the handler we add an HTML form
,
we add helper functions to decode URL query parameters,
and we add the code to search for cows once we got a query string.
// File: Sources/sssfun/HelloHTTP.swift
import Foundation
import HTTP
import cows
func hello(request: HTTPRequest, response: HTTPResponseWriter )
-> HTTPBodyProcessing
{
let q = request[query: "q"]?.lowercased() ?? ""
let cow = q.isEmpty
? vaca() // random cow
: allCows.first(where: { $0.lowercased().contains(q) })
?? "No such cow"
response.writeHeader(status: .ok,
headers: [ "Content-Type": "text/html" ])
response.writeBody(
"""
<center>
<form action="/hello/" method="get">
Find Beef: <input name="q" placeholder="e.g. 'moon'">
<a href="/hello/">[random]</a>
</form>
<pre>\(cow.htmlEscaped)</pre>
</center>
"""
)
response.done()
return .discardBody
}
// That can be put into a separate Helpers.swift file:
extension HTTPRequest {
subscript(query q: String) -> String? {
return URLComponents(string: target)?.queryItems?
.first(where: { $0.name == q })?.value
}
}
extension String {
var htmlEscaped : String {
let escapeMap : [ Character : String ] = [
"<" : "<", ">": ">", "&": "&", "\"": """
]
return map { escapeMap[$0] ?? String($0) }.reduce("", +)
}
}
Build and re-run the thing, then hit http://localhost:1337/hello. Enter some query string, e.g. ‘moon’, ‘lator’, ‘trouble’ or ‘night’.

Very well. We built a very basic HTTP service which competes with Google on search, and also delivers pretty cows.
Bonus: Use Apache to host your HTTP endpoint
So you are happy with our cows web service and want to deploy it on production. But the 0.1.0 API server may not be quite there yet, and you really want HTTP/2, HTTPS and NTLM authentication for sure.
Fortunately the ARI hacked up an implementation of the very same Swift API which allows you to run the exact same code in the “The most popular web server on the Internet since April 1996”: Apache httpd. We are going to use an Apache module called mod_swift and an implementation of the HTTP API 0.1.0 for mod_swift.
For the following we assume that you are on macOS w/ Homebrew. (The same should work fine on Linux, just checkout the mod_swift Installation to get going, and please let us know if you run into issues).
If you don’t have Homebrew yet, get it at: brew.sh
(installation is a one liner).
Then install mod_swift, and maybe httpd
w/ HTTP/2 support and threading:
brew reinstall httpd --with-mpm-event --with-http2
brew install mod_swift
==> Installing mod_swift from modswift/mod_swift
🍺 /usr/local/Cellar/mod_swift/0.8.10: 25 files, 68.5KB, built in 5 seconds
To check whether it worked, call swift apache validate
, like so:
swift apache validate
The Swift Apache build environment looks sound.
Neat. The next step is to replace the Sources/sssfun/main.swift
,
which contains the API 0.1.0 test server w/ Apache.
Note that we keep using the HelloHTTP.swift
w/o any changes!
We also need to clean the package:
rm -rf .build Package.resolved
rm Sources/sssfun/main.swift
Create a new file ApacheMain.swift
in Sources/sssfun
:
import Foundation
import HTTP
@_cdecl("ApacheMain")
public func ApacheMain(cmd: OpaquePointer) {
let app = HTTP.apache(cmd, name: "mods_httpapi")
app.use("/hello", hello)
}
Before we can build this, we need to replace the API dependency in
Package.swift
.
Note that the module name is still the same, we just change the package
providing that HTTP module.
Replace this:
.package(url: "https://github.com/swift-server/http",
from: "0.1.0"),
with:
.package(url: "https://github.com/modswift/http.git",
.branch("implementation/mod_swift")),
Now call swift apache build
, which will create the Apache module for us:
swift apache build
Fetching https://github.com/modswift/http.git
Fetching https://github.com/AlwaysRightInstitute/cows.git
Fetching https://github.com/modswift/CApache.git
Fetching https://github.com/swift-server/http
Cloning https://github.com/modswift/CApache.git
Resolving https://github.com/modswift/CApache.git at 1.0.0
Cloning https://github.com/modswift/http.git
Resolving https://github.com/modswift/http.git at implementation/mod_swift
Cloning https://github.com/swift-server/http
Resolving https://github.com/swift-server/http at 0.1.0
Cloning https://github.com/AlwaysRightInstitute/cows.git
Resolving https://github.com/AlwaysRightInstitute/cows.git at 1.0.2
Compile Swift Module 'HTTP' (10 sources)
Compile Swift Module 'cows' (3 sources)
Compile Swift Module 'sssfun' (2 sources)
Then start Apache:
swift apache serve
Note: DocRoot /usr/local/var/www (create a 'public' dir to use an own one)
Starting Apache on port 8042/8442: // yes, a bug ;-)
GET /hello 200 295 - 1ms
GET /hello 200 654 - 0ms
GET /hello/ 200 870 - 2ms
And hit is using: http://localhost:8042/hello. Or hit it using HTTPS (self signed certificate): https://localhost:8442/hello. If you are using Chrome and open the Developer Tools, you’ll notice it is talking HTTP/2 to the server (Protocol: h2).

Summary
Looks like Server Side Swift is getting somewhere. To get going with simple endpoints there is no need anymore to download big application server packages with tons of dependencies. You can simply drop in the HTTP API package, that’s it. And if you have more complex needs: mod_swift and ApacheExpress 😬
Next: Build your own MicroFramework
In part two of the Swift Server API Series we
are going to write a small but useful
micro server framework wrapping the raw API.
Say Hello World
to MicroExpress!
Part 2: µExpress
Links
- Swift Server Working Group
- Swift Apache
- ASCII Cows
- Swiftmon/S
Contact
Hey, we love feedback! Twitter, any of those: @helje5, @ar_institute, @mod_swift, @ApacheExpress3, @noze_io. Email: me@helgehess.eu.