Color Mode


    Language

6 simple steps to get your first Vapor JSON response

November 8, 2016

We will cover the initial setup and how to make both POST and GET with controllers and models.

This tutorial will assume that you already have installed Vapor and Xcode 8 and are familiar with Swift 3.

### 1. Creating your project

Now open your terminal and cd into the place you want your project.

Now run (also in the terminal, of course change the ProjectName to your actual project name)

vapor new ProjectName

You should now see an ASCII art of the Vapor logo. Like so:



                                                 **~~**
                                               **~~~~~~**
                                             **~~~~~~~~~~**
                                           **~~~~~~~~~~~~~~**
                                         **~~~~~~~~~~~~~~~~~~**
                                       **~~~~~~~~~~~~~~~~~~~~~~**
                                      **~~~~~~~~~~~~~~~~~~~~~~~~**
                                     **~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
                                    **~~~~~~~~~~~~~~~~~~~~~++++~~~**
                                     **~~~~~~~~~~~~~~~~~~~++++~~~**
                                      ***~~~~~~~~~~~~~~~++++~~~***
                                        ****~~~~~~~~~~++++~~****
                                           *****~~~~~~~~~*****
                                              *************

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

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

Congratulations, you have succesfully created your first Vapor project. Let's now try to run the project. First cd into your project (cd ProjectName) and after that run:

vapor xcode

Vapor will then fetch the dependencies and run the project. Vapor will now ask you if you want to open the project, and yes, yes we do! (So type y to open)

Select the `App` scheme to run.
Open Xcode project?
y/n>

You should now have your Xcode project open. Let's check if we have everything running correctly. So in Xcode press the 'Run' button in the top left corner of the UI (or Cmd + R). You should now see this in your console in Xcode (Make sure that you switch target from the framework to App, just next to the stop button)

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

Now c+p 0.0.0.0:8080 into your browser. If you see the 'It works.' screen then you have successfully run your first local Vapor.

Note: Beacuse Vapor is constantly updating and new versions pushed out frequently then you might have this error in your Xcode project after running for the first time.

ld: library not found for -lCLibreSSL for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

If you didn't get that error then just go to section two of this article. If you did then open your terminal and first run vapor build --clean, after that run a vapor xcode that should do the trick. (This basically cleans the project and compiles it again)

And hopefully you should be ready to rock and roll.

### 2. Creating a model

Open the terminal again, and run:

touch Sources/App/Models/Car.swift

After that run vapor xcode this will make sure that you get the correct path for the model. If you open Xcode then you should be able to see your model in the Models folder.

Fill the model with the code below:

final class Car {

    //Add your properties like you normally would in iOS
    var name: String
    var color: String
    var milesDriven: Int

    //Convinience method for instanciation of our object
    init(name: String, color: String, milesDriven: Int) {
        self.name = name
        self.color = color
        self.milesDriven = milesDriven
    }
}

### 3. Returning some JSON baby

Vapor is using a class called Droplet, the Droplet is the main entry point on the server (If you are familiar with iOS development then this would be our AppDelegate class or your entry point in Storyboard). The Droplet instance will contain all providers, routes and makes sure that everything has been linked correctly to run the different functionalities in the run time. The line drop.run() in the main.swift is where the server actually gets booted, so everything you need to add has to be before this line.

Now open the main.swift file and add the following code right above the drop.run()

//Declaring our first route, this will be a 'get' (could also be 'post' etc..)
drop.get("get_a_car") { request in

    //Creating our object
    let car = Car(name: "Toyota", color: "Red", milesDriven: 0)

    //Formating it into JSON
    return try JSON(node: [
        "name" : car.name,
        "color" : car.color,
        "miles_driven" : car.milesDriven
	])
}

Now Run your Xcode project. You should now see this in your console in Xcode (Make sure that you switch target from the framework to App, just next to the stop button)

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

Now open your browser and type in 0.0.0.0:8080/get_a_car, you should now have returned your first JSON feed! Of course, Vapor is way smarter than this. So please go ahead an delete the code you added in step 3.

### 4. Creating a Controller

Now go back to your terminal and run touch Sources/App/Controllers/CarController.swift and again vapor xcode, the Controllers folder should now contain your newly created CarController.swift

Before we can make any good use of our new Controller then we have to update our Car.swift model to actually get some of Vapors awesome magic.

So jump back into the Car.swift file and update the file to have the following:

//Importing the Vapor framework to make the model support dependencies
import Vapor
//Importing Fluent which is Vapors powering of databases and tables
import Fluent

//Subclassing our Car model from 'Model' (Vapors magic model object)
final class Car: Model {

    //Vapor uses 'Node' as their Model ids. This is the datatype that they use to make lookup and look at the primary key in the DB's.
    var id:Node?

    var name: String
    var color: String
    var milesDriven: Int

    init(name: String, color: String, milesDriven: Int) {
        self.name = name
        self.color = color
        self.milesDriven = milesDriven
    }

    //Adding one of Vapors protocols to conform to the 'Model' object. This basically makes sure that data is mapped correctly when getting extracted from a data source such as a DB.
    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        name = try node.extract("name")
        color = try node.extract("color")
        //We are making this underscored because that's how we normally name attributes in a database table
        milesDriven = try node.extract("miles_driven")
    }

    //makeNode makes sure that data can be saved into the given database (this is made super dynamic)
    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "name": name,
            "color": color,
            //We are making this underscored because that's how we normally name attributes in a database table
            "miles_driven": milesDriven
        ])
    }
}

//This is Vapor way of making migrations, leave them empty for now. We will add it later
extension Car: Preparation {
    static func prepare(_ database: Database) throws {
        //
    }

    static func revert(_ database: Database) throws {
        //
    }
}

So now our model should be ready for some cool controller action. Please now open our CarController.swift and add the following code:

import Vapor
//Import HTTP for getting all our response types, codes, etc..
import HTTP

//Adopt the ResourceRepresentable protocol in our Controller
final class CarController: ResourceRepresentable {

    //This will get called if the index in 'makeResource()' below will be called.
    func index(request: Request) throws -> ResponseRepresentable {

        //Create an object
        let car = Car(name: "Fiat", color: "SpaceGrey", milesDriven: 1000)
        return try car.converted(to: JSON.self)
    }

    //This is the function the figure out what method that should be called depending on the HTTP request type. We will here start with the get.
    func makeResource() -> Resource<Car> {
        return Resource(
            index: index
        )
    }
}

This is how some of the simplest controllers will look in Vapor. Now let's try to see how we will use them in our Droplet.

### 5. Grouped routes and Controller bindings

Are you ready to see some magic? Ok, now open the main.swift let's start by doing some house cleaning, delete the following lines from the file:

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

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

and add the following lines of code:

//Creating a route group, in this way you won't have to add the same slugs over and over again
drop.group("api") { api in
    //Adding a sub slug to our URL and redirecting all requests to the CarController we just built.
    api.resource("cars", CarController())
}

Now try to open your browser and run http://0.0.0.0:8080/api/cars you should now see:

{
	color: "SpaceGrey",
	id: null,
	milesDriven: 1000,
	name: "Fiat"
}

The id will still be null because the object wasn't instantiated from the db.

### 6. Adding some more magic

Ok cool, let's make our API actually hold some data (remember this will only be held until you have run the project again. Open your CarController.swift file and update the code to this:

import Vapor
import HTTP

final class CarController: ResourceRepresentable {

    //Adding our session container object
    var cars: [Car] = []

    func index(request: Request) throws -> ResponseRepresentable {

        //Update method to return the container object instead of the static object
        return try JSON(node: cars)
    }

    //This is where the 'post' request gets redirected to
    func create(request: Request) throws -> ResponseRepresentable {

        //Guard statement to make sure we are validating the data correct (we of course should also later guard for the color etc)
        guard let name = request.data["name"]?.string else {
            //Throw a Abort response, I like using the custom status to make sure the frontends have the correct message and response code
            throw Abort.custom(status: Status.preconditionFailed, message: "Missing name")
        }

        //Create a car
        let car = Car(name: name, color: "Red", milesDriven: 0)
        //Add it to our container object
        cars.append(car)
        //Return the newly created car
        return try car.converted(to: JSON.self)
    }

    //Add the store: create to tell Vapor that if a 'post' http requst comes in to redirect it there.
    func makeResource() -> Resource<Car> {
        return Resource(
            index: index,
            store: create
        )
    }
}

Now Run your Xcode project again. If you start by opening your browser at our 'get' endpoint http://0.0.0.0:8080/api/cars for the cars then you will of course be able to see that it's returning an empty array.

So let's now try to make a post request to our 'post endpoint' which is basically the same slug http://0.0.0.0:8080/api/cars (you can use whatever API testing tool, I normally use Postman. If you don't have any tools like that then just use this cURL:

curl -H "Content-Type: application/json" -X POST -d '{}' http://0.0.0.0:8080/api/cars

This should return our error response (response code 412):

{"error":true,"message":"Missing name"}

Let's now add our name parameter to the cURL:

curl -H "Content-Type: application/json" -X POST -d '{"name":"Fiat"}' http://0.0.0.0:8080/api/cars

And voila! We have stored our first (only in session object (and should ofc see our stored car object as JSON response). Try running the curl (or postman action) a couple of times with different names, and then run the 'get' in the browser.

[
	{
		color: "Red",
		id: null,
		milesDriven: 0,
		name: "Fiat"
	},
	{
		color: "Red",
		id: null,
		milesDriven: 0,
		name: "Tesla"
	},
	{
		color: "Red",
		id: null,
		milesDriven: 0,
		name: "BMW"
	}
]

### What's next

So as you could see it's pretty easy to get up running with Vapor. In the next blog post, I will add a MySQL database and show how you CRUD objects. (If you have followed this tutorial then it's only a small amount of lines of code that needs to be added). Sign up to the blog to make sure you get the latest tutorials and tech posts from our engineering team.

Article Photo by Dmitry Chernyshov

vaporsetup

Author

Oliver Wang Hansen

Oliver Wang Hansen

Technical Director

I love technology and people, and create tools that join both of them on a daily basis.

You may also like

March 29, 2023

Migrating Obj-C/C++ module to SPM

Swift Package Manager(SPM) provides a powerful system of modularizing Swift projects. With SPM, you can create separate modules for different parts of your codebase and manage their dependencies in a clean and efficient way. It could be done using the old...

Adrian Macarenco

Adrian Macarenco

March 1, 2023

How to set up Firebase dSYM upload for an iOS project with multiple environments (Xcode 14)

If you ever used Firebase Crashlytics for an iOS app's crash reports, chances are you also encountered the “missing dSYM files” warning in the Firebase console. The crash reports are basically useless without these files. The Firebase Crashlytics document...

Jiri Bucek

Jiri Bucek

iOS

ServicesCasesAbout Us
CareersThought LeadershipContact
© 2022 Monstarlab
Information Security PolicyPrivacy PolicyTerms of Service