I have a project that consists of ktor backend and...
# compose-web
g
I have a project that consists of ktor backend and Compose Web frontent. Because I basically glued it from Ktor wizard and Compose wizard, the frontend uses its own server, on port 8080 (btw - I have no idea where it is set!), and the backend runs on port 8081. All fine, until I tried to setup authorization, which causes CORS problems that I'm struggling with for 3 days. Would it be instead possible to serve Compose part from Ktor server, so everything runs on the same port and save me from CORS pain?
f
The cors issue would only apply if you're using Compose for web (it only happens on Browsers) you can solve it by adding a CORS header to all your backend response. You can install the Ktor Plugin CORS. TL;DR: Install:
Copy code
implementation("io.ktor:ktor-server-cors:$ktor_version")
Install the plugin and allow any host to access it
Copy code
embeddedServer(Netty, port = 8080) {
  install(CORS) {
      anyHost()
  }
}
g
ohaho - you thought it would be so easy? Nope, with anyhost it will give:
Access to fetch at 'http://localhost:8081/user/images/1' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
but anyway, my current CORS config is:
Copy code
install(CORS) {
    // albo:
    anyHost() // Allow requests from any origin - you can restrict it later
    // albo (jeśli allowCredentials = true), to musimy podać hosta:
    allowHost("${WebUICfg.HOST}:${WebUICfg.PORT}", schemes = listOf("http", "https"))

    allowMethod(HttpMethod.Get) // Allow specific methods or use HttpMethod.Any for all
    allowMethod(<http://HttpMethod.Post|HttpMethod.Post>)
    allowMethod(HttpMethod.Put)
    allowMethod(HttpMethod.Options) // Allow OPTIONS requests (for preflight)
    allowMethod(HttpMethod.Delete)
    allowMethod(HttpMethod.Patch)

    allowHeader(HttpHeaders.Authorization) // Allow any headers you need
    allowHeader(HttpHeaders.ContentType) // Allow the Content-Type header
    allowHeader("MyCustomHeader")

    allowCredentials = true // This is important to allow cookies to be sent
    allowSameOrigin = true // Allow the SameSite lax policy by setting the proper CORS headers

}
And yes - it IS compose for web!
a
Write in your backend module a simple gradle task to compile your compose client and place into the build resources of the backend module. Then use this new task instead of
run
task. Kind of (assuming that the client module is named
app
): 1) tasks { val resourcesMain by lazy { project.layout.buildDirectory.dir("resources/main").get().asFile } register("runWithComposeApp") { dependsOn("appjsBrowserDevelopmentExecutableDistribution") doLast { copy { from(File(rootDir, "app/build/dist/js/developmentExecutable")) into(resourcesMain) } } finalizedBy("run") } } 2) add to your Ktor backend this singlePageApplication route routing { singlePageApplication { defaultPage = "index.html" useResources = true filesPath = "" } }
👍 1
g
I'll try this, thanks!
Copy code
> Configure project :composeApp
w: 'java' Gradle plugin is not compatible with 'org.jetbrains.kotlin.multiplatform' plugin.

Consider adding a new subproject with 'java' plugin where the KMP project is added as a dependency.

w: 'application' (also applies 'java' plugin) Gradle plugin is not compatible with 'org.jetbrains.kotlin.multiplatform' plugin.

Consider adding a new subproject with 'application' plugin where the KMP project is added as a dependency.
😄