Skip to content
// EFFECT DAYS 2026

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.

15 exports Since v4.0.0 Source

Constructors

Since v4.0.7

Builds 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)
})

endpoint

unstable
Since v4.0.0

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>

group

unstable
Since v4.0.0

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;
}>>>>>

make

unstable
Since v4.0.0

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>>>>

makeWith

unstable
Since v4.0.0

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

unstable
Since v4.0.0

Creates 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

unstable
Since v4.1.0

Returns 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

Client

type unstable
Since v4.0.0

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>>

Upgraded

interface unstable
Since v4.0.6

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;
}

UrlBuilder

type unstable
Since v4.0.0

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>> : never

Other

Client

unstable
Since v4.0.0

Helper types used to describe generated HTTP API clients, including endpoint methods, response modes, and grouped client shapes.

Type IDs

Since v4.1.0

Runtime brand key used to mark Upgraded values.

Signature

declare const UpgradedTypeId: "~effect/http-api/HttpApiClient/Upgraded"

UpgradedTypeId

type unstable
Since v4.1.0

Type-level brand identifier used by Upgraded values.

Signature

type UpgradedTypeId = typeof UpgradedTypeId

Utility Types

ForApi

type unstable
Since v4.0.0

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> : never

SuccessType

type unstable
Since v4.0.0

Computes 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