What kinda routes can I use with Hummingbird?

This article is part of a series.

I mean…all of them? So this is all based on walking through the Router Guide, but I wanted one project that served up the sampler.

Here is a buildRouter function with a smorgasbord of examples.

In context at: https://github.com/carlynorama/HummingbirdExamples/tree/main/02_basic_responses

buildRouter

func buildRouter() -> Router<AppRequestContext> {
    let router = Router(context: AppRequestContext.self)

    router.addMiddleware {
        // logging middleware
        LogRequestsMiddleware(.info)
        //serves the static files in public folder by default.
        FileMiddleware(searchForIndexHtml: true)
    }

    // SIMPLE ROUTING

    // These three types have been given ResponseGenerator status by Hummingbird
    // - HTTPResponseStatus
    // - String
    // - Optional

    // The closure takes in a request and a context and returns
    // some ResponseGenerator

    router.get("/ping") { _, _ -> HTTPResponse.Status in
        return .ok
    }

    router.get("/hello") { request, _ -> String in
        return "Well Hello!\n\n\(request)"
    }

    // full call
    router.on("/goodbye", method: .get) { request, context in
        return "Ciao!"
    }

    //---- WILDCARDS
    // https://docs.hummingbird.codes/2.0/documentation/hummingbird/routerguide#Wildcards
    router.get("/files/*") { request, _ in
        return request.uri.description
    }

    //note: ** is 1 or more, not zero or more. (2025/08)
    router.get("/doublewild/**") { _, context in
        return context.parameters.getCatchAll().joined(separator: "/")
    }

    //---- Parameter Capture

    // //https://docs.hummingbird.codes/2.0/documentation/hummingbird/routerguide#Parameter-Capture
    router.get("/user/:id") { _, context in
        guard let id = context.parameters.get("id", as: Int.self) else {
            throw HTTPError(.badRequest)
        }
        return "\(id)"
    }

    router.get("/user/:id/:name") { _, context in
        guard let id = context.parameters.get("id", as: Int.self) else {
            throw HTTPError(.badRequest)
        }
        guard let name = context.parameters.get("name", as: String.self) else {
            throw HTTPError(.badRequest)
        }
        return "found \(name) for \(id)"
    }

    router.get("/img/{image}.jpg") { _, context in
        guard let imageName = context.parameters.get("image") else {
            throw HTTPError(.badRequest)
        }
        switch imageName {
        case "bunny":
            return " \\\n  \\ /\\\n  ( )\n.( o )."
        //    \
        //     \ /\
        //     ( )
        //   .( o ).
        default: return "this is a \(imageName) "
        }
    }

    router.get("/query") { request, _ in
        // extract parameter from URL of form /query?id={userId}&message={message}
        guard let id = request.uri.queryParameters.get("id", as: Double.self) else {
            throw HTTPError(.badRequest, message: "id?")
        }
        guard let message = request.uri.queryParameters.get("message", as: String.self) else {
            throw HTTPError(.badRequest, message: "message?")
        }
        return "tell \(id) I said \"\(message)\""
    }

    // GROUPS

    //if one has a logical group one can batch them together.
    router.group("/circus")
        .get(use: { _, _ in "if I had a list of circus acts I would show a list of circus acts" })

        .get("peanuts") { _, _ in "yummy peanuts" }
        .group("clowns")
        .get { _, _ in "if I had a list of clowns I would show a list of clowns" }
        .get(
            "{id}",
            use: { _, context in
                "that's the clown called \(context.parameters.get("id") ?? "no ID")!"
            })

    struct MiniCodable: Decodable, ResponseEncodable {
        let number: Int
        let phrase: String?
    }

    //the default encoder is JSON
    router.get("/encodable") { _, _ -> MiniCodable in
        return MiniCodable(number: 5, phrase: "hello!")
    }

    router.group("/decodable")
        .get { _, _ in "this was a GET. Please submit some info using POST" }

        //curl -i -X POST localhost:8080/decodable/default -d'{"number":12,"phrase":"are you going to go my way"}'
        .post("default") { request, context in
            let decoded = try await request.decode(as: MiniCodable.self, context: context)
            print("DECODED: \(decoded)")
            return "DECODED: \(decoded)"  //also consider an HTTPResponseStatus
        }
        //curl -i -X POST 'http://localhost:8080/decodable/form' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'number=36&phrase=message%20string'
        .post("form") { request, context in
            print(request.headers)
            let decoded = try await URLEncodedFormDecoder().decode(
                MiniCodable.self, from: request, context: context)
            print("DECODED: \(decoded)")
            return "DECODED: \(decoded)"
        }

    return router
}

Testing Script

To test them all here’s a build script with curl calls.

#!/bin/sh

MY_PORT="8080"

separate () {
sleep 2s
echo -e "\n---------------------\n" 
}

curl -i "http://localhost:$MY_PORT/ping"
separate 
curl -i "http://localhost:$MY_PORT/hello"
separate  
curl -i "http://localhost:$MY_PORT/goodbye"

separate 
curl -i "http://localhost:$MY_PORT/files/putonethingafter"
separate 
curl -i "http://localhost:$MY_PORT/doublewild/put/anything/I/want"

separate
curl -i "http://localhost:$MY_PORT/user/8675309"
separate
curl -i "http://localhost:$MY_PORT/user/8675309/Jenny"

separate
curl -i "http://localhost:$MY_PORT/img/non-bunny.jpg"
separate
curl -i "http://localhost:$MY_PORT/img/bunny.jpg"

separate
curl -i "http://localhost:$MY_PORT/query?id=35.53&message=hello%20with%20spaces"

separate
curl -i "http://localhost:$MY_PORT/circus"
separate
curl -i "http://localhost:$MY_PORT/circus/peanuts"
separate
curl -i "http://localhost:$MY_PORT/circus/clowns"
separate
curl -i "http://localhost:$MY_PORT/circus/clowns/anythingcanbeanidforthis"

separate 
curl -i "http://localhost:$MY_PORT/encodable"

separate 
curl -i -X POST localhost:$MY_PORT/decodable/default -d'{"number":12,"phrase":"are you going to go my way"}'
separate 
curl -i -X POST localhost:$MY_PORT/decodable/default -d'{"number":14}'

separate 
curl -i -X POST 'http://localhost:8080/decodable/form' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'number=36&phrase=message%20string'
separate 
curl -i -X POST 'http://localhost:8080/decodable/form' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'number=36&phrase'

This article is part of a series.