Skip to Content
Mélodium 0.10.3 is now available!
DocsExamples05. HTTP Client

HTTP Client

Source: tutorial/05_http_client See in Playground

Fetches one post from a public test API (jsonplaceholder.typicode.com), re-serialises its JSON body, and writes it to a file.

Running

cd tutorial/05_http_client melodium run Compo.toml --post_id 1

The request URL is built from post_id with |format. The example logs when the request completes, and separately handles a technical failure (DNS, connection, timeout) versus a body that fails to parse, then writes the parsed and re-serialised JSON body to post.txt.

Optional: add --api-report and an API token (MELODIUM_API_TOKEN) to see this run’s full trace on Cadence.CI.

How it works

This example uses no models: get needs no client model for a one-off request. A connection-reusing HttpClient model is used instead in other examples, once a connection should stay open across requests.

Data flow

Firing the request

get(url=...) fires on startup.trigger and streams the response body through data, independently of status/completed/failed/error, which each fire once. The URL is built with |format, called directly as a value, with no ports or connections involved:

fetch: get(url=|format("https://jsonplaceholder.typicode.com/posts/{id}", |map([|entry("id", post_id)]))) startup.trigger -> fetch.trigger logOk: logInfoMessage(label="http", message="request completed") fetch.completed -> logOk.trigger

Technical failure versus data failure

fetch.failed/fetch.error fire when the request itself could not be completed: DNS, connection, or timeout issues. A response that arrives successfully but contains invalid JSON is a completely separate, later failure mode, handled below when toJson’s Option comes back none. The two should not be conflated:

netFailed: logErrorMessage(label="http", message="request failed technically") netError: logError(label="http") fetch.failed -> netFailed.trigger fetch.error -> netError.message

Decoding and re-serialising the body

The raw byte body is turned into a Stream<string> with decode, parsed with toJson, unwrapped with unwrapOr, and re-serialised. This round-trip is a good way to confirm a response really is valid JSON without changing its meaning:

decode: decode() fetch.data -> decode.data parsed: toJson() body: unwrapOr<Json>(default=|null()) asText: toString<Json>() write: writeTextLocal(path=output) logDone: logInfoMessage(label="http", message="response written to file") decode.text -> parsed.text,json -> body.option,value -> asText.value,into -> write.text write.finished -> logDone.trigger

Dependencies

[dependencies] std = "0.10.3" # core flows, logging, data structures http = "0.10.3" # HTTP client and server json = "0.10.3" # JSON parsing and serialisation encoding = "0.10.3" # UTF-8 encode / decode fs = "0.10.3" # local file I/O