Kitura tutorial part 4: Configuration
Now that we have created a working endpoint in part 3, lets look at the configuration. At the moment, it’s all hard coded! We can do better than that.
If we’re ever going to run this application in the cloud, then it’s advantageous to use environment variables for configuration as per the 12 Factor App guidelines as these vary per deploy.
Environment variable configuration works especially well in containerised / VM applications, but can be a little more problematic when developing on your local computer as different apps may have the same envrionment variable names. To solve this, .env
files (pronounced “dotenv”) have become popular for storing configuration, especially in development and test environments.
We will use a .env
file locally, but not store it in version control.
.env
1 2 3 4 5 6 7 8 9 10 |
# CouchDB configuration DB_HOSTNAME="localhost" DB_PORT=5984 DB_HTTPS=false DB_USERNAME=rob DB_PASSWORD=123456 DB_NAME=bookshelf_db # App configuration APP_PORT=8090 |
This file contains the 6 confiugration values we need for our CouchDB configuration along with the port we wish to run our API on as we may not always want to use port 8090.
.env
files are only to be used in development, so we don’t need it in our git repository. Hence the first thing we do is add it to .gitignore:
.gitignore
1 2 3 |
.build/* Packages/* .env |
Adding SwiftDotEnv<
To read our .env
file, we are going to use the SwiftDotEnv package. This library will read our .env
file and put the variables into the environment for us. It also provides some convenience methods for reading environment variables.
It is used like this:
1 2 3 4 5 6 7 |
import DotEnv let env = DotEnv() let someString = env.get("SOME_STRING") ?? "example" let someNumber = env.getInt("SOME_NUMBER") ?? 123 let someFlag = env.getBool("SOME_BOOLEAN") ?? true |
As you can see, DotEnv
provides three get methods:
get()
returns aString?
getInt()
returns anInt?
getBool()
returns aBool?
where case-insensitive"true"
,"yes"
and"1"
evaluate totrue
We then can use the nil coalescing operator (??
) to unwrap with a default if the environment variable doesn’t exist.
To install it, we add its GitHub URL to the dependencies
array in our Package.swift
file:
Package.swift
1 2 3 4 5 6 7 8 9 10 11 |
import PackageDescription let package = Package( name: "BookshelfAPI", dependencies: [ .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 0, minor: 25), .Package(url: "https://github.com/IBM-Swift/HeliumLogger.git", majorVersion: 0, minor: 14), .Package(url: "https://github.com/IBM-Swift/Kitura-CouchDB.git", majorVersion: 0, minor: 25), .Package(url: "https://github.com/SwiftOnTheServer/SwiftDotEnv.git", majorVersion: 0), ] ) |
A quick run of make
will download the library into our Packages
directory and compile it.
We can now update main.swift
to take advantage of our new configuration system.
Firstly, we import the DotEnv module:
Sources/BookshelfAPI/main.swift
1 |
import DotEnv |
Now we can update our hardcoded configuration values:
Sources/BookshelfAPI/main.swift<
Replace:
1 2 3 4 5 6 7 8 |
let connectionProperties = ConnectionProperties( host: "localhost", port: 5984, secured: false, username: "rob", password: "123456" ) let databaseName = "bookshelf_db" |
with:
1 2 3 4 5 6 7 8 9 10 |
let env = DotEnv() let connectionProperties = ConnectionProperties( host: env.get("DB_HOST") ?? "localhost", port: Int16(env.getAsInt("DB_PORT") ?? 5984), secured: env.getAsBool("DB_HTTPS") ?? false, username: env.get("DB_USERNAME") ?? "rob", password: env.get("DB_PASSWORD") ?? "123456" ) let databaseName = env.get("DB_NAME") ?? "bookshelf_db" |
We have now replaced all our hardcoded values with the value from the environment. If the environment variable doesn’t exist then we provide defaults so that the ??
operator can unwrap the Optional from the get
methods.
We also need to update the port that Kitura will serve our API on:
Sources/BookshelfAPI/main.swift
Replace:
1 |
Kitura.addHTTPServer(onPort: 8090, with: router) |
with:
1 2 |
let port = env.getAsInt("APP_PORT") ?? 8090 Kitura.addHTTPServer(onPort: port, with: router) |
All done
With those changes, you can now make
the app and run it. Nothing should change. However if you edit .env
and change, say APP_PORT
to 8091 then when you make and run you should see that it’s now listening on port 8091:
1 2 3 4 5 6 |
$ ./.build/debug/BookshelfAPI VERBOSE: init() Router.swift line 57 - Router initialized INFO: BookshelfAPI main.swift line 41 - Starting server VERBOSE: run() Kitura.swift line 44 - Staring Kitura framework... VERBOSE: run() Kitura.swift line 46 - Starting an HTTP Server on port 8091... INFO: listen(socket:port:) HTTPServer.swift line 132 - Listening on port 8091 |
Let’s now look at adding some more end points in part 5.
Tutorial navigation
- Part 1: Getting Started with Kitura
- Part 2: CouchDB
- Part 3: List books
- Part 4: Configuration
- Part 5: Hypermedia
- Part 6: Adding a book
GitHub repository: kitura_bookshelfapi