Routing in Vapor 2.0, Part 1: Basic Routing

1473 Views

Hello droplets! Routing is one of the core functions of any backend framework. It is quite simple and elegant with vapor. So let's begin!

Create a new Vapor project, called Routing, by running:

    $ vapor new Routing --template=api

Then run vapor xcode to create the Xcode project, and press y when prompted to open the Xcode project.

First, delete the Post.swift file and the PostController.swift file, we will not be using them. Then, in the setupPreparations() method, in Config+Setup.swift, delete the line preparations.append(Post.self).

This entire tutorial will take place in Routes.swift. Delete all of the content from the setupRoutes() function.

Basic route

First, let's make a simple GET request. add the following route to setupRoutes():

    get("hello") { request in
        return "Hello, Vaporists!"
    }

The get function is called on our instance of droplet. All routes in vapor are called from the instance of our droplet.

Every route in vapor has 3 things:

  • A method (get, post, put, patch, delete)
  • A path, supplied by the user ( `"hello"` in our case)
  • a closure with the request

Also, we may notice that we returned a string. Every route closure in Vapor can return 1 of 3 ways:

  • a Response
  • a type conforming to the ResponseRepresentable protocol
  • throw an error that conforms to Vapor.error

Nesting Routes

Nesting a hard-coded route is quite simple in vapor. Add the following to setupRoutes():

    get("hi/how/are/you") { request in
        return "this path is /hi/how/are/you"
    }

Custom Response:

We can also return a custom Response object as well. First, add import HTTP to the top of your Routes.swift file. Now, for example, we can redirect:

    get("foo") { request in
        return Response(redirect: "https://apple.com")
    }

Navigating to /foo will now redirect you to the Apple website.

or we can return a certain status:

    get("bar") { request in
        return Response(status: Status.forbidden)
    }

Like Status.forbidden, we can also return other instances of the Status enum. Some of the more common ones are:

    Status.accepted
      Status.authenticationTimeout
      Status.badRequest
      Status.badGateway
      Status.created
      Status.notFound

Or, we can return a Response with a status and customized JSON:

    get("vapor") { request in
        return try Response(status: Status.accepted, json: JSON(node: ["yourStatus here": "hello world!"]))
    }
    

Parameters

Oftentimes we need routes such as /posts/1, for showing a post with an id of 1, or /posts/1/comments to show the post's comments. Vapor handles this very elegantly with its type-safe Parameterizable protocol:

    get("post", Int.parameter) { request in
        let parameter = try request.parameters.next(Int.self)
        return "You requested route /post/\(parameter)"
    }
        
    get("post", Int.parameter, "comments") { request in
        let parameter = try request.parameters.next(Int.self)
        return "You requested route /post/\(parameter)/comments"
    }

With the Parameterizable protocol, any type that conforms to it can be represented as a parameter as above. If your uniqueId is not an int, but a string, that is also Parameterizable:

    get("heart", String.parameter) { request in
        let parameter = try request.parameters.next(String.self)
        return "You requested route /heart/\(parameter)"
    }

Part 2 of this series on Routing in vapor will go more in depth on the Parameterizable protocol, and how we can use it with our models to efficiently retrieve objects.

Fallback Routes

A fallback route is defined in vapor as follows:

    get("foobarbaz", "*") { request in
        // this route matches:
           // /foobarbaz/1
           // /foobarbaz/1/2/3
        return "this is a fallback route"
    }

As the second parameter, simply put a "*", and the route will match anything, as long as it begins with foobarbaz

Groups

If we need to put multiple routes underneath one common route, for example /v1/users and /v1/posts and /v1/comments, we can easily group them:

    group("v1") { v1 in
        v1.get("users") { request in
            return "this route is /v1/users"
        }
        v1.get("comments") { request in
            return "this route is /v1/comments"
        }
    }

Alternatively, you can do the exact same as above, using this syntax:

    let v1 = grouped("v1")
    v1.get("users") { request in
        return "this route is /users/all"
    }
    v1.get("comments") { request in
        return "this route is /users/none"
    }

And of course, you can combine different types of routes as well, such as grouping and parameters:

    v1.get("users", Int.parameter) { request in
        let parameter = try request.parameters.next(Int.self)
        return "You requested /v1/users/\(parameter)"
    }

That's it for this beginning tutorial to routing in Vapor 2! Check back soon for Part 2 of the series, which will focus on using the Parameterizable protocol with your models, organizing your routes in controllers, and route collections. Thanks for reading!