HttpApiClient unstable
Builds HTTP clients from HttpApi declarations.
The client methods are derived from the groups and endpoints in an HttpApi
and run through an HttpClient. They use the same schema-driven contract as
the server: request parts are encoded from endpoint schemas, client
middleware is applied, the HTTP request is executed, and declared success or
error responses are decoded. This module also includes helpers for building a
client for only one group, one endpoint, or only the encoded URL.
Constructors
decodeResponse
unstableBuilds a decoder for raw responses of one endpoint in one API group, using
the same success and error decoding as a generated client method in
"decoded-only" mode.
When to use
Use with responseMode: "response-only" when the response head and body
arrive at different times, such as long polling, and the body should be
decoded later.
Details
The response does not need to come from a generated client. Declared error
responses fail with the decoded error, and undeclared statuses fail with an
HttpClientError whose reason is a DecodeError. Parse options are resolved
from the API, group and endpoint annotations. The body is only read when the
returned effect runs, so it must run inside the scope that owns the response.
Middleware and transformResponse are not applied.
Building the decoder walks the API declaration, so build it once per endpoint and reuse it.
Signature
declare function decodeResponse<ApiId extends string, Groups extends Constraint, GroupIdentifier extends string, EndpointIdentifier extends string>(api: HttpApi<ApiId, Groups>, options: { readonly endpoint: EndpointIdentifier; readonly group: GroupIdentifier;}): DecodeResponse<Groups, GroupIdentifier, EndpointIdentifier>Example Decoding a response body after the head arrives
import { Effect, Schema } from "effect"import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/http-api"
const Api = HttpApi.make("Api").add( HttpApiGroup.make("containers").add( HttpApiEndpoint.post("wait", "/containers/:id/wait", { params: { id: Schema.String }, success: Schema.Struct({ StatusCode: Schema.Number }) }) ))
const decodeWait = HttpApiClient.decodeResponse(Api, { group: "containers", endpoint: "wait"})
const program = Effect.gen(function*() { const client = yield* HttpApiClient.make(Api) // Succeeds as soon as the response head arrives const response = yield* client.containers.wait({ params: { id: "my-container" }, responseMode: "response-only" }) // Waits for the body, then decodes it with the endpoint schemas return yield* decodeWait(response)})Builds the typed client method for one endpoint in one API group, using the
supplied HttpClient and endpoint metadata.
Signature
declare function endpoint<ApiId extends string, Groups extends Constraint, GroupIdentifier extends string, EndpointIdentifier extends string, E, R>(api: HttpApi<ApiId, Groups>, options: { readonly baseUrl?: string | URL; readonly endpoint: EndpointIdentifier; readonly group: GroupIdentifier; readonly httpClient: With<E, R>; readonly transformClient?: (client: With<E, R>) => With<E, R>; readonly transformResponse?: (effect: Effect<unknown, unknown, unknown>) => Effect<unknown, unknown, unknown>;}): EndpointReturn<Groups, GroupIdentifier, EndpointIdentifier, E, R>Builds a typed client object for a single API group from the supplied
HttpClient, filtering the API to that group.
Signature
declare function group<ApiId extends string, Groups extends Constraint, GroupIdentifier extends string, E, R>(api: HttpApi<ApiId, Groups>, options: { readonly baseUrl?: string | URL; readonly group: GroupIdentifier; readonly httpClient: With<E, R>; readonly transformResponse?: (effect: Effect<unknown, unknown, unknown>) => Effect<unknown, unknown, unknown>;}): Effect<GroupByEndpoint<Extract<Groups, { readonly identifier: GroupIdentifier;}>, E, R>, never, MiddlewareClient<Middleware<Endpoints<Extract<Groups, { readonly identifier: GroupIdentifier;}>>>>>Constructs a type-safe client for an HTTP API using the HttpClient service,
endpoint schemas, middleware, and optional client or response transformations.
Signature
declare function make<ApiId extends string, Groups extends Constraint>(api: HttpApi<ApiId, Groups>, options?: { readonly baseUrl?: string | URL; readonly transformClient?: (client: HttpClient) => HttpClient; readonly transformResponse?: (effect: Effect<unknown, unknown, unknown>) => Effect<unknown, unknown, unknown>;}): Effect<Simplify<{ [Group in { readonly topLevel: false;} & Constraint]: GroupByEndpoint<Group, never, never> } & TopLevelMethods<Groups, never, never>>, never, HttpClient | MiddlewareClient<Middleware<Endpoints<Groups>>>>Constructs a type-safe client for an HTTP API from the supplied HttpClient,
using the API metadata to encode requests, execute middleware, and decode
responses.
Signature
declare function makeWith<ApiId extends string, Groups extends Constraint, E, R>(api: HttpApi<ApiId, Groups>, options: { readonly baseUrl?: string | URL; readonly httpClient: With<E, R>; readonly transformResponse?: (effect: Effect<unknown, unknown, unknown>) => Effect<unknown, unknown, unknown>;}): Effect<Simplify<{ [Group in { readonly topLevel: false;} & Constraint]: GroupByEndpoint<Group, Exclude<E, HttpClientError>, R> } & TopLevelMethods<Groups, Exclude<E, HttpClientError>, R>>, never, MiddlewareClient<Middleware<Endpoints<Groups>>>>urlBuilder
unstableCreates a type-safe URL builder that mirrors HttpApiClient.make.
Signature
declare function urlBuilder<Api extends Constraint>(api: Api, options?: { readonly baseUrl?: string | URL;}): UrlBuilder<Api>Example Building typed URLs
import { Schema } from "effect"import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup } from "effect/http-api"
const Api = HttpApi.make("Api").add( HttpApiGroup.make("users").add( HttpApiEndpoint.get("getUser", "/users/:id", { params: { id: Schema.String } }) ))
const buildUrl = HttpApiClient.urlBuilder(Api, { baseUrl: "https://api.example.com"})
buildUrl.users.getUser({ params: { id: "123" }}) // => "https://api.example.com/users/123"Guards
isUpgraded
unstableReturns true when a client method result is an upgraded connection.
Details
An endpoint may declare HttpApiSchema.Upgrade alongside ordinary success
responses, because a server can decline an upgrade (RFC 9110 ยง7.8). The
client method then returns either an Upgraded value or the decoded
ordinary success, depending on the response status. This predicate narrows
that union.
Signature
declare function isUpgraded(u: unknown): u is Upgraded<unknown>Example Handling a declined upgrade
import { Effect, Schema } from "effect"import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api"
const Api = HttpApi.make("docker").add( HttpApiGroup.make("exec").add( HttpApiEndpoint.post("start", "/exec/:id/start", { params: { id: Schema.String }, payload: Schema.Struct({ Detach: Schema.Boolean }), success: [HttpApiSchema.Upgrade({ protocol: "tcp" }), HttpApiSchema.Empty(200)] }) ))
const program = Effect.gen(function*() { const client = yield* HttpApiClient.make(Api) const result = yield* client.exec.start({ params: { id: "abc" }, payload: { Detach: false } }) if (HttpApiClient.isUpgraded(result)) { const writer = yield* result.socket.writer yield* writer.write("ls\n") }})Models
The type-safe client shape generated from HTTP API groups, with non-top-level groups exposed as nested objects and top-level endpoints exposed as methods.
Signature
type Client<Groups extends HttpApiGroup.Constraint, E = never, R = never> = Simplify<{ [Group in Extract<Groups, { readonly topLevel: false;}>]: Client.Group<Group, E, R> } & Client.TopLevelMethods<Groups, E, R>>The value a client method returns for an endpoint declared with
HttpApiSchema.Upgrade: the headers of the 101 Switching Protocols
response and the upgraded connection.
Details
headers is decoded with the upgrade's headers schema when it declares
one. See HttpClientResponse.upgrade for how the connection behaves and
when it closes.
When the endpoint also declares ordinary success responses, the method
returns either an Upgraded value or the decoded ordinary success. Use
isUpgraded to tell them apart.
Signature
interface Upgraded<Headers = Headers.Headers> { readonly "~effect/http-api/HttpApiClient/Upgraded": "~effect/http-api/HttpApiClient/Upgraded"; readonly headers: Headers; readonly socket: Socket;}The type-safe URL builder shape for an HTTP API, mirroring the generated client layout while returning URL strings instead of executing requests.
Signature
type UrlBuilder<Api extends HttpApi.Constraint> = Api extends HttpApi.HttpApi<infer _ApiId, infer Groups> ? [Extract<Groups, { readonly topLevel: true;}>] extends [never] ? UrlBuilderGroups<Groups> : [Extract<Groups, { readonly topLevel: false;}>] extends [never] ? UrlBuilderTopLevelMethods<Groups> : Simplify<UrlBuilderGroups<Groups> & UrlBuilderTopLevelMethods<Groups>> : neverOther
Type IDs
UpgradedTypeId
unstableRuntime brand key used to mark Upgraded values.
Signature
declare const UpgradedTypeId: "~effect/http-api/HttpApiClient/Upgraded"Type-level brand identifier used by Upgraded values.
Signature
type UpgradedTypeId = typeof UpgradedTypeIdUtility Types
Derives the typed client interface for an HttpApi, preserving any additional
client error and service requirements supplied by the caller.
Signature
type ForApi<Api extends HttpApi.Constraint, E = never, R = never> = Api extends HttpApi.HttpApi<infer _Id, infer Groups> ? Client<Groups, E, R> : neverComputes the decoded success value a client receives for an endpoint success schema, including headers, streaming Server-Sent Events, and streaming byte responses.
Signature
type SuccessType<S> = S extends HttpApiSchema.WithHeaders<infer _Inner, infer _Headers> ? HttpApiSchema.withHeaders<SuccessType<_Inner>, _Headers["Type"]> : S extends HttpApiSchema.StreamSse<infer _Events, infer _Error, infer _Value> ? Stream.Stream<_Value, _Error["Type"] | HttpClientError.HttpClientError | Schema.SchemaError | Sse.Retry | Sse.SseError, never> : S extends HttpApiSchema.StreamUint8Array ? Stream.Stream<Uint8Array, HttpClientError.HttpClientError, never> : S extends HttpApiSchema.StreamNdjson<infer _S> ? Stream.Stream<_S["Type"], HttpClientError.HttpClientError | Schema.SchemaError, never> : S extends HttpApiSchema.Upgrade<infer _Headers> ? [_Headers] extends [never] ? Upgraded : Upgraded<_Headers["Type"]> : S extends Schema.Constraint ? S["Type"] : never