Gettings started with Vapor - A Web Framework for Swift.

We will start a new vapor app with RESTful controller and SQLite support. So let's get started

Installation:

Vapor comes with nice CLI utility called Toolbox.
The easiest way to install it on macOS is to use homebrew ( package manager for macOS )
if you don't have homebrew installed already run following command in the Terminal:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

after you have installed homebrew you can install Toolbox by running following command:

brew install vapor/tap/vapor

after the installation completes try running:

vapor --help

it should provide you the following output:

Usage: vapor <new|build|run|fetch|clean|test|xcode|version|self|heroku>
Join our Slack if you have questions, need help,
or want to contribute: http://vapor.team

Create new project

Run:

vapor new hello-vapor

It should create a new project in folder hello-vapor and provide something like following output:

Cloning Template [Done]
Updating Package Name [Done]
Initializing git repository [Done] 

          _       __    ___   ___   ___
         \ \  /  / /\  | |_) / / \ | |_)
          \_\/  /_/--\ |_|   \_\_/ |_| \
            a web framework for Swift

     Project "hello-vapor" has been created.
Type `cd hello-vapor` to enter the project directory.
                      Enjoy!

So we can now cd into that folder run following command:
vapor xcode

which would fetch dependencies and create Xcode project for us, also when finished it would ask us if we like to open this project in Xcode.

After your open your project in the Xcode you would see that you have two schemes "hello-vapor" and "App", select the later and build in run it.

In the Xcode console you should see following output:

No command supplied, defaulting to serve...
No preparations.
Server 'default' starting at 0.0.0.0:8080

That's it. We have our project running, if you would go to http://localhost:8080 you would see a page which states "It works"

Looking in main.swift - Into the rabbit hole.

If we would open main.swift file we would see something similar to:

import Vapor

let drop = Droplet()

drop.get { req in
    return try drop.view.make("welcome", [
        "message": drop.localization[req.lang, "welcome", "title"]
    ])
}

drop.resource("posts", PostController())

drop.run()

Let's go line by line.
At first we import the Vapor framework, then we instaniate Droplet object.
Droplet is a service container that gives you access to many of Vapor's facilities. It is responsible for registering routes, starting the server, appending middleware, and more stuff like that.

after we are saying that on the root route we would like to return a view rendered from the "welcome" template and passing localized string with key "welcome.title" to our view accessible there with name of "message".

If you are interested more how the view rendering works,
take a look at the documentation here https://vapor.github.io/documentation/guide/leaf.html

the next line register REST'full controller PostController for the route "posts"

and the final line actually starts the server

Adding database support (SQLite)

So if look at the PostController.swift we would see that it actually maps CRUD operations on Post model to REST interface, using makeResource method. but if we would try to open http://localhost:8080/posts we would get a page which states "EntityError: noDatabase"
and returns http status 500. Let's fix that by adding database support so we could work with our Post model.

At first open Package.swift and modify it so it would look like this:

import PackageDescription

let package = Package(
    name: "hello-vapor",
    dependencies: [
        .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5),
        .Package(url: "https://github.com/vapor/sqlite-provider.git", majorVersion: 1, minor: 1)
    ],
    exclude: [
        "Config",
        "Database",
        "Localization",
        "Public",
        "Resources",
    ]
)

as you can see we are adding https://github.com/vapor/sqlite-provider.git package here.
Now let's go back to our project folder in the terminal and type:

vapor fetch

It would fetch dependencies for our project, based on the contents of Package.swift
next we need to regenerate our xcode project with included support of new dependencies
so let's close our project and run

vapor xcode

command again, and reopen the project when command is finished.

Next step is to add configuration file for the database named sqlite.json to the Config folder of our project.
since we are using sqlite the only config param we actually need for now is path of the database file, so the file would look like this:

{
    "path": "hello-vapor.db"
}

Next we need to let Vapor know that we are using sqlite, so let's open main.swift

and modify it as follows:

import Vapor
import VaporSQLite

let drop = Droplet()

try drop.addProvider(VaporSQLite.Provider.self)

drop.get { req in
    return try drop.view.make("welcome", [
        "message": drop.localization[req.lang, "welcome", "title"]
    ])
}

drop.resource("posts", PostController())

drop.run()

here we added import of VaporSQLite and registered it's provider with our droplet.

Working with Model and Vapor Preparations

Now lets head to our Post model, first we need to remove following lines from it:

extension Post {
    /**
        This will automatically fetch from database, using example here to load
        automatically for example. Remove on real models.
    */
    public convenience init?(from string: String) throws {
        self.init(content: string)
    }
}

Second let's add Preparations to our model. Basically preparation is Vapor name for migrations.
So it's a right place to prepare our database for our data objects. I'll just post a full file listing here since it's pretty
self explanatory.

import Vapor
import Fluent
import Foundation

final class Post: Model {
    var id: Node?
    var content: String
    var exists: Bool = false

    init(content: String) {
        self.id = UUID().uuidString.makeNode()
        self.content = content
    }

    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        content = try node.extract("content")
    }

    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "content": content
        ])
    }
}

extension Post: Preparation {
    static func prepare(_ database: Database) throws {
        try database.create("posts", closure: { posts in
            posts.id()
            posts.string("content")
        })
    }

    static func revert(_ database: Database) throws {
        try database.delete("posts")
    }
}

As you can see we are just creating and deleting posts table in our database.

So the last thing we need to do is to let our droplet know about the migration:

In the main.swift add line

drop.preparations.append(Post.self)

More information about model and prepations can be find in the documentation at https://vapor.github.io/documentation/fluent/model.html

So let's run finally run our project, this time the console output would be:

No command supplied, defaulting to serve...
Preparing Post
Prepared Post
Database prepared
Server 'default' starting at 0.0.0.0:8080

Yay. Our model and database are prepared. and we actually have a REST interface for your Post model. See for your self using your favourite client be curl or postman ;)

That's it for now. Have fun.