How does the continuous compilation work in a mult...
# ktor
m
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
Run two gradle tasks separately (in two consoles).
m
@Robert Jaros Which two Gradle tasks?
r
One to start your backend (it depends on your project) and one to start webpack dev server for Kotlin/JS (
jsBrowserDevelopmentRun
).
m
@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
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
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
You are right - this should be a custom task.
m
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
create one and put in the frontend resources directory - it will be used by webpack
m
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
What can I say. I've created a project just now and IMHO it's definitely not very productive configuration ;-)
m
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
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
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
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
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
Where have you put the
index.html
file?
m
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
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
Then why would the JetBrains team create such a default project? I don't understand it...
r
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
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
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
There's something wrong here in that case...
r
You are missing proxy configuration
m
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
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
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
just note kvision has
frontend
and
backend
instead of
js
and
jvm
task names are different
m
Perfect, awesome, I got it to work. I'll definitely check out KVision.