Hi all, I try to figure out why I get this error ...
# ktor
f
Hi all, I try to figure out why I get this error whenI try to wrap my Authdata in kotlin Result like this Result<AuthDataResponse>
Copy code
route(path = "/login") {
        post {
            val params = call.receive<SignInParams>()
            val result = repository.signIn(params = params)
            call.respond(
                message = result
            )
        }
    }
This is the stacktrace
Copy code
2023-09-03 20:28:51.996 [eventLoopGroupProxy-4-1] ERROR ktor.application - Unhandled: POST - /login
kotlinx.serialization.SerializationException: Serializer for class 'Result' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.

	at kotlinx.serialization.internal.Platform_commonKt.serializerNotRegistered(Platform.common.kt:92)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:285)
	at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
	at io.ktor.serialization.kotlinx.SerializerLookupKt.guessSerializer(SerializerLookup.kt:50)
	at io.ktor.serialization.kotlinx.KotlinxSerializationConverter.serializeNullable(KotlinxSerializationConverter.kt:66)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$2.invokeSuspend(ResponseConverter.kt:66)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$2.invoke(ResponseConverter.kt)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$2.invoke(ResponseConverter.kt)
	at io.ktor.server.application.OnCallRespondContext.transformBody(KtorCallContexts.kt:86)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invokeSuspend(ResponseConverter.kt:39)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invoke(ResponseConverter.kt)
	at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invoke(ResponseConverter.kt)
	at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invokeSuspend(PluginBuilder.kt:215)
	at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invoke(PluginBuilder.kt)
	at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invoke(PluginBuilder.kt)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invokeSuspend(PluginBuilder.kt:198)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invoke(PluginBuilder.kt)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invoke(PluginBuilder.kt)
	at io.ktor.util.debug.ContextUtilsKt$addToContextInDebugMode$2.invokeSuspend(ContextUtils.kt:33)
	at io.ktor.util.debug.ContextUtilsKt$addToContextInDebugMode$2.invoke(ContextUtils.kt)
	at io.ktor.util.debug.ContextUtilsKt$addToContextInDebugMode$2.invoke(ContextUtils.kt)
	at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:169)
	at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
	at io.ktor.util.debug.ContextUtilsKt.addToContextInDebugMode(ContextUtils.kt:33)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invokeSuspend(PluginBuilder.kt:194)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invoke(PluginBuilder.kt)
	at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invoke(PluginBuilder.kt)
	at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:120)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:78)
	at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:98)
	at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77)
	at com.talamyd.auth.AuthRouteKt$authRouting$2$1.invokeSuspend(AuthRoute.kt:80)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask$$$capture(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.ktor.server.netty.EventLoopGroupProxy$Companion.create$lambda$1$lambda$0(NettyApplicationEngine.kt:296)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
Any help is appreciated
b
Error message says:
Copy code
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
f
Yes but how can I make Kotlin Result class serializable? My AuthDataResponse class is Serializable
Scherm­afbeelding 2023-09-03 om 20.38.02.png
b
you can do result.get() instead of directly using Result
f
If I do that in my Server can I use this in my Client Result<AuthResponseData>
Copy code
suspend fun signIn(request: SignInRequest): Result<AuthResponseData> = <http://client.post|client.post> {
        endPoint("login")
        setBody(request)
    }.body()
b
Copy code
suspend fun signIn(request: SignInRequest): AuthResponseData = <http://client.post|client.post> {
        endPoint("login")
        setBody(request)
    }.body()
You can directly deserialize to AuthResponseData
You dont need to use Result
f
I understand. That was my initial implementation. But I read some article stating/encouraging to use Result or Arrow for a more Functional Programming approach. But it looks like my skillset isn’t ready for that yet 😅. Thanks for your help!
🙏 1
b
Im not familiar with FP too, but in this case you dont use leverage of
Result
wrapping, If you are intended to use benefits of result then you can use
Copy code
route(path = "/login") {
        post {
            val params = call.receive<SignInParams>()
            val result = repository.signIn(params = params)
            call.respond(
                message = result.getOrNull()
            )
        }
    }
🙌 1
c
I am not sure if
Result
is intended to be transferred over the internet or should be used as a wrapper for actions that may be successful or not. Normally what I do is I use serializable data classes that are my data transfer objects (DTOs) and send those over the internet. Any client (and server) that executes an action that may be successful or may fail (typically in the repositories, e.g. a
GET
request) returns a
Result
, either with or without data (
Unit
in that case). If it fails, the result contains the error that occurred (e.g. timeout, server unreachable, internal server error etc.) and I can handle it accordingly. If it succeeds, I get the data / I am sure that the action was executed successful and I can proceed. Please note that in your example (server side), a failed result should be represented with the according HTTP status code in the response, typically a 4XX, rather than a successful response (2XX) with an error in the body. Latter is common (bad) practice I have seen in many applications that I do not recommend personally.
f
Thank you very much Christos