What’s new in Vapor 4?


Vapor is the most popular server side Swift web application framework. This time we’ll cover what’s new in Vapor 4.


πŸ“– Practical Server Side Swift – Third edition of my book is now available.

Swift 5.1

Vapor 3 was built on top of some great new features of Swift 4.1, that’s why it was only released shortly (2 months) after the new programming language arrived. This is the exact same situation with Vapor 4. Property wrappers are heavily used in the latest version of the Vapor framework, this feature is only going to be finalized in Swift 5.1 during the fall, which means that we can expect Vapor 4 shortly after. 🍁

SwiftNIO v2 andΒ HTTP2 support

A HUGE step forward and a long awaited feature, because HTTP2 is amazing. Multiplexed streams, server push, header compression, binary data format instead of the good old textual one over a secure layer by default. These are just a few important changes that the new protocol brings to the table. The basic implementation is already there in Vapor 4 alpha 2, I tried to setup my own HTTP2 server, but I faced a constant crash, as soon as I can make it work, I’ll write a tutorial about it. 🀞

Fluent is amazing in Vapor 4!

Controllers now have an associated database object, this means you can query directly on this database, instead of the incoming request object. Note that the Future alias is now gone, it’s simply EventLoopFuture from SwiftNIO.

import Vapor

final class TodoController {
    func index(_ req: Request) throws -> Future<[Todo]> {
        return Todo.query(on: req).all()

    func create(_ req: Request) throws -> Future<Todo> {
        return try req.content.decode(Todo.self).flatMap { todo in
            return todo.save(on: req)

    func delete(_ req: Request) throws -> Future<HTTPStatus> {
        return try req.parameters.next(Todo.self).flatMap { todo in
            return todo.delete(on: req)
        }.transform(to: .ok)

import Fluent
import Vapor

final class TodoController {
    let db: Database

    init(db: Database) {
        self.db = db

    func index(req: Request) throws -> EventLoopFuture<[Todo]> {
        return Todo.query(on: self.db).all()

    func create(req: Request) throws -> EventLoopFuture<Todo> {
        let todo = try req.content.decode(Todo.self)
        return todo.save(on: self.db).map { todo }

    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
        return Todo.find(req.parameters.get("todoID"), on: self.db)
            .unwrap(or: Abort(.notFound))
            .flatMap { $0.delete(on: self.db) }
            .transform(to: .ok)

Fluent has dynamic models, also the entire database layer is more sophisticated. You can define your own keys, schemas and many more which I personally love it, because it reminds me of my really old PHP based web framework. It’s really amazing that you don’t have to deal the underlying database provider anymore. It’s just Fluent so it really doesn’t matter if it’s pgsql or sqlite under the hood. ❀️

import FluentSQLite
import Vapor

final class Todo: SQLiteModel {
    var id: Int?

    var title: String

    init(id: Int? = nil, title: String) {
        self.id = id
        self.title = title

extension Todo: Migration { }

extension Todo: Content { }

extension Todo: Parameter { }

import Fluent
import Vapor

final class Todo: Model, Content {
    static let schema = "todos"

    @ID(key: "id")
    var id: Int?

    @Field(key: "title")
    var title: String

    init() { }

    init(id: Int? = nil, title: String) {
        self.id = id
        self.title = title

There is a brand new migration layer with a ridiculously easy to learn API. πŸ‘

import Fluent

struct CreateTodo: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        return database.schema("todos")
            .field("id", .int, .identifier(auto: true))
            .field("title", .string, .required)

    func revert(on database: Database) -> EventLoopFuture<Void> {
        return database.schema("todos").delete()


A native logger library made by Apple is now the default logger in Vapor 4.

The entire logging system is bootstrapped during the boot process which I like quite a lot, because in the past I had some issues with the logger configuration in Vapor 3. πŸ€”

import Vapor

func boot(_ app: Application) throws {
    try LoggingSystem.bootstrap(from: &app.environment)
    try app.boot()

“Syntactic sugar”

Some little changes were introduced in the latest version of the framework.

For example the input parameter names in the config and the routes file are just one letter long (you don’t need to type that much). I personally don’t like this, because we have auto-complete. I know, it’s just a template and I can change it, but still… 🀐

Another small change is that the entire application launch / configuration process is way more simple than it was before, plus from now on you can shut down your app server gracefully. Overall it feels like all the API’s in Vapor were polished just the right amount, I really like the changes so far. πŸ˜‰

… and many many more!

Tanner Nelson has posted quite a list on Vapor’s discord server (it’s such an amazing community, you should join too). I’m going to shamelessly rip that off to show you most of the things that are going to be included in Vapor 4. Here is the list:


  • services on controllers
  • synchronous content decoding
  • upload / download streaming
  • backpressure
  • http/2
  • extensible route builder (for openapi)
  • apple logging
  • improved session syntax
  • dotenv support
  • validation included
  • authentication included
  • XCTVapor testing module
  • swift server http client
  • simplified websocket endpoints
  • graceful shutdown
  • nio 2



  • performance improvements
  • performance testing bot


  • dynamic models
  • simplified driver requirements
  • eager loading: join + subquery
  • partial selects
  • dirty updates


  • improved body syntax
  • separate lexer + parser


How to set up a Vapor 4 project (on macOS)?

If you want to play around with Vapor 4, you can do it right now. You just have to install Xcode 11, the Vapor toolbox and run the following command from Terminal:

sudo xcode-select --switch /Applications/Xcode-beta.app/Contents/Developer

vapor new myproject --branch=4
cd myproject
vapor update -y

Personally I really love these new changes in Vapor, especially the HTTP2 support and the new Fluent abstraction. Vapor 3 was quite a big hit, I believe that this trend will continue with Vapor 4, because it’s going to be a really nice refinement update. πŸ’§

I can’t wait to see some new benchmarks, because of the underlying changes in vapor, plus all the optimizations in Swift 5.1 will have such a nice impact on the overall performance. Vapor 3 was already crazy fast, but Vapor 4 will be on fire! πŸ”₯


Source link

Leave a Reply

Your email address will not be published.