What is routing?

Routing is the way in which requests (the combination of a URL and a HTTP method) are routed to the code that handles them.

When you navigated to www.kitura.dev in your browser you were requesting the HTML file that is located there. The server hosting the files will respond with the requested file, if it exists, and you will see the file rendered in your browser. This means that under the covers there’s a server hosting the files for the kitura.dev website. In this server there is a route defined that states if we receive a GET request on "/" then respond with the home page.

Here we mentioned a GET request being made on "/" but what does that mean? In the next section we will look at GET, which is an HTTP Method, in more detail.


REST APIs

Representational state transfer (REST) or RESTful APIs are a way of building web services to allow clients and servers to communicate using standard HTTP protocols. Essentially it’s an architectural and design pattern for building an API using HTTP methods, URI paths, and request and response body data.

HTTP Methods

An HTTP method is the part that defines what type of REST API we are calling. For example: A GET request gets a resource from the specified location, hence the name GET.

Below is a table of the main HTTP methods used in REST, listing their primary usage, what the request body should contain and what response we should expect from a call of that type.

HTTP Method Usage Request Body Response Body
GET The GET method is used to retrieve resources only No request body needed Reponds with resource
POST The POST method is used to create a new resource The resource to be created Reponds with the created resource
PUT The PUT method updates an existing resource The new version of a resource Responds with the updated resource
DELETE The DELETE method deletes an existing resource No request body needed No reponse body
PATCH The PATCH method makes a partial update to a resource The partially updated resource Responds with the updated resource

URI Paths

A URI path is the path upon which this route is linked. That is to say when a request is made to the specified path we know this is the code block to execute. Here is an example:

router.get("/hello")

Above is an example of registering a GET method on the "/hello" path. Let's assume our server is running at localhost:8080, then if I want to make a call to this endpoint I could open http://localhost:8080/hello in my browser and view the response.

Route handler

Each route has a route handler. This is a closure or function that is executed when a request is made to the route's path. The route handlers contain the developer's logic for how to process incoming requests and what response should be sent.

If multiple route handlers match the path, they will be executed in the order that they are registered on the router. This is important when using middlewares, since they should normally be executed before the other handlers.

Kitura provides two 'types' of routing, Raw Routing and Codable Routing. Over the next couple of sections we will discuss them and highlight their differences.


Raw Routing

Raw routing is where the route handlers were called with RouterRequest and RouterResponse objects with which to handle the client request and build the response, along with a next completion handler. This approach provides great flexibility and control, but requires you to understand the structure of requests, how to interpret HTTP request headers correctly, how to verify data, and to manually carry out things like JSON parsing.

The following is an example of a Raw Route:

router.get("/") { request, response, next in
    response.send("Hello world")
    next()
}

Router Request

The RouterRequest class is used to interact with incoming HTTP requests to the router. It contains and allows access to the request’s headers and body as well as other properties of the request. It can also perform content negotiation based on the request’s Accept header.

Router Response

The RouterResponse class is used to define and work with the response that will be sent by the router. It contains and allows access to the HTTP response code (e.g. 404 Not Found), the HTTP headers and the body of the response. It can also render template files, using a template engine registered to the router.

Next

In Kitura, multiple handlers can be registered on a single route. The handlers can alter the request or response before passing them to the next handler. By having the next closure, we can register a handler that acts as a middleware and is called before your routes.

Once a handler is finished, it calls the next handler using the next() closure. If there are no more handlers, the router will send the response. If there was an error and you do not want to call the next handler, you can call request.end() to send back the response immediately. You must call next() or request.end() once, and only once, in each route handler because the route handlers are asynchronous.

For example:

router.get("/") { request, response, next in
    response.send("Hello from the 1st route!")
    next()
}

router.get("/") { request, response, next in
    response.send("Hello from the 2nd route!")
    next()
}

If a request comes into the server on "/" then the following would be returned to the client:

"Hello from the 1st route!"
"Hello from the 2nd route!"

As you can see one route is called immediately after the other, this is made possible because of next().


Codable Routing

Codable routing is where the route handlers are like normal functions you might define elsewhere in your code; they take struct or class types as parameters, and respond with struct or class types via a completion handler. The only requirement is that those types conform to the Codable protocol introduced in Swift 4 (hence the name).

func getHandler(completion: ([String]?, RequestError?) -> Void) {
    completion(["Hello World!"], nil)
}

router.get("/", handler: getHandler)

Codable model

Any type that conforms to Codable can be used as a parameter in Codable routing, including custom structs and classes. As we are simulating a bookstore in our guides, we are going to need to define a book. The book is a model so the first thing we need to do is create our Models folder:

mkdir Sources/Application/Models

This is the directory where we will store all of our models.

Next we will create our Book.swift file:

touch Sources/Application/Models/Book.swift

We will open the Book.swift file in our default text editor:

open Sources/Application/Models/Book.swift

Inside this file we define our Book:

struct Book: Codable {
    let id: Int
    let title: String
    let price: Double
    let genre: String

    init(id: Int, title: String, price: Double, genre: String) {
        self.id = id
        self.title = title
        self.price = price
        self.genre = genre
    }
}

Conclusion

Codable routing provides a simple approach to building RESTful APIs. It adds compile-time type safety, creates cleaner code and can automatically generate an OpenAPI specification. If you are building a Backend For Frontend (BFF) for an iOS app, you should probably use Codable routing.

Raw routing is more flexible and provides greater control over your route handler implementation but does not have the same inherent type safety. If you are creating a non-RESTful API, such as a web application, then you will need to use Raw routing.

Next steps

Add Codable Routing: Use Kitura's Codable routing to define routes on your server.

Add Raw Routing: Use Kitura's Raw routing to define routes on your server.