Server-side Swift using Vapor

As an iOS developer, I've always been drawn to writing even the backend of my own app so as to get full control over it. My language of choice when working is Swift, which just doesn't stop surprising me with its possibilities. It offers a truly versatile use. Apart from programming mobile and desktop apps, it's proven itself great for scripting and more and more for backend programming, too.

swift vapor banner

When it comes to backend, the obvious choice is Node.js. The Javascript on a server is very powerful, and, thanks to its wide use, also easily accessible. A large community has formed around Node.js, and this community now generates a great number of various libraries. Why try anything new, then? Javascript's main advantage is its spread; however, it tends to bear the burden of architecture decisions that haven't always been the best ones.

Since Javascript is loosely typed, the programs built upon it are prone to various bugs. Swift, on the other hand, has been a strongly typed language since day one. But still, it comes with an elaborate system of type inference, where a compiler can derive types from the context. Swift eliminates some traditional syntax items that we know from other languages, e.g. semicolons and surplus brackets, resulting in a concise, well readable and effective piece of code.

Swift has started as an Apple-platforms-only language; however, it's opened up to the world in version 2. The new open-source approach has enabled developers to use Swift with major Unix platforms, so nothing has stood in the way of leveraging it for backend programming.

One project stands out among those aiming at deploying Swift to a server — Vapor. It's exceptional not just in the number of projects built upon it, but also in the way it uses Swift's advanced features like generics and protocol extensions. Let's take a look at an example. The following piece of code shows a part of REST API for a simple CD catalogue.

import Vapor  
import HTTP

final class ArtistController {

    // Routes registration
    func addRoutes(drop: Droplet) {
        let group = drop.grouped("artists")
        group.get(handler: index)
        group.get(Artist.self, "albums", handler: albumsIndex)

        let searchGroup = group.grouped("search")
        searchGroup.get(handler: search)
    }

    // GET /artists - all artists in database
    func index(request: Request) throws -> ResponseRepresentable {
        return try JSON(node: Artist.all().makeNode())
    }

    // GET /artists/:id/albums - albums of particular artist
    func albumsIndex(request: Request, artist: Artist) throws -> ResponseRepresentable {
        let children = try artist.albums()
        return try JSON(node: children.makeNode())
    }

    // GET artists/search?q=... - search artist by name
    func search(request: Request) throws -> ResponseRepresentable {
        let results: Node

        if let searchQuery = request.query?["q"]?.string {
            results = try Artist.query().filter("name", .contains, searchQuery).all().makeNode()
        }
        else {
            results = []
        }

        return JSON(results)
    }
}

extension Artist {  
    func albums() throws -> [Album] {
        return try children(nil, Album.self).all()
    }
}

Vapor can't deny taking inspiration from the Express.js framework. An interesting thing is that handlers for the given routes gain parameters in the form of ready-made instances of model classes. An answer to a request might be any type that complies with the ResponseRepresentable protocol. Vapor also defines in a uniform manner the relationships among model classes, as you can see from the example of albumsindex method.

When working with databases, Vapor uses a high-level ORM (object relational mapping) called Fluent, which is designed to work with all databases including NoSQL. The only condition is that the selected database must include a suitable driver. In most cases, this means we can submit a request to the database directly in Swift, which makes it quite elegant. Should we find it necessary, we can still use raw queries to the database.

I'm positive that everyone who knows what classic backend programming tastes like will agree that the selection of tools is far too fragmented. It's hard to find two developers with the same programming environment preference and favorite libraries. This makes me appreciate Vapor even more; I can program the backend of my apps in my favorite Xcode and with all the comfort that comes with it when writing code and debugging later on.

Vapor is still a new framework, which leads me to a question: Is it ready for production yet? The answer depends on what we expect to implement in our backend. My needs - REST API connected to Postgres database providing security based on JWT token - has been fulfilled perfectly. We need to keep in mind that Vapor undergoes a process of active development and the API might be changing. In terms of modules available, Vapor can't compete with well-established web frameworks yet. But the community around it is growing day by day, so we can expect this to change. All in all, Vapor has a lot to offer today while looking very promising for the near future.

Share Article
Daniel Čech

Daniel Čech

You might also like...