Getting Started with Server-Side Swift Using the Vapor Web Framework

Continued from page 2. 

Let's add some sample data. First, initialize the array in the mongo console, as follows:

$var apple_stores =
    [
      {
        "name":"Apple Store Burlingame",
        "address":"1301 Burlingame Ave, Burlingame, CA 94010"
      },
      {
        "name":"Apple Store Union Square",
        "address":"300 Post St, San Francisco, CA 94108"
      },
      {
        "name":"Apple Store Palo Alto",
        "address":"340 University Ave, Palo Alto, CA 94301"
      }
    ];

Then load the array into the database:

$ db.stores.insert(apple_stores);

You should now have three documents representing the three stores, but just to be sure, do a quick query of the database, which should yield the following:

$ db.stores.find()
{ "_id" : ObjectId("5883fb7f6776b880e715167e"), "name" : "Apple Store Burlingame", "address" : "1301 Burlingame Ave, Burlingame, CA 94010" }
{ "_id" : ObjectId("5883fb7f6776b880e715167f"), "name" : "Apple Store Union Square", "address" : "300 Post St, San Francisco, CA 94108" }
{ "_id" : ObjectId("5883fb7f6776b880e7151680"), "name" : "Apple Store Palo Alto", "address" : "340 University Ave, Palo Alto, CA 94301" }

Now you're done setting up Swift, Vapor, and MongoDB in your development environment, you'll now create your app.

Learning some of the Vapor concepts

This is the part that differs between the various server-side Swift projects, like Perfect and Kitura, so spend a few minutes going through some concepts before you start coding your server-side app, starting with droplets.

  • Droplets are central to Vapor apps and play the role of service containers, providing a portal of access to various framework facilities, as well as registering routes, middleware, and databases.
  • Views return HTML back to the client side, either returned back in pure HTML form or via an intermediary rendered like Stencil. In this example you won't be using Views because you'll be returning back JSON.
  • Controllers are similar to controllers on the iOS client side and in the MVC pattern, is used to separate functionality and group-related functionality from the model and the Views. In your case, controllers are also used to create RESTful endpoints.
  • Providers connect your models to databases or to integrate other types of third-party packages to your project.

This may sound a bit abstract as of now, but it will make more sense as you start to use these concepts as you build your sample app.

Developing the application

When you created your skeleton Vapor project earlier using vapor init, the framework created a file and folder structure similar to the following:

applestoresAPI
|— Sources
| |— App
| |— Controllers
| |— Middleware
| |— Models
| |— main.swift
|— Public
|— Resources
| |— Views
|— Package.swift

Setting up the Packages

You will notice in the root of your project a file called Package.swift. This is where you set the packages/libraries you will use in your project, so edit the file in an editor of your choice, adding the following:

import PackageDescription

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

After you finish, save, then go back in terminal, and run the following to fetch the actual packages and dependencies (it may take a while to finish downloading):

vapor build

Once it's finished fetching the dependencies, provided you didn't get any errors, you can open the project in Xcode, by running:

vapor xcode

You should be able to see the project now opening up in Xcode, setting the platform for you to start working on each of the components.

Setting up the Droplet

The first thing you'll do in Xcode, is open up the main.swift file, so you can register the driver youll be using. In the file, replace the line that says:

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

with

drop.resource("stores", StoreController())

This wires up in your droplet, an API resource for the controlled stores. You haven't created that controller yet, but you will later on, in case you get a warning or error regarding that line. Edit the rest of the file to make it look as follows:

import Vapor
import Fluent
import VaporMongo

let drop = Droplet()
try drop.addProvider(VaporMongo.Provider.self)

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

let store = StoreController()
drop.get("stores", handler: store.index)

drop.run()

You'll also need to set the configuration options so that your app can connect to our database, so add a new file, mongo.json, to the config folder in your project, with the mongo database settings for the user you created for the stores database.(Note: if you are running your mongo server on 27017, please amend as appropriate):

{
  "user": "myTester",
  "password": "xyz123",
  "database": "stores",
  "host": "localhost",
  "port": "27018" 
}

Setting up the Controller

Next in the Sources/App/Controllers folder, rename the class PostsController to StoreController.swift, and delete all the contents of that file. Replace it with the following:

import Vapor
import HTTP

final class StoreController: ResourceRepresentable {
    typealias Item = Store

    func index(request: Request) throws -> ResponseRepresentable {
        return try JSON(node: [
            "controller": "StoreController.index"
        ])
    }

    func show(request: Request, item store: Store) throws -> ResponseRepresentable {
        return try JSON(node: [
            "controller": "StoreController.show",
            "store": store
        ])
    }

    func makeResource() -> Resource {
        return Resource(
            index: index,            
            show: show
        )
    }

}

extension Request {
    func store() throws -> Store {
        guard let json = json else { throw Abort.badRequest }
        return try Store(node: json)
    }
}

As you can see, you have one simple GET method, which returns an object of type ResourceRepresentable, which is an agile type that allows you to return the result as a JSON via a node. Another thing of note is that you use try because it's possible that the call might fail, and finally, using the in-built fluent, you can make a simple query call through Store.all().

Doron Katz A keen passion for emerging technologies, practices and methodologies, Doron embraces the vision of lean development with continuous customer development. Consultant for various startups, as a Project and Product Manager, with a mobile engineering background in iOS, and over 10 years of professional web development experience.
 

Comments

Comments(2)

phani2

Hi , am able to successfully create the user but am not able to acces the console for stores db with the following command  >mongo -u myTester -p xyz123. I get this error  E QUERY    [main] SyntaxError: missing ; before statement @(shell):1:9

Am new to mongodb, any help appreciated. Thanks