DEV Community

Cover image for A Vapor Guide-Setup and Deployment with Heroku and Ubuntu
leogdion
leogdion

Posted on

A Vapor Guide-Setup and Deployment with Heroku and Ubuntu

It can be a real challenge picking the right backendfor your mobile app. For instance, Swift developers don’t have anything withoutneeding to run on another language. Therefore, I decided to do a review of Vapor and whether it is really a right choice.

What I found was that for Swift developers, Vapor is a really great choice and fairly easy to work with. In addition, Vapor has an effective community and a great API if your development team is dedicated toSwift. Therefore, rather than having your team hop from Xcode and Swift to VSCode or Firebase etc…

In this article I am going to cover how to get started, specifically:

  • Setup your Mac for Vapor Development
  • Convert your project to use PostgreSQL Database
  • Modify your project for Heroku Deployment
  • Prepare an Ubuntu Server for Vapor
  • Modify your project for Ubuntu deployment

As a result, you’ll have fully working backend with Postgres ready and easily deployable.

Setting Up Your Mac For Vapor Development

In order to start developing a Vapor application, we’ll need to setup our Mac. According to the Vapor docs,** you need Xcode 9.3 or greater**. However for this tutorial, I used the latest version of macOS and Xcode at the time of writing:

Prerequisites For Vapor

I also have installed Homebrew. To install Homebrew, open your terminal application and type:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
Enter fullscreen mode Exit fullscreen mode

For more details you can go to the official Homebrew home page.

Installing Vapor

Once you have Homebrew installed we can go ahead and install Vapor on your Mac. Before we proceed, we’ll need to add the Vapor repository to Homebrew.

  1. Firstly, add the Vapor repository using the tap command: brew tap vapor/tap
  2. Next, install the Vapor toolkit: brew install vapor/tap/vapor As a result of installing the Vapor toolkit, we’ll have access to the Vapor CLI tool. Therefore, you can get access to the list of commands available by typing: vapor --help

For example, with the Vapor CLI tool, you can:

  • Build the application
  • Run the application
  • setup for Heroku deployment later
  • Create the Xcode project and of course…
  • Create the new Swift package

Creating Your First Vapor Project

Therefore, we can create the Switch package which will contain the dependencies needed to run our server application with the command:

Additionally, we can create an Xcode project from our Vapor application Swift package by typing:

vapor xcode
Enter fullscreen mode Exit fullscreen mode

As a result, we can open the newly created Xcode project.

Switching the target to Run


Switching the target to Run

Switching the target to Run


Building and running the Run target

After that, switch the target in the top left to Run and press the play button to run the application. Consequently, you should see a log message at the bottom stating that a http server is started at port 8080.

As a result, go to the terminal and run the command:

curl http://localhost:8080/hello
Enter fullscreen mode Exit fullscreen mode

Therefore, you should receive the message:

%Hello, world!%
Enter fullscreen mode Exit fullscreen mode

In conclusion, we’ve successfully used the provided Vapor templates with the new and xcode commands to create the Swift package and build the application in Xcode. However the application only used SQLite for our database storage. Instead, let’s switch to using a more common database: PostgreSQL.

Setting Up For PostgreSQL

PostgreSQL has been my favorite relational SQL based database server. As a result, we are going to look at converting the current Vapor project to use PostgreSQL rather than the default SQLite.
Therefore, make sure you already have PostgreSQL installed. Consequently, if you don’t you can use Homebrew to install PostgreSQL with the following command:

brew install postgresql
Enter fullscreen mode Exit fullscreen mode

Further, to run the server from the Terminal:

pg_ctl -D /usr/local/var/postgres start
Enter fullscreen mode Exit fullscreen mode

Similarly, you can stop the server from the Terminal with:

pg_ctl -D /usr/local/var/postgres stop
Enter fullscreen mode Exit fullscreen mode

On the other hand, if you wish to run PostgreSQL as background service with launchd you can use the brew services command:

brew services start postgresql
Enter fullscreen mode Exit fullscreen mode

Most importantly, once PostgreSQL is started, we’ll need to add the database and user for our application to use.

Setup Database and User

As a result of switching the PostgreSQL, we’ll need to add the user and database our Vapor application will use. Therefore, let’s run the PostgreSQL terminal-based front-end by going to the Terminal and typing:

psql -d postgres
Enter fullscreen mode Exit fullscreen mode

After that, at the psql prompt, create the database with:

create database app_collection;
Enter fullscreen mode Exit fullscreen mode

Likewise, we need to create a user. To clarify, we are creating a user with no
password by typing:

create user app_collection;
Enter fullscreen mode Exit fullscreen mode

Subsequently grant that user the privileges it needs with:

grant all privileges on database app_collection to app_collection;
Enter fullscreen mode Exit fullscreen mode


As a result, our database should be ready for access. However, we’ll need to update the code and specifically the Swift package to use PostgreSQL.

Updating the Code For PostgreSQL

Update Package.swift with PostgreSQL

In order to use PostgreSQL in our backend application, we’ll need to update the Package.swift file to use PostgreSQL as opposed to SQLite.

Therefore, open the Package.swift and update dependencies by changing the line from:

.package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0")
Enter fullscreen mode Exit fullscreen mode

to the line:

.package(url: "https://github.com/vapor/fluent-postgresql.git", from: "1.0.0")
Enter fullscreen mode Exit fullscreen mode

To clarify, we’ll be using Fluent, Vapor’s ORM Framework, along with PostgreSQL. In other words, we are swapping the Fluent driver for SQLite with the one for PostgreSQL. Likewise, if you can read more details on the different drivers and how their setup works in Fluent’s documentation.

Consequently, we need to also update the App targets to use the new
FluentPostgreSQL dependency. Therefore change the line:

.target(name: "App", dependencies: ["FluentSQLite", "Vapor"]),
Enter fullscreen mode Exit fullscreen mode

to the line:

.target(name: "App", dependencies: ["FluentSQLite", "Vapor"]),
Enter fullscreen mode Exit fullscreen mode

In addition to updating the Package.swift file, we need to actually update our Xcode project to use the new dependencies.

Update Packages

Therefore, in order to fetch and update the package with the new dependencies and update the Xcode project file, we can run the xcode command again with:

vapor xcode
Enter fullscreen mode Exit fullscreen mode

As a result, vapor has fetched the Fluent PostgreSQL driver and updated the Xcode project. Therefore, we can move forward by updating the code.

Update the Code

Consequently, open the update Xcode project. Since we have removed the Fluent SQLite driver, we should no longer be able to build the project. As a result we need update our models to use PostgreSQL as well as the Provider, Database, and Migration used in our configuration. Therefore, open the configure.swift.

Firstly, let’s update the import at the top of the file from:

import FluentSQLite
Enter fullscreen mode Exit fullscreen mode

to:

import FluentPostgreSQL
Enter fullscreen mode Exit fullscreen mode

Secondly, update the provider by updating the line:

try services.register(FluentSQLiteProvider())
Enter fullscreen mode Exit fullscreen mode

to

try services.register(FluentPostgreSQLProvider())
Enter fullscreen mode Exit fullscreen mode

Further, update the database used by changing the line from:

// Configure a SQLite database
let sqlite = try SQLiteDatabase(storage: .memory)

// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.add(database: sqlite, as: .sqlite)
services.register(databases)
Enter fullscreen mode Exit fullscreen mode

to:

// Configure a PostgreSQL database
let postgreSQLConfig = PostgreSQLDatabaseConfig(hostname: "localhost", username: "app_collection")
let postgreSQL = PostgreSQLDatabase(config: postgreSQLConfig)

// Register the configured PostreSQL database to the database config.
var databases = DatabasesConfig()
databases.add(database: postgreSQL, as: .psql)
services.register(databases)
Enter fullscreen mode Exit fullscreen mode

In other words, we are replacing the memory based SQLite database with the PostgreSQL local database configured with the user name app_collection.

To clarify, the PostgreSQLDatabaseConfig initializer can multiple parameters in this case we are using the default port number and the default database name which matches with user name, in this case app_collection.

Lastly, in the configuration.swift, update the migration for the Todo model. To clarify, migrations are used to create and update the tables and relationships used by Fluent. Therefore, in this case we just need to change the parameter in this line:

migrations.add(model: Todo.self, database: .sqlite)
Enter fullscreen mode Exit fullscreen mode

to this line:

migrations.add(model: Todo.self, database: .psql)
Enter fullscreen mode Exit fullscreen mode

In the same vein, we need to change the subclasses used by our model by opening Todo.swift. After that change the import statement at the top again and then update model subclass from:

final class Todo: SQLiteModel {
Enter fullscreen mode Exit fullscreen mode

to

final class Todo: PostgreSQLModel {
Enter fullscreen mode Exit fullscreen mode

As a result, we can go ahead and run the Vapor application.

Run the Code

To sum up, now that have setup the PostgreSQL database on our machine, update the Swift package dependencies, and updated the code accordingly; we can build and run the Vapor application. Therefore, in Xcode, run the Run target again. After that, let’s go to the Terminal app to test the application. For example we can run again:

curl http://localhost:8080/hello
Enter fullscreen mode Exit fullscreen mode

However, we can also add a Todo item by running:

curl -X POST -H "Content-Type: application/json" -d '{"title": "example"}' http://localhost:8080/todos
Enter fullscreen mode Exit fullscreen mode

Consequently, we can verify the Todo item was added by calling:

curl http://localhost:8080/todos
Enter fullscreen mode Exit fullscreen mode

As a result, we get the JSON response:

[{"id":1,"title":"example"}]
Enter fullscreen mode Exit fullscreen mode

Subsequently, let’s deploy this to the cloud. Firstly, we’ll look into Heroku.

Deploying Vapor to Heroku

In addition to setting up our application for PostgreSQL, we will move forward with deploying it Heroku. Above all, Heroku offers the easiest way to get up and running. In addition, it offers enough plugins and services on its free tier that we can simply get started. Therefore, let’s begin by setting up our project for Heroku.

Preparing Vapor Package For Heroku

Importantly, make sure you have an account setup at Heroku. After that, go to the Terminal again, and in the project directory, we can simply run the command:

vapor heroku init
Enter fullscreen mode Exit fullscreen mode

As a result, of running the command, we have created a Heroku application and added:

  • Vapor buildpack for Heroku deployment
  • git remote for Heroku
  • Procfile needed for Heroku to run the application

The Procfile, which you read about in the Procfile documentation, contains the following line:

web: Run serve --env production --port $PORT --hostname 0.0.0.0
Enter fullscreen mode Exit fullscreen mode

Moreover when you run the command, you do not need the supply any customizations. However we will need to add PostgreSQL to our Heroku application.

Adding PostgreSQL To Heroku Instance

Similarly, as we have setup the Vapor application in Heroku via the Terminal, we need to add PostgreSQL to our application. Therefore, we can add the Heroku repo and install the heroku command with Homebrew by running:

brew tap heroku/brew && brew install heroku
Enter fullscreen mode Exit fullscreen mode

Consequently, we can add PostgreSQL by running the command:

heroku addons:create heroku-postgresql:hobby-dev
Enter fullscreen mode Exit fullscreen mode

As a result of running this command, we’ve added a free hobby level instance of PostgreSQL to our application.

Consequently, Heroku will add an environment variable for the database called DATABASE_URL. Therefore, we'll need to update our code to use this.

Update Code For Heroku PostgreSQL

Since we have added PostgreSQL to our Heroku instance, we need to update our code to use the environment the variable for the new database url,
DATABASE_URL. Therefore, open the configure.swift. After that, change the lines where we setup the database from:

let postgreSQLConfig = PostgreSQLDatabaseConfig(hostname: "localhost", username: "app_collection")
Enter fullscreen mode Exit fullscreen mode

to:

let postgreSQLConfig : PostgreSQLDatabaseConfig

if let url = Environment.get("DATABASE_URL") {
  postgreSQLConfig = PostgreSQLDatabaseConfig(url: url)!
} else {
  postgreSQLConfig = PostgreSQLDatabaseConfig(hostname: "localhost", username: "app_collection")
}
Enter fullscreen mode Exit fullscreen mode

As a result of changing the code, we will be checking if there is an environment variable called DATABASE_URL. In other words, we are using the static method Environment.get(_:) to fetch the string value if it exists. Secondly, if the variable doesn't exist we assume this is being run locally and it uses the default settings. To clarify, we are using an unconditional unwrapping since we'd rather it trigger a runtime error than attempt to use the default settings. Lastly, let's move forward with deploying our application.

Deploying Vapor Package to Heroku

Similarly, we can deploy the application to Heroku in the terminal with one of two commands. Firstly, you can push directly with git using the command:

git push heroku master
Enter fullscreen mode Exit fullscreen mode

Secondly, you can push with the Vapor CLI command:

vapor heroku push
Enter fullscreen mode Exit fullscreen mode

In short, either command will:

  • Firstly, push your code to the Heroku
  • Secondly, build the Application
  • Lastly, run the application (based on the command in the Procfile)

Therefore, if our application instance is called *damp-spire-56788 *, we should be call our application with curl:

> curl http://damp-spire-56788.herokuapp.com/hello
Hello, world!%
> curl -X POST -H "Content-Type: application/json" -d '{"title": "example"}' http://damp-spire-56788.herokuapp.com/todos
{"id":1,"title":"example"}%
> curl http://damp-spire-56788.herokuapp.com/todos 
[{"id":1,"title":"example"}]%
Enter fullscreen mode Exit fullscreen mode

In short, we can setup our app for Heroku by

  • Firstly, using the Vapor CLI to setup the instance, build pack, Procfile, and git remote
  • Secondly, using setting up a PostgreSQL instance in Heroku
  • After that, updating the code to use the environment variable for the database url to access the Heroku PostgreSQL instance
  • Lastly, pushing the code to Heroku and testing the application

Similarly, now that we know how to deploy to Heroku, let’s look at what we need to do to setup our own Ubuntu server for hosting our Vapor application.

Setting Up Vapor For Ubuntu

In the same way, I showed how to setup our Vapor application for Heroku; we are going to look at what is needed to setup an Ubuntu server. In this case, we are using the latest LTS version of Ubuntu which is Bionic Beaver 18.04. However, according to the documentation, Vapor does support version 14.04 (Trusty Tahr) and up including 18.10 (Cosmic
Cuttlefish).

Installing Prerequisites

With a new server setup, we need to install several package. However, first we need add Vapor’s APT repo. Therefore, on the server (as root), run the following command:

eval "$(curl -sL https://apt.vapor.sh)"
Enter fullscreen mode Exit fullscreen mode

After that, we can proceed with installing the software we’ll be using to setup our server:

  • git — for fetching the code of our application
  • nginx — our http server
  • postgresql — our database server
  • supervisor — which is used to monitor and execute the application
  • swift and vapor — which is used to build and run the application

Therefore, we can install the application with:

apt install git supervisor postgresql swift vapor nginx
Enter fullscreen mode Exit fullscreen mode

Setting Up the PostgreSQL Database

Similarly to how we use the DATABASE_URL on Heroku, we are going to setup environment variables in Ubuntu.

Updating the Code

Therefore, open up Xcode and in our project go back to configure.swift. After that, between the import statements but before the configure function add a struct to store the defaults for PostgreSQL:

public struct PostgresDefaults {
  public static let hostname = "localhost"
  public static let username = "app_collection"
  public static let port = 5432
}
Enter fullscreen mode Exit fullscreen mode

To clarify, this struct stores the default host name, user name, and port, we use for PostgreSQL configuration

Moreover change the line in the else statement from:

postgreSQLConfig = PostgreSQLDatabaseConfig(hostname: "localhost", username: "app_collection")
Enter fullscreen mode Exit fullscreen mode

to:

let hostname = Environment.get("DATABASE_HOSTNAME") ?? PostgresDefaults.hostname
let username = Environment.get("DATABASE_USERNAME") ?? PostgresDefaults.username
let database = Environment.get("DATABASE_DATABASE")
let password = Environment.get("DATABASE_PASSWORD")

let port : Int

if let portString = Environment.get("DATABASE_PORT") {
  port = Int(portString) ?? PostgresDefaults.port
} else {
  port = PostgresDefaults.port
}

postgreSQLConfig = PostgreSQLDatabaseConfig(hostname: hostname, port: port, username: username, database: database, password: password, transport: .cleartext)
Enter fullscreen mode Exit fullscreen mode

To sum up, this code fetches from the environment variables for the host name, user name, database, password, and port. Consequently, if any value is invalid or does not exist it falls back to the default setup in the PostgresDefaults struct or null and let's the PostgreSQLDatabaseConfig use its default value. Likewise, this code will still work on Heroku (with the DATABASE_URL) and on localhost with the defaults setup.

However, before we setup the environment variables for our application, we need to setup the database.

Add Database and User

Therefore, as root, sudo in as the postgres user and run psql:

sudo -u postgres psql
Enter fullscreen mode Exit fullscreen mode

After that, run the following sql statements:

create database app_collection;
create user app_collection with encrypted password 'app_collection_pw';
grant all privileges on database app_collection to app_collection;
Enter fullscreen mode Exit fullscreen mode

To clarify, we are creating:

  • database named app_collection
  • user named app_collection with a password app_collection_pw
  • grant the user all privileges on the database, we’ve created

As a result of setting up the database, we can begin to setup supervisor.

Setting Up Supervisor

Since we are using supervisor, we are going to be setting up a Linux user to run the actual application.

Adding a Linux User

As root user again, run the following command:

adduser app_collection
Enter fullscreen mode Exit fullscreen mode

To clarify, we are setting up user called app_collection which does not need
any other metadata (Full Name, Room Number, etc...). Therefore having setup the
user, let's pull and build the code.

Pulling Repo

Having setup the user in Linux, go ahead and super user in as the new user:

su -l app_collection
Enter fullscreen mode Exit fullscreen mode

After that, as the new user, clone the repo:

git clone https://github.com/brightdigit/swift-vapor-app-collection-sample.git app
Enter fullscreen mode Exit fullscreen mode

Subsequently, go to the directory and build the application:

cd app
vapor build
Enter fullscreen mode Exit fullscreen mode

In short, we have cloned the code from our repo and built the application. As a
result, we can move forward with configuring supervisor.

Configuring Supervisor

While our application will serve our Rest API and nginx will proxy that
connection, supervisor will monitor and control the actual Vapor application process. Therefore, let’s create a configuration for our Vapor application. In other words, as root, create a file here:

/etc/supervisor/conf.d/app_collection.conf
Enter fullscreen mode Exit fullscreen mode

After that, paste this text into the configuration file:

[program:app_collection]
environment =
        DATABASE_PASSWORD="app_collection_pw",
command=vapor run --port=3000 --env=production
directory=/home/app_collection/app/ 
autorestart=true
user=app_collection             
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log
Enter fullscreen mode Exit fullscreen mode

To clarify:

  • the name of this process will be program:app_collection
  • we setup an environment variable for the database password
  • the command is to run Vapor from our code’s directory
  • auto restart if the app fails
  • run as our new user
  • lastly, we have setup a path for the application output and errors

Subsequently, we can go ahead and update supervisor by with the following
commands:

supervisorctl reread
supervisorctl update
Enter fullscreen mode Exit fullscreen mode

After that, you can test if supervisor was successful by calling the application from port 3000:

curl http://localhost:3000/hello
Enter fullscreen mode Exit fullscreen mode

In short, we’ve setup our Vapor application in supervisor to run on port 3000 with a database password setup as an environment variable. As a result, let’s setup nginx to proxy our application.

Setting Up Nginx

For our http server, we’ll be using nginx. Most importantly, we’ll be setting up a host name for our application. Therefore, we are using the nginxconfig.io to setup the config files. Consequently, you can download the specific nginx configuration
we are using from here. In addition, feel free to change host name before downloading.

After that, unzip the file:

unzip nginxconfig.io-app_collection.local.zip
Enter fullscreen mode Exit fullscreen mode

Firstly, we can, as root, move the files to their proper locations:

mv nginx.conf /etc/nginx/nginx.conf
mv sites-available/app_collection.local.conf /etc/nginx/sites-available
ln -s /etc/nginx/sites-available/app_collection.local.conf /etc/nginx/sites-enabled
mv nginxconfig.io /etc/nginx/
Enter fullscreen mode Exit fullscreen mode

Secondly, now that we have moved the configuration files over, restart nginx:

service nginx restart
Enter fullscreen mode Exit fullscreen mode

Most importantly, if you are using a .local host name, make sure to setup the host name on your network. That is to say, make sure the host name is accessible from your local development computer. For instance, on macOS or Linux, you would need to edit the /etc/hosts file. On the other hand, if this is to be a public host name, verify you have updated your dns accordingly.

After you have verified the dns or hosts file is updated, we can proceed if testing the application out:

> curl http://app_collection.local/hello
Hello, world!%
> curl -X POST -H "Content-Type: application/json" -d '{"title": "example"}' http://app_collection.local/todos
{"id":1,"title":"example"}%
> curl http://app_collection.local/todos 
[{"id":1,"title":"example"}]%
Enter fullscreen mode Exit fullscreen mode

In short, we’ve successfully setup the application on an Ubuntu server.

And There’s More…

In conclusion, we’ve successfully:

  • setup our Mac for developing server-side Swift application using Vapor
  • converted the beginner template to use PostgreSQL from SQLite
  • updated the code to use environment variables for different server environments
  • setup and deployed the application to Heroku
  • setup and deployed to an Ubuntu server

Vapor will will reduce friction in your Swift development and has effective community support. To get a full guide and video mini-course, fill out this form and let me know what other Vapor topics you are interested in learning more about such as:

  • setting up complicated database models and relationships
  • how to configure advanced Rest API routes
  • writing complex queries using Fluent ORM and Futures in Swift
  • setting up for authentication

I look forward to hearing your feedback and I’ll keep you posted on updates.

Top comments (2)

Collapse
 
zaindevstudio profile image
zaindevstudio

I have followed all steps, everything goes well. I have installed Heroku Postgres. but when I try to push code to Heroku, it gives me error "No default language could be detected for this app."
I have attached screen shot of error.

Collapse
 
leogdion profile image
leogdion

Did you add the build pack for your application?
If you run vapor heroku init it should set you up. The other file you need is a Procfile which tell Heroku what to do. You read more about that here:
devcenter.heroku.com/articles/proc...