For Compose Web (WASM), is it possible to download...
# compose-web
h
For Compose Web (WASM), is it possible to download (save) a JSON file from another host (trusted), then read the file and process it? Does that get around CORS? I am thinking about making an app that needs to access JSON data from another domain (trusted). I get around that in JavaScript with
jsonp
basically, or something like
<http://window.my|window.my>_data="something"
to get the data currently. Is there a similar workaroudn mechanism I can use in Compose WASM?
m
If you're in the browser, I don't see how you would workaround CORS?
I get around that in JavaScript with
jsonp
basically, or something like
<http://window.my|window.my>_data="something"
to get the data currently.
Not sure what you mean by that. You would still need a HTTP client to do the download?
c
There’s really no way for a client app to go around CORS, it’s up to the server to allow itself to be accessed cross-origin. Whether its the server sending the right headers, or exposing JSONP callbacks, CORS is ultimately about protecting the server, so it’s up to the server to make it work. But once the server is ready to share content cross-origin, it’s no more difficult to access that content than your own APIs. #ktor client works in JS and allows you to fetch any kind of content you need.
JSONP is doable basically in the same way you’d do it from normal JS. Use Browser APIs as exposed through the Kotlin stdlib to fetch an endpoint as a script, which sets a variable on the window scope. Kotlin can then read that variable. Its a terrible hack and not very secure, so you should probably avoid it if possible. A better (though slightly more involved) solution would be to proxy that other endpoint through your own backend. You can set up a AWS lambda for example, which your Compose Web app calls, and that lambda calls the API on another domain and returns the result. In this way, you avoid the cross-origin browser policy altogether
h
You can load any remote JS file and it'll execute (think Google Analytics, Facebook, etc).
jsonp
means that you wrap the json in a function call
my_function({"key": "value"})
in the response. My current code just sets a global variable
<http://window.my|window.my>_data = "something"
👀 1
today i learned 1
Use Browser APIs as exposed through the Kotlin stdlib to fetch an endpoint as a script, which sets a variable on the window scope.
This is pretty interesting. It would only work using the methods I described above (jsonp and other javascript execution), but would not work for other CORS requests. think smart
c
Basically, the Ktor HTTP client wraps the
fetch
API. The Fetch and XMLHttpRequest browser APIs both respect CORS, so any calls made through those HTTP clients (including Ktor) need proper CORS headers to be sent by the server. JSONP uses the browser’s mechanism for downloading and running JS code as a proxy for fetching data. This API ignores CORS restrictions, but requires a painful and insecure method of getting usable data from a server. Like with CORS, it requires cooperation from the server you’re trying to access. If a server offers a JSONP endpoint, then they might also offer ways to call it cross-origin instead, and if so that would be a better option. Anything Kotin/JS is doing is ultimately just compiled down to JS code, using the same browser APIs and fighting the same limitations you’d get if you wrote JS code by hand. There’s no fundamental difference to how browser APIs work when using Kotlin/JS, so the same kinds of solutions you’d find for a normal JS web app will also work for Kotlin apps. I’m not 100% certain about the difference with WASM, but from this docs page it looks like WASM interops with JS and browser APIs the same as with Kotlin/JS targets
👀 1
h
Totally sounds like it'd work 😲 think smart
For testing the Desktop version I just used okhttp + gson to download the javascript, strip the wrapper, and parse the object. Will report regarding the wasm/js version if I get that going. As you mentioned, seems totally do-able and 🤯
It works! Pretty easy. You can only pass primitives across the JS/WASM border, but I pass the JSON and then parse using
org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1-wasm0
inside the WASM code to an object. HTML:
Copy code
<script src="<https://something/report-data.js>"></script>
Content of ☝️ is something like:
Copy code
window.reportData = {"key": "value"}
Javascript:
Copy code
function getJson() {
  return JSON.stringify(window.reportData)
}
Kotlin WASM Code:
Copy code
// Use external functions to call JS functions defined in global scope
external fun getJson(): String

...

// This is the parsed version of it in WASM that I can now iterate over
val jsonElement = Json { }.parseToJsonElement(getJson())
🎉 1
Typesafe Deserialization with kotlinx-serialization-json 🎉
Copy code
@Serializable
data class MyReportData(val key: String)


val reportData = json.decodeFromString(MyReportData.serializer(), getJson())