Skip to Content
Mélodium 0.10.2 is now available!
DocsExamplesJavaScript Transform

JavaScript Transform

Source: 05_javascript_transform

An HTTP server that applies a JavaScript function to every incoming JSON payload and returns the transformed result. The JS engine is declared as a model so the function is compiled once at startup and reused across all concurrent requests.

Running

melodium run 05_javascript_transform/Compo.toml
$ curl -X POST http://127.0.0.1:8080/transform \ -H "Content-Type: application/json" \ -d '{"name":"Alice","score":82}' {"name":"Alice","score":82,"grade":"B","note":"processed by Mélodium JS engine"}

How it works

The Transformer model embeds the JavaScript function directly in the source:

model Transformer() : JavaScriptEngine { code = " function transform(input) { var grade = 'F'; if (input.score >= 90) grade = 'A'; return { name: input.name, score: input.score, grade: grade, … }; } " }

The JS engine compiles this function once when the model is instantiated. startup starts the HttpServer and registers the /transform connection:

treatment main(const port: u16 = 8080) model server: HttpServer(host=|from_ipv4(|localhost_ipv4()), port=port) { startup() start[http_server=server]() logReady: logInfoMessage(label="server", message="JavaScript transform service ready") startup.trigger -> start.trigger startup.trigger -> logReady.trigger connection[http_server=server](method=|post(), route="/transform") status: emit<HttpStatus>(value=|ok()) headers: emit<StringMap>(value=|map([])) bodyTrigger: trigger<byte>() connection.data -> bodyTrigger.stream,start --> status.trigger,emit -> connection.status bodyTrigger.start --------> headers.trigger,emit -> connection.headers jsTransform() connection.data -> jsTransform.data,data -> connection.data }

Every request then calls process[engine=engine](code="transform(value)") which invokes the compiled function with the parsed JSON value.

main treatment diagram See in Compositeur Studio

The jsTransform sub-treatment

treatment jsTransform() model engine: Transformer() input data: Stream<byte> output data: Stream<byte> { decode() toJson() unwrap<Json>() process[engine=engine](code="transform(value)") unwrapOr<Json>(default=|null()) toString<Json>() encode() Self.data -> decode.data,text -> toJson.text,json -> unwrap.option,value -> process.value,result -> unwrapOr.option,value -> toString.value,into -> encode.text,data -> Self.data }

Two different unwrap strategies are used deliberately:

The jsTransform sub-treatment declares its own model engine: Transformer(). This model is scoped to that declaration and lives for the entire program execution, so the same instance is reused across every call to jsTransform, and the function is not recompiled per request.

Dependencies

[dependencies] std = "0.10.2" # core flows, logging, data structures http = "0.10.2" # HTTP server and client net = "0.10.2" # IP address helpers json = "0.10.2" # JSON parsing and serialisation encoding = "0.10.2" # UTF-8 encode / decode javascript = "0.10.2" # embedded JavaScript engine