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
# 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
.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:
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
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
import DotEnv
Now we can update our hardcoded configuration values:
Sources/BookshelfAPI/main.swift<
Replace:
let connectionProperties = ConnectionProperties(
host: "localhost",
port: 5984,
secured: false,
username: "rob",
password: "123456"
)
let databaseName = "bookshelf_db"
with:
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:
Kitura.addHTTPServer(onPort: 8090, with: router)
with:
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:
$ ./.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