Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra

HTTP client

Http is Laravel's Http facade in Rust, over reqwest (feature http). App binds one, so a command resolves it. It's the base for the Laravel backend (RFC 0005), and works for any other API.

use elyra::http::Http;
use std::time::Duration;

#[command]
async fn rates(ctx: Ctx) -> elyra::Result<Rates> {
    let res = ctx.get::<Http>()
        .get("https://api.example.com/rates")
        .query(&[("base", "NOK")])
        .token(&api_key)                       // Authorization: Bearer …
        .timeout(Duration::from_secs(10))
        .retry(3, Duration::from_millis(200))
        .send()
        .await?;
    Ok(res.json()?)
}

The request

Method Does
.get(url) / .post / .put / .patch / .delete start a request
.query(&q) query parameters: a struct, a map, or pairs. None fields are left out; a list becomes key[]=…, as Laravel reads it
.json(&body) a JSON body
.header(name, value) / .token(t) headers; token is Authorization: Bearer
.timeout(d) the deadline (30 s by default)
.retry(times, sleep) try again after a failed connection or a 5xx, for GET / PUT / DELETE
.retry_any_method() retry POST / PATCH too, when the server makes it safe
.send().await a Response, or an HttpError when no answer came

Every request asks for JSON (Accept: application/json), which is also what makes Laravel answer a failed validation with 422 JSON instead of a redirect.

The response

Any status is a Response: res.status(), res.ok() (2xx), res.header(name), res.text(), res.bytes(), res.json::<T>(). What a 404 means is up to the caller. The body is read whole, up to 32 MiB.

An HttpError means no usable answer:

  • Unreachable: the connection, DNS, TLS, or the timeout.
  • Insecure: not HTTPS.
  • InvalidUrl.
  • Encode / Decode: the query or body couldn't be encoded, or the response isn't the type asked for.
  • TooLarge.

? turns it into a command error.

Safety

  • HTTPS only, except to loopback (localhost, 127.0.0.1, ::1), and that holds for every redirect too.
  • A redirect to another host or port doesn't carry Authorization or cookies.
  • Logs (elyra::http) show the method, the host, the path, the status and the duration. They never show the query, the body or a header.

Testing

HttpFake answers from a table, never touches the network, and records each request:

use elyra::http::{Http, HttpFake, Method};

let fake = HttpFake::new()
    .get("https://api.example.com/rates", 200, json!({ "nok": 11.0 }))
    .post("https://api.example.com/orders", 422, json!({ "errors": { "qty": ["Too many."] } }))
    .unreachable(Method::Get, "https://api.example.com/down");
let app = TestApp::new(app().swap(Http::fake(fake.clone())));

// … invoke commands …
let sent = fake.assert_sent("GET", "https://api.example.com/rates");
assert_eq!(sent.header("authorization"), Some("Bearer t0ken"));
fake.assert_not_sent("DELETE", "https://api.example.com/orders/1");

Routes match the URL without its query. A trailing * matches the rest, and a later route wins over an earlier one. A request with no route fails clearly, rather than getting an empty answer.

Related