/ Server

Vapor 3 tutorial for beginners

Beginner's guide how to make server side Swift applications using Vapor 3 framework. Get started with your first backend project written in Swift 4.1 now!


TL;DR: you can download the Vapor 3 starter project from gitlab. 💧


Vapor Toolbox

Vapor 3 Toolbox

Vapor's command line interface provides shortcuts and assistance for common tasks.

Vapor's CLI is a really helpful tool if you are a newbie, it's available both for macOS and (ubuntu) linux, you can simply install it through brew or apt-get. 📦

# macOS
brew install vapor/tap/vapor 

# linux
eval $(curl -sL https://apt.vapor.sh)
sudo apt-get update
sudo apt-get install vapor

Now you are ready to use the vapor command. It's really convenient, you can create a new boilerplate project like this.

vapor new myProject --branch=beta
cd myProject
vapor update -y

You can also use vapor build to build your project and vapor run to execute it directly. This comes handy if you don't want to mess with makefiles or don't want to interact directly with the Swift Package Manager tool.

These are just the basics, you can deploy to the cloud and many more, but this time it will be more than enough. You can vapor --help if you want to learn more about the Vapor toolbox. Altough it's not a must have, but more than a convenience tool. 🎓


Vapor Drop

Vapor application structure

First of all let's check our newly generated project structure. There are lots of components that could be hard to understand for newcomers to Vapor.

Run

Inside the Sources you will find the run executable package. Basically this one is the beginning of everything. It'll load your application library and fires up the Vapor backend server with proper configs and environmental variables. 🏃

App

This one is where you put your actual application code. It's a library package by default what you can import inside the Run target. There are some top level functions that you have to define, these are going to be under the App namespace.

NOTE: it's not necessary to have both an App and a Run target, it' can be merged together, but in order to do that you have to modify the Package.swift file and do some other semi-advanced tricks. Let's talk about these topics in the next post.

Boot

The boot function gets called after the application starts, but before the server runs. Basically if you need to preload some data or do some initial task before your server starts, that should be located here. 👢

Config

You can put together your application's configuration inside this function. For example this is where you should register all the various services, use middlewares, set the router object, etc.

Routes

This is where you can add the actual routes for your router. But first, what is routing? If you don't know what's HTTP, please stop here and start reading about networks. 😅

Routing refers to how an application’s endpoints respond to client requests.

This is already well-explained in the expressjs docs. Let's say that routing is the subsystem that connects your code with the API endpoints. You can define these connections at this level. So if you request for /cats, your actual cat getter method will be called and you'll see lots of happy kittens. 🐱

Controllers

Controllers are code organization tools, with the help of them you can group your API endpoints, based on topics. In the sample project there is a Todo controller which is responsible of CRUD operations on Todo models. The router connects the endpoints with this controller, and the controller will use the appropriate models.

Models

Models are simple data entities. For example Todo row in the database is a model. It can have various attributes, and because of the protocol oriented paradigm you can extend models to be powerful as fck. 💪

That's the reason why you don't have to mess with JSON encoding / decoding, because models are Codable Content types, so you can just return them in the response handlers, the JSON middleware will take care of the rest.

Services

Services is a dependency injection (also called inversion of control) framework for Vapor. The services framework allows you to register, configure, and initialize anything you might need in your application.

So, this means that most of the components are written as a service in Vapor 3. The router is a service, middleware system works as a service, database connections are services, even the HTTP server engine is implemented as a service.

This is incredibly useful, because you can replace anything inside your configuration, there only a few hardcoded elements, everything is customizable. You don't need databases? That's fine, just remove the service. You got the idea, right? 😉


Vapor 3

Vapor 3 - beginner tips

Simple starter project

The project created by Vapor toolbox can be a little bit complex for beginners, that's why I stripped down all the unnecessary stuff from it. You can grab my simple Vapor 3 starter project from gitlab. You won't find any database connections, but you can play around with routes and taste the feeling of coding in Vapor. 💧

Using environments

In the Vapor template project you can see these lines.

    let sqlite: SQLiteDatabase
    if env.isRelease {
        /// Create file-based SQLite db using $SQLITE_PATH from process env
        sqlite = try SQLiteDatabase(storage: .file(path: Environment.get("SQLITE_PATH")!))
    } else {
        /// Create an in-memory SQLite database
        sqlite = try SQLiteDatabase(storage: .memory)
    }

Ok, but how the hell can I run the app in production mode with a proper SQLITE_PATH path environment variable? It's actually pretty simple.

export SQLITE_PATH=~; swift run Run --env production

This will set the path to your home directory and the app will run in production mode instead of development mode. You can always use environment variables to configure your application at runtime. Vapor has support for storing environment variables in config json files, but sadly that feature was not working for me when I tried, so I had to stick with plain old bash commands. 😎

Change port number and host name

There should be a config.json for this purpose, but as I mentioned before that was not working for me at all. Althought it's really simple to change the listening port number without a configuration file. You don't even have to write a single line of code in order to do it. Just append the serve --port XXXX parameter to your run command.

swift run Run serve --port 8081
# you can also set the host name
swift run Run serve --hostname api.example.com --port 8081

This method I demonstrated above will also work if you register your own EngineServerConfig service with a default port number.

let myService = EngineServerConfig.default(port: 8001)
services.register(myService)

Static File server with custom path

This one is pretty easy, you just have to setup your static file server middlware inside the configuration file. I'm using a custom folder inside the user's home directory for storing public files, here is the code snippet.

var staticFileUrl = FileManager.default.homeDirectoryForCurrentUser
staticFileUrl.appendPathComponent("vapor-starter-project")
middlewares.use(FileMiddleware(publicDirectory: staticFileUrl.path))

Router parameters

If you want dynamic path parameters in your route, you can use the following code snippet in order to make them work. Straightforward, but when I was searching for this, the docs were not updated so I had to look Vapor's source. 🤓

router.get("hello", String.parameter) { req -> String in
    let name = try req.parameter(String.self)
    return "Hello, \(name.capitalized)!"
}

Codable routes with custom encoder service

This was one of the reasons why I abandoned Kitura, because there is no option to customize the encoder service. With Vapor, this feature is supported out of the box, through services. What a relief... haha 😄

let jsonEncoder = JSONEncoder()
jsonEncoder.dateEncodingStrategy = .secondsSince1970
var contentConfig = ContentConfig.default()
contentConfig.use(encoder: jsonEncoder, for: .json)
services.register(contentConfig)

Last time you told me Kitura is the one... 🤔

Ok, first of all I'm just a guy who wants to share all the experience, to make things more easy for newcomer developers. Maybe I'm wrong with something, maybe I'm right (but usually it's the last one). 😛 With Kitura I had a bad mistake...

I was using Kitura 2 in my recent backend project for my app, but eventually it turned out that it's not the right choise for me. Maybe it's for you, but you know:

you have to find the right tool that helps you solve the problem

Kitura is evolving really slowly and I don't like that. Even simple things are pain in the ass to do with it. It's sponsored by IBM, but still Vapor is updated more often, has a better design - with better code quality - and it feels like that's how someone should make a server side swift framework for "real world people". 👍

End of the story:

my new favorite Swift backend framework right now is oficially Vapor 3. 💧

It's already powered by Swift-NIO, so it's blazing fast, async, modular and I'm really glad that I started only with the 3rd major version of Vapor, because now I only see a mature web application framework. Good job team Vapor! 👏👏👏


External sources

Vapor 3 tutorial for beginners
Share this