Skip to content

HTTP & HTTPS Upstreams

Declare an upstream to call an external REST API with typed requests and responses — pure Zig (std.http.Client + TLS). Calls are fallible (T!): try propagates a failure as an error response, or else handles it.

weld
upstream backend {
    base env("BACKEND_URL")                 # env() reads an environment variable
    header "Accept" "application/json"
    call getUser(id: int) GET "/users/{id}" -> User
    call listUsers()      GET "/users"      -> [User]
}

route GET "/proxy/:id" (id: int) -> User {
    respond try backend.getUser(id)          # try -> auto 502 on failure
}
route GET "/safe/:id" (id: int) -> User {
    let u = backend.getUser(id) else e {     # else -> handle it
        status 502
        respond "upstream down: {e.message}\n"
    }
    respond u
}
  • base sets the base URL; a block-level header "Name" "value" adds a default header to every call.
  • Path templates interpolate the call's typed params ("/users/{id}").
  • A POST / PUT call with a record or list parameter sends it as a JSON body.
  • The built-in Error { status: int, message: string } is what else binds.

Per-call headers. A call can add its own header "Name" <value> clauses whose values interpolate the call's params — so a request-specific header (auth token, correlation id) is forwarded to the upstream:

weld
upstream api {
    base "https://api.example.com"
    call getUser(id: int, token: string)
        GET "/users/{id}"
        header "authorization" "Bearer {token}"   # forwarded per call
        -> User
}

route GET "/me" (auth: string) -> User {
    respond try api.getUser(1, auth)                 # pass the request's token through
}

Weld also injects a W3C traceparent automatically for distributed tracing.

HTTPS

An https:// base does a real TLS handshake and verifies the server certificate against the system CA bundle — no configuration needed. See examples/httpsapi.weld, which calls a public JSON API over HTTPS.

TIP

HTTPS upstreams use HTTP/1.1 over TLS, which needs no ALPN, so they work today. This is distinct from native gRPC-over-TLS, which is blocked at the std layer until ALPN lands — see gRPC.