Using Vapor and Fluent to create a REST API

Swift is awesome. Yes, it’s mature (now with 5.0 we have ABI stability, hooray!). You have the power of OOP, POP, functional and imperative programming in your hands.

You can do almost anything in Swift nowadays. If you ever thought of being a full stack developer with knowing both backend and frontend, then this article is for you.
The most known web frameworks written in Swift are Kitura and Vapor.
Vapor is now at version 3 (released in May, 2018), is open source and you can easily create your REST API, web application or your awesome website.

In this tutorial you will learn:

  • how to get started with Vapor
  • create your first REST API
  • how to use Fluent ORM Framework
  • how to transform 1:M and M:M db relationships to parent-child or siblings relationships in Fluent
  • apply what you learn in a real scenario example

If you want to skip this part, the whole project is found on GitHub:


For this tutorial you will need:

  • Xcode 10.2
  • Knowledge of Swift
  • Basic knowledge of REST API
  • Some knowledge of Swift Package Manager

Getting started

First, you need to install Xcode from Mac App Store.
You can use brew to install Vapor Toolbox. This is useful so we can run command line tasks for common operations.

/usr/bin/ruby -e "$(curl -fsSL"brew tap vapor/tap
brew install vapor/tap/vapor

You are ready to go!

Football Gather — iOS App Example

FootballGather is a demo project for friends to get together and play football matches as quick as possible.
You can imagine the client app by looking at this mockups (created with Balsamiq):

FootballGather sample iOS App — Mockups created with Balsamiq


  • Persist players
  • Ability to add players
  • Set countdown timer for matches
  • Use the application in offline mode
  • Persist players

Database Structure

Let’s use a database schema like in the image below:

In this way we can exemplify the 1:M relationship between users and gathers, where one user can create multiple gathers and M:M Player to Gather, where a gather can have multiple players and a player can play in multiple gathers.

List of controllers

If we look at the iOS app, we will create the following controllers:


  • POST /api/users/login — Login functionality for users
  • POST /api/users — Registers a new user
  • GET /api/users — Get the list of users
  • GET /api/users/{userId} — Get user by its id
  • DELETE /api/users — Get the list of users


  • GET /api/players — Gets the list of players
  • GET /api/players/{playerId} — Gets the player by its id
  • GET /api/players/{playerId}/gathers — Gets the list of gathers for the player
  • POST /api/players — Adds a new player
  • DELETE /api/players/{playerId} — Deletes a player with a given id
  • PUT /api/players/{playerId} — Updates a player by its id


  • GET /api/gathers — Gets the list of gathers
  • GET /api/gathers/{gatherId} — Gets the gather by its id
  • GET /api/gathers/{gatherId}/players — Gets the list of players in the gather specified by id
  • POST /api/gathers/{gatherId}/players/{playerId} — Adds a player to the gather
  • POST /api/gathers — Adds a new gather
  • DELETE /api/gathers/{gatherId} — Deletes a gather with a given id
  • PUT /api/gathers/{gatherId} — Updates a gather by its id

App Structure

Open the Xcode project that you created in previous section.


vapor xcode -y

This may take a while.

Here are the generated files:
├── Public
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ ├── Models
│ │ ├── boot.swift
│ │ ├── configure.swift
│ │ └── routes.swift
│ └── Run
│ └── main.swift
├── Tests
│ └── AppTests
└── Package.swift

What you will be touching in this project:

This is the manifest of the project and defines all dependencies and the targets of our app.
I am using Vapor 3.3.0. You can change Package.swift as below:.package(url: “", from: “3.3.0”)

All the resources that you want to make them public, such as images.

Here you can see two separate modules: App and Run.
You usually have to put all of your developed code inside “App”. The Run folder contains the main.swift file.

Add here your Fluent models. In our app: User, Player, Gather.

The controller is where you write the logic of your REST API, such as CRUD operations.
Similar with iOS ViewControllers, but instead they handle the requests and manage the models.

Used to find the appropriate response for an incoming request.

Called before app is initialised. Register router, middlewares, database and model migrations.

Implementing the UserController

Before starting to implement our user controller, remove the generated Todo related code:
TodoController.swift from controllers folder.
Todo.swift from Models.
Line migrations.add(model: Todo.self, database: .sqlite)) from configure.swift
All that is found in routes function from routes.swift.

A user will be defined by a username and a password. The primary key will be of type UUID representing a unique String.
Create a new file, User.swift and add it to the Models folder. Add in the file a class called User.

Make it comply to the following protocols:

  • Codable: Map the parameters of the service to the actual class parameters.
  • SQLiteUUIDModel: Convenience helper protocol to make the Model as an
  • SQLite Model class with a UUID as primary key. Used for compilation safety for referring to properties.
  • Content: Used to easy decode the information with Vapor
  • Migration: Tells Fluent how to configure the database.
  • Parameter: Used for requests with parameters, such as GET /users/{userId}.

Now you have your User model. Let’s create the UserController.

Create a new struct called UserController inside Controllers folder. Make it comply to RouteCollection protocol.
Leave the boot function for now.

Next we are going to add the CRUD operations for Users.

GET all users

func getHandler(_ req: Request) throws -> Future<User> {
return try

This will extract the user id from the request and query the database to return the User.


func createHandler(_ req: Request, user: User) throws -> Future<Response> {
return req).map { user in
var httpResponse = HTTPResponse()
httpResponse.status = .created
if let userId = {
let location = req.http.url.path + "/" + userId
httpResponse.headers.replaceOrAdd(name: "Location", value: location)
let response = Response(http: httpResponse, using: req)
return response

We are going to use save function that returns a user object. Following REST API standard, we extract the created UUID of the user and return as part of the Location header of the response.


func deleteHandler(_ req: Request) throws -> Future<HTTPStatus> {
return try HTTPStatus.self) { user in
return user.delete(on: req).transform(to: .noContent)

First we extract the user id from the request parameters. We perform delete function on the user and return a HTTPStatus associated with no content.

Implementing PlayerController

PlayerController follows the same pattern as UserController. The extra function in this case consists of update part.

func updateHandler(_ req: Request) throws -> Future<HTTPStatus> {
return try flatMap(to: HTTPStatus.self,, req.content.decode(Player.self)) { player, updatedPlayer in
player.age = updatedPlayer.age =
player.preferredPosition = updatedPlayer.preferredPosition
player.favouriteTeam = updatedPlayer.favouriteTeam
player.skill = updatedPlayer.skill
return req).transform(to: .noContent)

If we look at this function, we first extract the player id for the player that we want to perform an update.
Next, we extract all of the properties and map them to a Player object. We perform the update and as you might guessed it we call save method.

Register functions

func boot(router: Router) throws {
let playerRoute = router.grouped("api", "players")
playerRoute.get(use: getAllHandler)
playerRoute.get(Player.parameter, use: getHandler), use: createHandler)
playerRoute.delete(Player.parameter, use: deleteHandler)
playerRoute.put(Player.parameter, use: updateHandler)
playerRoute.get(Player.parameter, "gathers", use: getGathersHandler)

For GatherController we stick to the same pattern as for UserController.

1:M and M:M relationships

In order to implement a relationship between two model classes we will have to create a Pivot class.

final class PlayerGatherPivot: SQLiteUUIDPivot {
var id: UUID?
var playerId: Player.ID
var gatherId: Gather.ID
var team: String
typealias Left = Player
typealias Right = Gather
static var leftIDKey: LeftIDKey = \PlayerGatherPivot.playerId
static var rightIDKey: RightIDKey = \PlayerGatherPivot.gatherId
init(playerId: Player.ID, gatherId: Gather.ID, team: String) {
self.playerId = playerId
self.gatherId = gatherId = team
}// Player.swift
extension Player {
var gathers: Siblings<Player, Gather, PlayerGatherPivot> {
return siblings()
// Gather.swift
extension Gather {
var players: Siblings<Gather, Player, PlayerGatherPivot> {
return siblings()

The implementation from above describes the M:M relationship between players and gathers. We use the left key as the primary key for players table and the right key as the primary key for gathers.
This is similar as a primary key composed of FK/PK for a M:M relationship. The ‘team’ attribute describes the team in which the player is member in the current gather.
We will have to specify the siblings inside our model classes. This is done using Generic principle from Swift.

For a 1:M relationship we can look at User v Gather:

final class Gather: Codable {
var userId: User.ID
extension Gather {
var user: Parent<Gather, User> {
return parent(\.userId)

Inside our controller classes the methods can be seen below:

extension GatherController {
func getPlayersHandler(_ req: Request) throws -> Future<[Player]> {
return try [Player].self) { gather in
return try gather.players.query(on: req).all()
extension PlayerController {
func getGathersHandler(_ req: Request) throws -> Future<[Gather]> {
return try [Gather].self) { player in
return try player.gathers.query(on: req).all()

Registering routes and configuring database

Open routes.swift and add the following inside routes function:

let userController = UserController()
try router.register(collection: userController)
let playerController = PlayerController()
try router.register(collection: playerController)
let gatherController = GatherController()
try router.register(collection: gatherController)

These lines will register all of your controllers.

In configure.swift add all of your models in the MigrationsConfig:
migrations.add(model: User.self, database: .sqlite)
migrations.add(model: Player.self, database: .sqlite)
migrations.add(model: Gather.self, database: .sqlite)
migrations.add(model: PlayerGatherPivot.self, database: .sqlite)

That’s it. Build & run.

iOS Developer / Doing magic things in Swift

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store