HTTP requests

Fetch a response with day-part-http and handle transport failures separately from HTTP status codes. Start with a working Day app and add day-part-http from the same checkout or pinned revision as day. Use the recipe harness to compile the example without making a network request.

Fetch when the input changes

Keep the endpoint in a signal. Use day::reactive::Resource for a request owned by the current page scope: it starts on construction, supersedes the previous request when the endpoint changes, and aborts its task when the scope is disposed. This differs from an ordinary day::task, which continues after the page closes unless you abort it.

Complete module for src/http.rs in the technical recipe harness. The harness compiles this exact block; it is not a standalone app. Add the catalog entries below to resource/locales/en/app.ftl and declare the module in src/lib.rs.

use crate::res;
use day::prelude::*;
use day::reactive::Resource as AsyncResource;
use day_part_http::{Client, HttpError, Request};
use std::time::Duration;

pub fn response_panel(endpoint: Signal<String>) -> impl Piece {
    let response = AsyncResource::new(
        move || endpoint.get(),
        |url| async move {
            let response = Client::new()
                .fetch_limited_future(
                    Request::get(url)
                        .timeout(Duration::from_secs(15))
                        .timeout_total(Duration::from_secs(60)),
                    1024 * 1024,
                )
                .await?;
            if !(200..300).contains(&response.status) {
                return Err(HttpError::Status(response.status));
            }
            Ok(response.text().into_owned())
        },
    );
    load(response, |body| {
        label(move || body.get()).id(id!(response_body))
    })
    .loading(|| label(res::str::request_loading()).id(id!(request_loading)))
    .failed(|_| label(res::str::request_failed()).id(id!(request_failed)))
}
request_loading = Loading response…
request_failed = The request could not be completed.

The endpoint and response body are user/server data, not bundled resources or app-owned copy. Validate an endpoint before enabling this panel. This recipe buffers at most 1 MiB of decoded response bytes and returns HttpError::BodyTooLarge if the advertised or received body exceeds that limit. Use streaming for larger responses. The body limit and timeouts are example policy choices, not Day defaults. Keep raw transport diagnostics in logs and map errors to localized UI messages.

Choose request policy

ContractBehavior in the checked-out source
fetch_future(Request) -> FetchFutureStarts immediately; awaiting yields Result<Response, HttpError>
HTTP 4xx/5xxArrive as a response; the example explicitly converts rejected statuses to HttpError::Status
Request::timeout(Duration)Overrides the client’s idle timeout, normally 30 seconds
Request::timeout_total(Duration)Bounds the whole request; the default client has no total limit
RedirectsThe default client follows up to 10; use ClientBuilder for another policy
Response::text()Lossy UTF-8; inspect body directly for binary data or strict decoding
Response::header(name)First matching header, ASCII case-insensitive; duplicates remain in headers
Request defaultsEmpty headers/body; expensive and constrained networking allowed; default cache policy; no explicit priority

Request::get, head, and delete accept a URL. post, put, and patch also accept body bytes. .header(name, value) appends a header; .body_file, .body_stream, and .form offer other payload sources. JSON parsing is an app dependency, not a built-in HTTP response method. See the HTTP reference for clients, cookies, redirects, credentials, streaming, body limits, and the complete non-exhaustive error enum.

Cancel and handle callbacks

Dropping a FetchFuture stops delivery and invokes the platform cancellation hook when available. Aborting its TaskHandle drops that future. Cancellation is not a rollback of a request the server has already accepted. Callback APIs take Send + 'static closures and may complete on an unspecified thread; capture a Setter, not a Signal. Awaiting inside day::task resumes on the UI thread. Format generated messages there or in a reactive label closure.

Account for the target

The source selects NSURLSession on Apple, OkHttp on Android, WinHTTP on Windows, the browser transport on wasm32, and an ArkTS bridge on OpenHarmony. Linux uses system libcurl loaded at runtime. Unknown targets have no transport. tier() describes the HTTP implementation, not the website’s maintenance tier; capabilities() describes available options.

Blocking fetch, fetch_to_file, and fetch_streamed must stay off the UI thread. Browser blocking calls return Unsupported; OpenHarmony also rejects blocking waits on its JS thread. Browser requests remain subject to CORS and restricted headers. TLS, cleartext, proxy, and credential behavior depend on the selected transport and host configuration.

Verify the result

Run the exact-code check from the recipe setup. In your app, test a successful response, HTTP 404, timeout, replacement of an in-flight request, and leaving the page on each target you ship. API details: request and future contracts and client defaults.