Skip to content

Upgrading Versions

This document provides information about changes between version and tips for migrating your projects.

2.4 to 3.0

Vapor 3 has been rewritten from the ground up to be async and event-driven. This release contains the most changes of any previous release (and most likely any future release).

Because of this, it is recommended that to migrate your projects you start by creating a new, empty template and migrate by copy / pasting code over to the new project.

We recommend reading the Getting Started → Hello, world! section for Vapor 3 to familiarize yourself with the new APIs.


The biggest change in Vapor 3 is that the framework is now completely asynchronous. When you call methods that need to perform slow work like network requests or disk access instead of blocking they will now return a Future<T>.

Futures are values that may not exist yet, so you cannot interact with them directly. Instead, you must use map/flatMap to access the values.

// vapor 2
let res = try drop.client.get("")
print(res.status) // HTTPStatus
return res.status

// vapor 3
let f = try req.client().get("").map { res in
    print(res.http.status) // HTTPStatus
    return res.http.status
print(f) // Future<HTTPStatus>

See Async → Getting Started to learn more.

Application & Services

Droplet has been renamed to Application and is now a service-container. In Vapor 2, the Droplet had stored properties for things you would need during development (like views, hashers, etc). In Vapor 3, this is all done via services.

While the Application is a service-container, you should not use it from your route closures. This is to prevent race conditions since Vapor runs on multiple threads (event loops). Instead, use the Request that is supplied to your route closure. This has a copy of all of the application's services for you to use.

// vapor 2
return try drop.view.make("myView")

// vapor 3
return try req.make(ViewRenderer.self).render("myView")
// shorthand
return try req.view().render("myView")

See Service → Getting Started to learn more.

Database Connections

In Vapor 3, database connections are no longer statically accessible. This makes doing things like transactions and connection pooling much more predictable and performant.

In order to create a QueryBuilder in Fluent 3, you will need access to something DatabaseConnectable. Most often you can just use the incoming Request, but you can also create connections manually if you need.

// vapor 2

// vapor 3
User.query(on: req).all()

See DatabaseKit → Getting Started to learn more.

Migrating SQL Database

When migrating from Fluent 2 to 3 you may need to update your fluent table to support the new format. In Fluent 3, the migration log table has the following changes:

  • id is now a UUID.
  • createdAt and updatedAt must now be camelCase.

Depending on how your Fluent database was configured, your tables may already be in the correct format. If not, you can run the following queries to transfer the table data.

Use this query if your column names were already set to camelCase.

ALTER TABLE fluent RENAME TO fluent_old;
    AS (SELECT UUID() as id, name, batch, createdAt, updatedAt from fluent_old);

Use this query if your column names were snake_case.

ALTER TABLE fluent RENAME TO fluent_old;
    AS (SELECT UUID() as id, name, batch, created_at as createdAt, updated_at as updatedAt from fluent_old);

After you have verified the table was transferred properly, you can drop the old fluent table.

DROP TABLE fluent_old;

Work in progress

This migration guide is a work in progress. Please feel free to add any migration tips here by submitting a PR.

Join the #upgrading-to-3 in Vapor's team chat to ask questions and get help in real time.

Also check out Getting started with Vapor 3, an in-depth article about the differences between Vapor 2 and 3. This article was written by two developers from an app development company using Vapor.