';
Migrating from Vapor 1 to Vapor 2

Vapor 2 is released 🚀🎉 This is a big breaking change, but a good one. We have a small server-side project which is developed with Vapor. When Vapor 2 released, we directly took a look at what changed. We were a bit 😍 and a bit 😩. Vapor 2 has fewer lines of code, the performance is better and has a lot of new cool features. But on the other side, there is too much change that we had to adopt. As a beginner to the server-side and Vapor, it took quite some time. We took some notes while migrating to version 2. First, let me introduce the basic components that I’ll talk about. We’re using MongoDB as a database. We have two custom middlewares which are for verifying requests and setting up response headers. We use custom Routing with RouteCollection. That’s all. Let’s start migrating 💪
We started with the Package.swift file. We were using Vapor 1.5.0 and VaporMongo 1.1.0

Now, we’re using Vapor 2.0 and MongoProvider 2.0.0 (was named VaporMongo before). MongoProvider comes with FluentProvider which is the new package. “The Fluent provider package adds Fluent to project and adds some additional, Vapor-specific conveniences like HTTP conformances.”

Then the moment came. Ran vapor clean && vapor build command and BAM! Tons of errors.😩 There are a lot of things to do.

Model

The Model protocol has moved to the FluentProvider package. We had to import FluentProvider to our Model files.
In Model, query() method change to makeQuery(). So, we were using User.query() and we changed it to User.makeQuery() to run custom queries on User model. The other entity methods like all, find, chunk, count are still same.
Fluent models now require Storable protocol conformance for database operations. This protocol requires only one property which handles id and exists properties’ jobs. We don’t need to handle these properties manually anymore. We put let storage = Storage() as a property of every model.
Instead of Node struct and NodeConvertible protocol, we now have Row struct and RowConvertible protocol. It has two new methods one for initializing model and the other for making a row. Even though I don’t get the idea of a Row in databases like MongoDB, the implementation looks better now. So, we had NodeConvertible implementations before like this:

We migrated to RowConvertible and changed our code to following:

Lastly, we need to send ids in the response body. We had to conform JSONRepresentable protocol and create custom JSON for our models.

Note: If you still need NodeConvertible, NodeRepresentable protocol’s function syntax has been changed.
func makeNode(context: Context) throws -> Node migrated to this:
func makeNode(in context: Context?) throws -> Node

RouteCollection protocol

The only change in RouterCollection protocol is the method definition.

migrated to

Also drop.collection(OurCoolCollection()) is now throwable.

Request Parameters

Because of technical limitations, we had a limit for the request parameters before. Now, there is no limit because the implementation is totally changed. There is a new Parameterizable protocol. Anything that conforms to Entity protocol, can conform Parameterizable without any extra work. So, every Fluent model can be Parameterizable easily with only conforming like
extension User: Parameterizable { } .

In our custom collection, implementation of endpoints was like this:

with only one change, it became like this

And the implementation of create function in the controller was like this:

Instead of the function parameters, we can get all request parameters directly from the request. So, the syntax of function became like following

String and Int data types automatically conforms Parameterizable protocol. It means that we can use them as parameters. We also have an endpoint which only requires the id of User as a String in the request. In Vapor 2, new implementation is like this:

and in the get function, we just called .parameter property of Parameterizable protocol.

users.get("someEndpoint", String.parameter, handler: userController.someFunction)

Droplet & Configuration

All of the Droplet parameters are now constants. Changing them after initialization is not allowed anymore. Vapor 2 has new Config concept which we can set up our Droplet’s configurations like middlewares, providers, preparations etc.. Droplet has two initialization methods. We used init(_ config: Config). Creating the configuration and setting it up separately looks cleaner.
Our Droplet was written in Vapor 1 and it was something like that:

Now, we first created the config with
let config = try Config()
next, we added middleware to config

lastly, we added these middlewares to our droplet.json file. Vapor 2 puts the middlewares in order according to droplet.json. For example, if you want your AwesomeMiddleware to run at first, add it to the top of middlewares array at the droplet.json file. After adding the middlewares to the droplet.json, it became like this

As mentioned before, we now have the new FluentProvider. We had to add fluent.json for under Config folder. Otherwise, it crashes the app when we run. Vapor allows you to define the idType for the database as UUID format in JSON file instead of creating manually in every model. Also, we have to define our driver in here. We’re using MongoProvider and because of that our database driver is mongo. We added this provider with only one line

try config.addProvider(MongoProvider.Provider.self)

and we defined a fluent.json file like

Let’s look at preparations quickly. There is no extra work like Provider. We just added them to the config.
config.preparations.append(User.self)
Now, we can initialize our Droplet with the configuration in one line, that’s all.
let drop = try Droplet(config)

Our Config is not done yet. It seems hash and cipher have mandatory keys right now. So, we had to change our crypto.json file. Both of them now requires three keys, method, encoding, and key. Our crypto.json file was like a dummy one before

Now, it looks smarter

The last thing for the config was the url. It’s mandatory in mongo.json for MongoProvider configuration. Even if we gave host, port, username, password, and database parameters in JSON file, it insistently asked the url key. So, we just added url key to mongo.json file in the following format. The key point is, MongoKitten requires this url in a certain format. It has to start with mongodb://.

"url": "mongodb://user:password@127.0.0.1:27017/test_db"

Abort

Abort is not enum anymore. It has converted to struct with three parameters.

We were using the Abort like following

and it is migrated to cleaner implementation

Update:

servers.json file is simplified and renamed to server.json. Basic server.json can be found in the documentation like this:

Last words

Adoption to the latest versions with breaking changes is generally hard. But when it comes to performance, clean code and other new features, walking around with the old version become redundant. Some people declare this as living on the edge. Although I agree with this on some level, I generally try to use latest versions when they came out. But I always give it a try with some trial projects, not directly on production code.
Further reading
Vapor 2 New Documentation
Steampress.io Vapor 2 Migration
Vapor Release Notes
Vapor Changelog


Thanks for reading! Help spread the word ❤️ 🚀.
Do you have questions, suggestions, comments or ideas for upcoming blog posts? Contact me on Twitter or write a comment! 😍 You can also follow me on GitHub.

Candost Dagdeviren

Software Developer. Technology and theater enthusiast. Starving for any kind of information.

  • Anthony

    Thank you, it was great !