Browser HTTP Client
trb/platform/typescript/browser is the official low-level HTTP client for
TypeScript browser applications. It keeps target-specific Fetch behavior in an
explicit platform package while exposing checked TypeRB request, response,
body, and error types.
Construct one HttpClient at the application boundary and pass or export the
higher-level API objects that use it:
import {
HttpClient,
RequestError,
Response,
json_body,
} from trb/platform/typescript/browser
import { Header, Headers, HttpMethod } from trb/http
import trb/std/result
import trb/std/url
record Todo
id: Integer
title: String
end
record CreateTodoInput
title: String
end
def fetch_todo(client: HttpClient, id: Integer): Result<Response<Todo>, RequestError>
raw := try client.request(
"/todos",
query: [URL::QueryParameter.new(name: "id", value: id.to_s())],
headers: Headers.new([Header.new(name: "accept", value: "application/json")]),
timeout_milliseconds: 2000,
)
return raw.json<Todo>()
end
def create_todo(client: HttpClient, input: CreateTodoInput): Result<Response<Todo>, RequestError>
body := try json_body(input)
raw := try client.request("/todos", method: HttpMethod.post(), body: body)
return raw.json<Todo>()
end
TypeRB source does not add async. The TypeScript backend identifies the
suspending Fetch operation and generates the required Promise and await
boundaries.
Response model
request() returns Result<Response<Body>, RequestError>. Prefix try
propagates an error from a Result-returning function and yields the response on
success. The response buffers its body once and preserves the status, final
URL, ordered headers, and bytes. Decode it explicitly when no endpoint contract
is available:
response.json<T>()validates JSON againstTand returnsResult<Response<T>, RequestError>.response.text()returnsResponse<String>.response.bytes()returnsResponse<Bytes>.response.no_body()validates an empty body and returnsResult<Response<NoBody>, RequestError>.
response.headers.first(name) performs case-insensitive lookup,
response.headers.values(name) preserves repeated values, and
response.headers.entries() exposes the ordered header list. These types come
from portable trb/http, so server and browser packages share the same HTTP
value model.
Declared HTTP statuses, including non-2xx statuses, remain ordinary responses.
They are not converted into transport errors. Literal status fields and union
narrowing can express a contract response such as
CreatedResponse | InvalidResponse, then narrow the complete value with
case response.status.
Errors and bodies
Fallible operations return Result with one RequestError type. Its kind is Network,
Timeout, Abort, or Contract. A JSON or empty-body contract failure keeps
the original Response<Body> in error.response, so diagnostics and explicit
fallback handling do not lose the status, headers, or body.
Use RequestBody::Text, RequestBody::Bytes, or RequestBody::Form for those
wire formats. RequestBody::File sends a browser File as the request body and
uses its media type as the default Content-Type; explicitly supplied headers
still take precedence. The platform File type exposes checked name, size,
type, and lastModified fields and can cross a supported native component
callback boundary. json_body(value) runs the checked JSON encoder and
produces a JSON request body. Query parameters and headers are ordered arrays,
so repeated names have an unambiguous representation.
The initial client is buffered and intentionally omits streaming, retry policy,
progress events, and interceptors. Contractless external APIs and generated
endpoint clients use the same transport and Response model.
Generated clients for trb/web
When the server publishes explicit trb/web endpoint contracts, generate a
higher-level client instead of repeating path, query, body, and status handling:
trb web client --output generated/api_client.trb --name ApiClient
The generated TypeRB class accepts an ordinary HttpClient, so base URL and
application composition remain explicit:
import generated/api_client
import { HttpClient } from trb/platform/typescript/browser
api := ApiClient.new(HttpClient.new("https://api.example.com"))
Each method returns an endpoint-specific status enum inside Result, rather
than treating every non-2xx response as a transport error. Contract types stay
in an authored shared module imported by both route and browser projects. The
generator copies no data model, runs no target toolchain, and adds no retry,
authentication, caching, or global error policy. Those concerns compose around
the generated class or the underlying HttpClient.