arrow-ktor icon indicating copy to clipboard operation
arrow-ktor copied to clipboard

Extracting Spine's Arrow helpers to this repository

Open CLOVIS-AI opened this issue 1 year ago • 6 comments

Spine is an alpha-quality library for declaring Ktor endpoints in common code, with no code generation etc:

object Api : RootResource("v1")

object Users : StaticResource("users", parent = Api) {
    class ListParameters(data: ParameterStorage) : Parameters(data) {
        val includeInactive by parameter(default = true)
    }

    val list by get()
        .parameters(::ListParameters)
        .response<User>()

    val create by post()
        .request<User>()
        .response<User>()
}

// Server-side
routing {
    route(Users.create) {
        require(!users.exists(body.email)) { "A user already exists with this email" }
        respond(users.create(body))
    }
}

// Client-side
client.request(Users.create, User("…", "…"))

Of course, I want Spine to play well with Arrow Core. To achieve this, I have a few helpers that look like:

// Server-side
routing {
    route(Users.create) {
        raise { // enter the world of Raise
            ensure(!users.exists(body.email)) { HttpFailure("A user already exists with this email", HttpStatusCode.UnprocessableEntity) }
            …
        }
    }
}

This helper is available standalone without the rest of Spine (dev.opensavvy.spine:arrow-server-independent).

Since you're starting development of a proper Ktor/Arrow integration, I think it would be beneficial that this part was extracted to your repository. Since both libraries are almost equivalent, I can almost replace mine by yours and be done with it.

The main difference is the Raise receiver… Mine is

class HttpFailure(
	val body: Any,
	val code: HttpStatusCode,
	val type: TypeInfo,
)

inline fun <reified Out : Any> HttpFailure(
	body: Out,
	code: HttpStatusCode,
) = HttpFailure(body, code, typeInfo<Out>())

(source)

whereas, this library allows raising the content and the status code separately:

suspend context(Raise<HttpStatusCode>, Raise<OutgoingContent>) PipelineContext<Unit, ApplicationCall>.() -> Unit

I'm curious, why this choice? I'm not sure I understand what benefits this brings, nor how would it be used.

CLOVIS-AI avatar Jun 12 '24 18:06 CLOVIS-AI