Hey everyone. Is anyone using <https://ktor.io/doc...
# ktor
s
Hey everyone. Is anyone using https://ktor.io/docs/type-safe-request.html for type safe requests with Ktor clients. Anyone have any good naming conventions / patterns they've been using they'd be happy to share? I'm thinking of just prefixing all my resource classes with
Endpoint
but not sure I'm happy with that
s
Generally they would be appropriately namespaced anyhow
s
Do you have a strategy you can share?
r
I have a
resource
package with resources (`data class`es and `object`s annotated with
@Resource
) and a
route
package with the route definitions. The
MyResource
files contain everything data: the resource, the
@Serializable
payload if any, the
@Serializable
response if any, and OpenAPI definitions using
tegral-openapi-ktor-resources
. The
MyRoute
files contain a single function:
fun Routing.setupMyRoute()
which starts with some dependency injection using Koin and then usually a single endpoint definition (rarely more than one, sometimes I can have multiple verbs for the exact same resource). I have no issue having separate resources for the same path, for exemple I could have a
@Resource("/v1/user/{userId}") GetUserResource(val userId: String)
and a
@Resource("/v1/user/{userId}") DeleteUserResource
with a
GetUserRoute
implementing the retrieval of a user and
DeleteUserRoute
implementing the deletion of a user: I prefer more files with less code
Another thing to note: I absolutely never use resources hierarchy, it makes the code a lot harder to read and understand while not providing any benefit. Every
@Resource
annotation references a full path.
On versioning: right now in my
resource
package, I have
v0
,
v1
,
v2
packages (
v0
is old unversioned endpoints), but not in my
route
package. For example, I could have an old
v0/GetUserResource
and a more recent
v2/GetUserResource
and implement both in a single
GetUserRoute
file, usually with an internal redirection using something like this:
Copy code
suspend inline fun <reified T : Any> ApplicationCall.redirectInternally(resource: T) {

    val path = application.href(resource)

    val connectionPoint = object : RequestConnectionPoint by request.local {
        override val uri get() = path
    }

    val request = object : ApplicationRequest by request {
        override val local get() = connectionPoint
    }

    val call = object : ApplicationCall by this {
        override val request get() = request
    }

    application.execute(context = call, subject = Unit)

}
s
That's really interesting, not thought about the hierarchy, I suppose it doesn't give much benefit, though I do like the way it models the api's structure, Thanks for your advice!
s
Sorry @Sam busy day - I was more meaning you dont need to explicitly add prefixes as you can isolate the reference via standard java/kotlin namespaces anyhow so you know the context
r
I'm currently working on refactoring an old Ktor project that used Location hierarchy (Location were how Resource were named in Ktor 1). It's a nightmare. Especially with inexperienced developers. What I described above is what I came to from years of experience using Ktor
👍 2
I have a file with hundreds of lines of resources declared inside other resources… Takes minutes to find what path a resource points to…
s
*is a very opinionated pita when it comes to routing / versioning etc which is often impractical when dealing with existing project / refactoring
r
Some paths are declared multiple times because one developer didn't find the resource another developer already defined in that hell, because it's virtually impossible to find something in there.
There is cool architecture and there is real world architecture. In the real world developers are generally bad.
👍 1
😆 1
s
Yeah this is kinda why I was thinking of suffixing my resources just so that I don't end up with these massive trees and can split the resources hierarchies between different files with a consistent naming convention
r
I personally would use Resource hierarchy in personal projects, with resources related to a same topic defined in the same file, but I'm never doing that at work anymore 😄
🙂 1
s
I’m a fan of I guess HATEOS style referencing but no client side URI construction aside ‘root’ URL and documents links as full URL’s works exceptionally well to the point you don’t generally need versioning unless there are significant application changes - it all falls to bits when you want client side persistence etc ( and yes have actually implemented it in real world systems and it worked extremely well )
But outside the scope of @Sam’s original question 🙂
Roy Fieldings dissertation has some great bits that work well in the real world
also some that don’t 🙂
@ribesg I take the the redirection is for resources that are common between versions?
r
I didn't choose to do versioning in this case, endpoints starting with
/v1/
and
/v2/
just exist in this project. I'm rewriting the entire thing in a better, modern way, but I can't change paths or functionality. I just need to set a standard for our Kotlin APIs and spread it, and hopefully our developers will know what to do by just mimicking what exists in the futur
👍 1
@swishy yes. In one instance, I have a
v1
endpoint which matches a similar
v2
endpoint, but the
v2
endpoint takes an additional, mandatory
locale
parameter, so the
v1
redirects to
v2
with a default value
👍 1
s
*has found better to reinforce such on every mention of a new route is a good idea 😜