https://kotlinlang.org logo
#http4k
Title
# http4k
s

Sean Abbott

08/12/2021, 6:16 PM
Hello, I'm new to this and having a little trouble wrapping my head around a basic concept. So I have the generated ExampleContractRoute,
Copy code
object ExampleContractRoute {
    // this specifies the route contract, including examples of the input and output body objects - they will
    // get exploded into JSON schema in the OpenAPI docs
    private val spec = "/echo" meta {
        summary = "echoes the name and message sent to it"
        receiving(nameAndMessageLens to NameAndMessage("jim", "hello!"))
        returning(OK, nameAndMessageLens to NameAndMessage("jim", "hello!"))
    } bindContract POST

    // note that because we don't have any dynamic parameters, we can use a HttpHandler instance instead of a function
    private val echo: HttpHandler = { request: Request ->
        val received: NameAndMessage = nameAndMessageLens(request)
        Response(OK).with(nameAndMessageLens of received)
    }

    operator fun invoke(): ContractRoute = spec to echo
}
which is being called inside contract in my app function
Copy code
"/" bind contract {
        renderer = OpenApi3(ApiInfo("Test API", "v1.0"), Jackson)

        // Return Swagger API definition under /swagger.json
        descriptionPath = "/swagger.json"

        // Add contract routes
        routes += ExampleContractRoute()
        routes += HealthRoute()
    },
and I'm trying to add a nested route to the ExampleContract route, like
/echo/excited
just as a toy example What would be idiomatic way to do that?
I think I have a handle on how to look for parameters, and I think I could do nested routes without openapi, but I"m not groking ContractRoutes I guess.
AFAICT none of the examples, deeper or otherwise, show nesting for openapi contracts. If I've missed it please let me know where
d

dave

08/12/2021, 6:56 PM
You haven't missed anything. Contract routes cannot be nested inside each other. You can mix and match the styles and put a contract inside another standard routes block however (and bind all routes to a common prefix)
s

Sean Abbott

08/12/2021, 7:04 PM
Ok, so if I had something like
Copy code
GET /account/{id}
POST /account/{id}/task/{taskType}
And wanted both to be reflect in the swagger docs, could I just do a ContractRoute function for both of those? like
Copy code
fun acountRoute(): ContractRoute {
    val spec = "/account" / <http://Path.int|Path.int>().of("accountId") meta {
        summary = "retuns accoutn details"
    } bindContract GET

    return spec to ::greet
}
fun taskRoute(): ContractRote {
    val spec = "/account" / <http://Path.int|Path.int>().of("accountId") / task / Path.string().of("taskType") meta {
...
}

// (elsewhere)
"/api" bind contract {
  ...
   routes += accountRoute()
   route += taskRoute()
}
Sorry, I'm also new to writing REST apis in general
d

dave

08/12/2021, 7:05 PM
Don't apologize! We're here to help. 🙃
🙏 2
Anyway - yes - you would probably put them next to each other. But in your example they would be on /api/account/... (Because of the routes block)
s

Sean Abbott

08/12/2021, 7:07 PM
Right, and that's fine. I just want to make sure that defining /account/{id}/task/{taskType} separately from (rather than nesting under) /account/{id} won't block things
I was about to try this, but also wanted to ask about the idiomatic way to do it It works doesn't always mean it makes sense. 🙂
d

dave

08/12/2021, 7:08 PM
Yep - the routes blocks all work the same under the covers - the route tree gets flattened and the first match in the list wins
s

Sean Abbott

08/12/2021, 7:09 PM
Ok, cool. Future proofing question so I can take notes: So if I think a route should be working, but it isn't, what's the best way to debug the route matching?
(Thank you so much for the quick responses!)
d

dave

08/12/2021, 7:10 PM
Np. It's easy when the questions are easy. 🙃