https://kotlinlang.org logo
#ktor
Title
# ktor
m

Martin Gaens

02/03/2023, 10:59 PM
How does the continuous compilation work in a multiplatform project using Ktor? I tried running the
run
Gradle task and with it at the same time the
build
task with the
-t
parameter for continuous build and it works, however, it is EXTREMELY slow. The recompilation of the app can take over 2 minutes (with almost no source files) so it is completely unusable for a hot reload type of situation which I'm in. What do people do while developing these web apps? Also, the
jsBrowserDevelopmentRun
task doesn't work because when I run it, the backend isn't running so I can't access the JS files that have been compiled since there is no server to server them to my browser...
r

Robert Jaros

02/04/2023, 9:21 AM
Run two gradle tasks separately (in two consoles).
m

Martin Gaens

02/04/2023, 10:47 AM
@Robert Jaros Which two Gradle tasks?
r

Robert Jaros

02/04/2023, 10:57 AM
One to start your backend (it depends on your project) and one to start webpack dev server for Kotlin/JS (
jsBrowserDevelopmentRun
).
m

Martin Gaens

02/04/2023, 11:01 AM
@Robert Jaros Yeah that is what I'm doing, yet it still doesn't work. I have the
application
Gradle plugin, so the backend can be run by the
run
task. However, the
run
task in this case compiles everything (even the frontend) and all my changes are updated. When I run the frontend separately, by running
jsBrowserDevelopmentRun -t
, it starts a new development server on the next available port (8081 in my case because 8080 is occupied by the server that's running through the
run
task) and it says "Cannot get /". When I access the website
localhost:8080
, I can see the webpage, however, it doesn't get any updates even though after making some changes, I can clearly see Gradle recompiling something in the
jsBrowserDevelopmentRun
task, the changes won't end up in my webpage. It's as if the backend isn't notified of the changes made to the frontend.
r

Robert Jaros

02/04/2023, 11:04 AM
First - there should be a gradle task (
jvmRun
?) which runs your backend without compiling everything.
Second - your generated JS files (compiled from the frontend code) are served by the webpack dev server. You should access them on this second port (you can choose an arbitrary port number in your webpack gradle configuration). You should have an
index.html
file served by the webpack. And to make your frontend app access your backend (API), user webpack proxy configuration.
You can see working configuration in the kvision fullstack example: https://github.com/rjaros/kvision-examples/blob/master/template-fullstack-ktor/build.gradle.kts#L56
m

Martin Gaens

02/04/2023, 11:08 AM
It's a Kotlin Multiplatform project and there's no task named
jvmRun
. The tasks with "jvm" in the name are:
jvmJar
,
jvmMainClasses
,
jvmSourcesJar
,
jvmTestClasses
,
compileKotlinJvm
,
jvmProcessResources
,
jvmTestProcessResources
,
jvmTest
.
r

Robert Jaros

02/04/2023, 11:09 AM
You are right - this should be a custom task.
m

Martin Gaens

02/04/2023, 11:10 AM
Also my project doesn't have any
index.html
file, the frontend is written in React. It's the project IntelliJ IDEA generates when you select "Kotlin Multiplatform -> Fullstack Web application" and leave everything default.
r

Robert Jaros

02/04/2023, 11:12 AM
create one and put in the frontend resources directory - it will be used by webpack
m

Martin Gaens

02/04/2023, 11:14 AM
Thank you for the example, I'll definitely look into it, but I'd still like to know why is it so complicated to setup live reload in a default Kotlin Multiplatform project generated by IDEA.
r

Robert Jaros

02/04/2023, 11:18 AM
What can I say. I've created a project just now and IMHO it's definitely not very productive configuration ;-)
m

Martin Gaens

02/04/2023, 11:20 AM
Okay, the non-existent HTML file at the resources of
jsMain
kind of was the culprit. However, the thing is, there was no need of an
index.html
in the first place, because the
index.html
was declared in Kotlin code inside the server's backend like this:
Copy code
fun HTML.index() {
    head {
        title("Hello from Ktor!")
    }
    body {
        div {
            +"Hello from Ktor"
        }
        div {
            id = "root"
        }
        script(src = "/static/vut-subject-market-kotlin.js") {}
    }
}

fun main() {
    embeddedServer(Netty, port = 8080, host = "127.0.0.1", module = Application::application).start(wait = true)
}

fun Application.application() {
    routing {
        get("/") {
            call.respondHtml(HttpStatusCode.OK, HTML::index)
        }
        static("/static") {
            resources()
        }
    }
}
And in the
jsBrowserDevelopmentRun
task, the
index.html
file is used, and not this code in the backend.
r

Robert Jaros

02/04/2023, 11:22 AM
And OK. That's the point. When developing your app this code is irrelevant.
When you are developing fontend, webpack takes care of serving your code, hot reload and so on.
Your ktor is just for API. You can have independant ktor hot reload as well.
m

Martin Gaens

02/04/2023, 11:28 AM
Oh, okay, I get it now. But then the question is, let's say I want to be working on both the backend and frontend simultaneously. Ktor server offers a hot reload which involves running the
build -t
task, which is in my large multiplatform codebase VERY slow. What are my options for developing the backend and hot reloading with Ktor then?
r

Robert Jaros

02/04/2023, 11:29 AM
Run
-t compileKotlinJvm
task (another console)
it will compile just jvm code and make ktor hot reload fast
Three tasks running in three terminals is my typical development workflow.
(even though I'm using spring boot)
m

Martin Gaens

02/04/2023, 11:33 AM
Thank you! I'm almost there, but not quite! 😕 The problem now is, backend (with the recompiled changes) runs at
localhost:8080
and frontend runs at
localhost:8081
and tries to find the
index.html
file in the resources and is unable to do so.
r

Robert Jaros

02/04/2023, 11:36 AM
Where have you put the
index.html
file?
m

Martin Gaens

02/04/2023, 11:36 AM
I have removed it because I have this code on my backend which I want to serve as my `index.html`:
Copy code
fun HTML.index() {
    head {
        title("Hello from Ktor!")
    }
    body {
        div {
            +"Hello from Ktor!"
        }
        div {
            id = "root"
        }
        script(src = "/static/vut-subject-market-kotlin.js") {}
    }
}
r

Robert Jaros

02/04/2023, 11:37 AM
This won't work. You need to open js file served by webpack.
You could try proxy configuration like this:
Copy code
`proxy = mutableMapOf("/*", to "<http://localhost:8080>")`
But I'm not sure it will work
no, it won't work correctly because ktor's html code uses
/static/...js
file
so even if proxy works, the webpack js file won't be loaded
I don't know if it's possible to work like this without separate
index.html
file
Perhaps with some advanced webpack configuration
Can't help with that
m

Martin Gaens

02/04/2023, 11:44 AM
Then why would the JetBrains team create such a default project? I don't understand it...
r

Robert Jaros

02/04/2023, 11:45 AM
It's just an example.
unfortunately there are no established procedures here
kvision template project takes care of lots of details, but it's just my experience (I'm the author)
m

Martin Gaens

02/04/2023, 12:00 PM
Thank you very much for your in-depth explanations of everything, I'll check out KVision then.
It's just that for my level of Gradle understanding, it's a little overwhelming
Sorry to bother you again, @Robert Jaros, but I still can't get my head around how to get the compilation of everything together. When you develop your apps, you have two separate tabs open? One for frontend, one for backend? Because they seem to be running on two different ports...
r

Robert Jaros

02/04/2023, 12:14 PM
No.
I'm using only one tab in my browser - the one from webpack.
When the browser calls the backend it's being proxied by the webpack to the backend app (which is executed with a second task).
So there are two apps with two different ports. But only one is opened in the browser.
m

Martin Gaens

02/04/2023, 12:17 PM
There's something wrong here in that case...
r

Robert Jaros

02/04/2023, 12:18 PM
You are missing proxy configuration
m

Martin Gaens

02/04/2023, 12:18 PM
jsDevelopmentRun -t
and
run
are running, yet I can't access the backend
Can I find an example of a proxy configuration in the KVision repo?
r

Robert Jaros

02/04/2023, 12:19 PM
It's that
proxy = mutableMapOf(...)
option in build.gradle.kts
I've already pasted a link an hour ago.
you can add
proxy = mutableMapOf("/test" to "<http://localhost:8080>")
or use some wildcards
In kvision only "/kv/*` calls are proxied
But you can configure as you need in your app.
m

Martin Gaens

02/04/2023, 12:21 PM
Oh, okay, perfect! Thank you very much, I'll try it out now. Do you have any learning resources to recommend, so I won't be as lost as I am now in this?
r

Robert Jaros

02/04/2023, 12:22 PM
just note kvision has
frontend
and
backend
instead of
js
and
jvm
task names are different
m

Martin Gaens

02/04/2023, 12:28 PM
Perfect, awesome, I got it to work. I'll definitely check out KVision.
7 Views