In Kitura 2.0 we added support for Codable Routing, which made the building of Web routes and REST APIs dramatically easier by removing the need for large amounts of boilerplate code and by automatically converting request and response data into Swift types used by the application. This provided type-safety and completely removed the need for application code to carry out things like JSON parsing and data validation.

The automatic conversion of request and response data was very much the first step in simplifying the programming model, and in Kitura 2.2 we have added the next step with support for the automatic conversion of URL encoded query parameters into Swift types.

What are URL Encoded Query Parameters?

URL encoded query parameters are covered in section 3.4 of RFC 3986: “Uniform Resource Identifier (URI): Generic Syntax”. These are essentially query components, typically “key=value” pairs, that are added to the end of a URI after a question mark (‘?’) and before a hash (‘#’) or the end of the URI. These are typically used to provide a filter to reduce the amount of data that would otherwise be returned.

The following is an example of a URI with a single query parameter, which would be used to request all users, but filtered to return only those with a firstName of John:

GET:    /users?firstName=John

and the following is an example that requests all users with a firstName of John and a familyName of Doe:

GET:    /users?firstName=John&familyName=Doe

Working with Query Parameters in Codable Routing

The following code implements a URI handler using Codable Routing that responds to a GET request on /users and returns all of the stored users:

  1. Implement a User type that implements Codable along with a simple datastore called users that holds an array of Users:

    public struct User: Codable {
        let firstName: String
        let familyName: String
    }
    
    var users = [User(firstName: "John", familyName: "Doe"),
                     User(firstName: "Jane", familyName: "Doe")]
    
  2. Create a handler that gets the users, and register it with the Router for GET requests on /users:

    func getUsers(completion: @escaping([User]?, RequestError?) -> Void) -> Void {
            completion(users, nil)
    }
    router.get("/user", handler: getUsers)
    

This has implemented the following URI:

GET:    /users

using only the application defined User type, without the need to do any additional parsing, conversion to or from JSON, or data validation.

The new query parameter feature in Kitura 2.2 extends this to make it possible to also add query parameters, again using only an application defined type and without having to do any additional parsing or data validation. This can be done in just two steps:

  1. Create a Swift type that conforms to the QueryParams protocol, which defines the key=value pairs that you want to be able to handle, eg:

    public struct Query: QueryParams {
        let firstName: String
    }
    

    This defines that your URI can accept a query parameter with a key of firstName which has a String value.

  2. Update the getUsers handler to also accept your type that implements QueryParams as an additional parameter:

    private func getUsers(query: Query, completion: @escaping([User]?, RequestError?) -> Void) -> Void {
            completion(users.filter{ ($0.firstName == query.firstName), nil)
    }
    

This now implements a completely type-safe implementation of both the data handling, and the URL encoded query parameters for the following URI!

GET:    /users?firstName=<String>

If you want to include support for optional (non-required) query parameters, you just need to mark them as optional in your declared Swift type, eg:

public struct Query: QueryParams {
    let firstName: String
    let familyName: String?
}

This supports firstName as a required parameter, and familyName as an optional parameter, ie. both of the following are supported:

GET:    /users?firstName=John
GET:    /users?firstName=John&familyName=Doe

Whilst these examples use Strings, you can use a much wider ranger of types. If you have a parameter that should be a number, for example to specify the start and end of a range, or for a page number, then those could be specified using a number type such as Int:

public struct Query: QueryParams {
    let page: Int
}

Working with Query Parameters in KituraKit

At the same time as we added Codable Routing to Kitura, we also released KituraKit which acts as a mirror image to the Codable Routing capabilities for clients wanting to connect to Kitura, whether the clients are iOS apps or other Swift servers. This makes it easier to share and reuse your data types, such as the User type in the example above, between the client and the server.

In tandem with Kitura, KituraKit has also been updated with query parameter support for Codable Routing, allowing query parameter types to be shared between the client and server. The following KituraKit code will connect to a locally running Kitura instance, request the list of Users which match the query and print them:

        guard let client = KituraKit(baseURL: "http://localhost:8080") else {
            print("Error creating KituraKit client")
            return
        }
        let query = Query(firstName: "John", familyName: nil)
        client.get("/user", query: query)) { (users: [User]?, error: RequestError?) in
            guard error == nil else {
                print("Error: \(error!)")
                return
            }
            users!.map{ $0.familyName }.forEach{ print($0) }
        }

What’s next? Is there more to come?

Absolutely. There are currently three areas of improvements that are being looked at and discussed for the Codable Routing APIs:

  • The URL itself
    Currently the URL is specified as a String, which means that it is error prone – a simple typo results in behavior that you did not intend, that cannot be identified at compile time. Additionally, it is possible to specify multiple identifiers inside the URL itself, e.g. /bookstore/:store_id/books/:book_id but this is not well supported with Codable Routing today.
  • Authenticated Users
    Kitura provides a set of Credentials middlewares to allow to you authenticate users making requests, for example using Facebook, Google, GitHub or another source of authentication. However, this is specified only in Kitura, with no mirror-image support in KituraKit for sending authentication information from the client.
  • User Sessions
    Kitura also provides a Session middleware for managing user sessions in Kitura. However, like authentication, this is specified only in Kitura, with no mirror-image support in KituraKit.

If you have input on any of these, or other ideas you’d like to see implemented in Kitura, join the Kitura community on Slack or post your thoughts in Kitura Evolution.






Join the discussion on Slack Learn more at kitura.io Star Kitura on GitHub

Join The Discussion

Your email address will not be published. Required fields are marked *