Effect.Effect<Client.HttpClient, never, Dispatcher>Creates an HttpClient that sends requests through the current Undici
Dispatcher, converts Effect HTTP bodies to Undici bodies, and maps
transport and decode failures to HttpClientError.
export const const makeUndici: Effect.Effect<
Client.HttpClient,
never,
Dispatcher
>
const makeUndici: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Creates an HttpClient that sends requests through the current Undici
Dispatcher, converts Effect HTTP bodies to Undici bodies, and maps
transport and decode failures to HttpClientError.
makeUndici = import EffectEffect.const gen: {
<Eff extends Effect<any, any, any>, AEff>(
f: () => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
<Self, Eff extends Effect<any, any, any>, AEff>(
options: { readonly self: Self },
f: (this: Self) => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
}
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Example (Sequencing effects with generators)
import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
gen(function*() {
const const dispatcher: Undici.Dispatcherdispatcher = yield* class Dispatcherclass Dispatcher {
key: Identifier;
of: (this: void, self: Undici.Dispatcher) => Undici.Dispatcher;
context: (self: Undici.Dispatcher) => Context.Context<Dispatcher>;
use: (f: (service: Undici.Dispatcher) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, Dispatcher | R>;
useSync: (f: (service: Undici.Dispatcher) => A) => Effect.Effect<A, never, Dispatcher>;
Identifier: Identifier;
Service: Shape;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Service tag for the Undici Dispatcher used by the Undici-backed HTTP
client.
Dispatcher
return import ClientClient.const make: (
f: (
request: HttpClientRequest.HttpClientRequest,
url: URL,
signal: AbortSignal,
fiber: Fiber.Fiber<
HttpClientResponse.HttpClientResponse,
Error.HttpClientError
>
) => Effect.Effect<
HttpClientResponse.HttpClientResponse,
Error.HttpClientError
>
) => HttpClient
Constructs an HttpClient from a low-level request runner.
Details
The runner receives the request, resolved URL, abort signal, and current fiber. The client wrapper handles URL construction failures, tracing and propagation, header redaction, and aborting non-scoped requests on interruption.
make((request: HttpClientRequest(parameter) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request, url: URLurl, signal: AbortSignalsignal, fiber: Fiber<
Response.HttpClientResponse,
Error.HttpClientError
>
(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber) =>
function convertBody(
body: Body.HttpBody
): Effect.Effect<
Exclude<
Undici.Dispatcher.DispatchOptions["body"],
undefined
>
>
convertBody(request: HttpClientRequest(parameter) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request.HttpClientRequest.body: Body.HttpBodybody).Pipeable.pipe<Effect.Effect<any, never, never>, Effect.Effect<unknown, Error.HttpClientError, never>, Effect.Effect<UndiciResponse, Error.HttpClientError, never>>(this: Effect.Effect<any, never, never>, ab: (_: Effect.Effect<any, never, never>) => Effect.Effect<unknown, Error.HttpClientError, never>, bc: (_: Effect.Effect<unknown, Error.HttpClientError, never>) => Effect.Effect<UndiciResponse, Error.HttpClientError, never>): Effect.Effect<...> (+21 overloads)pipe(
import EffectEffect.const flatMap: {
<A, B, E1, R1>(
f: (a: A) => Effect<B, E1, R1>
): <E, R>(
self: Effect<A, E, R>
) => Effect<B, E1 | E, R1 | R>
<A, E, R, B, E1, R1>(
self: Effect<A, E, R>,
f: (a: A) => Effect<B, E1, R1>
): Effect<B, E | E1, R | R1>
}
Chains effects to produce new Effect instances, useful for combining
operations that depend on previous results.
When to use
Use when you need to chain multiple effects, ensuring that each
step produces a new Effect while flattening any nested effects that may
occur.
Details
flatMap lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap used with arrays but works
specifically with Effect instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap always returns a new effect instead of
changing the original one.
Example (Choosing flatMap syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const transformation = (n: number) => Effect.succeed(n + 1)
const flatMappedWithPipe = pipe(myEffect, Effect.flatMap(transformation))
const flatMappedWithDataFirst = Effect.flatMap(myEffect, transformation)
const flatMappedWithMethod = myEffect.pipe(Effect.flatMap(transformation))
Example (Sequencing dependent effects)
import { Data, Effect, pipe } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`
const finalAmount = pipe(
fetchTransactionAmount,
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 95
flatMap((body: anybody) =>
import EffectEffect.const tryPromise: <
A,
E = Cause.UnknownError
>(
options:
| {
readonly try: (
signal: AbortSignal
) => PromiseLike<A>
readonly catch: (error: unknown) => E
}
| ((signal: AbortSignal) => PromiseLike<A>)
) => Effect<A, E>
Creates an Effect from an asynchronous computation that may throw or
reject, mapping failures into the error channel.
When to use
Use when you need to perform asynchronous operations that might fail, such
as fetching data from an API, and want thrown exceptions or rejected promises
captured as Effect errors.
Details
The promise thunk is evaluated when the effect runs. If it returns a promise
that resolves, the resolved value becomes the success value. If the thunk
throws before returning a promise, or if the returned promise rejects, the
thrown or rejected value is mapped into the error channel.
Passing the thunk directly maps failures to
Cause.UnknownError
.
Passing { try, catch } uses catch to map failures to an error of type
E.
The thunk receives an AbortSignal that is aborted if the effect is
interrupted. The underlying asynchronous operation only stops if it observes
that signal.
Gotchas
If catch throws while mapping the error, that thrown value is treated as a
defect. Return the error value you want in the error channel instead of
throwing it.
Example (Wrapping a fetch request that may fail)
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownError
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownError, never>
// ▼
const program = getTodo(1)
Example (Mapping Promise rejections to a tagged error)
import { Data, Effect } from "effect"
class TodoFetchError extends Data.TaggedError("TodoFetchError")<{ readonly cause: unknown }> {}
const getTodo = (id: number) =>
Effect.tryPromise({
try: () => fetch(`https://jsonplaceholder.typicode.com/todos/${id}`),
// remap the error
catch: (cause) => new TodoFetchError({ cause })
})
// ┌─── Effect<Response, TodoFetchError, never>
// ▼
const program = getTodo(1)
tryPromise({
try: (
signal: AbortSignal
) => PromiseLike<unknown>
try: () =>
const dispatcher: Undici.Dispatcherdispatcher.request({
...fiber: Fiber<
Response.HttpClientResponse,
Error.HttpClientError
>
(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber.Fiber<HttpClientResponse, HttpClientError>.getRef: <A>(ref: Context.Reference<A>) => AgetRef(const UndiciOptions: Context.Reference<Undici.Dispatcher.RequestOptions>const UndiciOptions: {
defaultValue: () => Shape;
of: (this: void, self: Undici.Dispatcher.RequestOptions) => Undici.Dispatcher.RequestOptions;
context: (self: Undici.Dispatcher.RequestOptions) => Context.Context<never>;
use: (f: (service: Undici.Dispatcher.RequestOptions) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
useSync: (f: (service: Undici.Dispatcher.RequestOptions) => A) => Effect.Effect<A, never, never>;
Identifier: Identifier;
Service: Shape;
key: string;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Fiber reference containing default Undici request options applied to requests
sent by makeUndici.
UndiciOptions),
signal: AbortSignalsignal,
method: HttpMethodmethod: request: HttpClientRequest(parameter) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request.HttpClientRequest.method: HttpMethodmethod,
headers: Headers.Headersheaders: request: HttpClientRequest(parameter) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request.HttpClientRequest.headers: Headers.Headersheaders,
origin: stringorigin: url: URLurl.URL.origin: stringThe origin read-only property of the URL interface returns a string containing the Unicode serialization of the origin of the represented URL.
origin,
path: stringpath: url: URLurl.URL.pathname: stringThe pathname property of the URL interface represents a location in a hierarchical structure.
pathname + url: URLurl.URL.search: stringThe search property of the URL interface is a search string, also called a query string, that is a string containing a '?' followed by the parameters of the URL.
search + url: URLurl.URL.hash: stringThe hash property of the URL interface is a string containing a '#' followed by the fragment identifier of the URL.
hash,
body: anybody,
// leave timeouts to Effect.timeout etc
headersTimeout: numberheadersTimeout: 60 * 60 * 1000,
bodyTimeout: numberbodyTimeout: 0
}),
catch: (
error: unknown
) => Error.HttpClientError
catch: (cause: unknowncause) =>
new import ErrorError.constructor HttpClientError(props: {
readonly reason: Error.HttpClientErrorReason;
}): Error.HttpClientError
Error wrapper for HTTP client failures, exposing the failed request and the optional response through its reason.
HttpClientError({
reason: Error.HttpClientErrorReason(property) reason: {
methodAndUrl: string;
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
request: HttpClientRequest;
description: string | undefined;
}
reason: new import ErrorError.constructor TransportError<{
readonly request: HttpClientRequest;
readonly cause?: unknown;
readonly description?: string;
}>(args: {
readonly request: HttpClientRequest;
readonly cause?: unknown;
readonly description?: string | undefined;
}): Error.TransportError
Error describing transport-level failures that occur while sending an HTTP request.
TransportError({
request: HttpClientRequest(property) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request,
cause?: unknowncause
})
})
})
),
import EffectEffect.const map: {
<A, B>(f: (a: A) => B): <E, R>(
self: Effect<A, E, R>
) => Effect<B, E, R>
<A, E, R, B>(
self: Effect<A, E, R>,
f: (a: A) => B
): Effect<B, E, R>
}
Transforms the value inside an effect by applying a function to it.
When to use
Use to transform an effect's success value with a function that returns a
plain value, producing a new effect without changing the original effect's
typed error or context requirements.
Details
map takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
Example (Choosing map syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const transformation = (n: number) => n + 1
const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
const mappedWithDataFirst = Effect.map(myEffect, transformation)
const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
Example (Adding a service charge)
import { Effect, pipe } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
Effect.map(addServiceCharge)
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 101
map((response: unknownresponse) => new constructor UndiciResponse(request: HttpClientRequest, source: Undici.Dispatcher.ResponseData): UndiciResponseUndiciResponse(request: HttpClientRequest(parameter) request: {
method: HttpMethod;
url: string;
urlParams: UrlParams.UrlParams;
hash: Option.Option<string>;
headers: Headers.Headers;
body: HttpBody.HttpBody;
toString: () => string;
toJSON: () => unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
request, response: unknownresponse))
)
)
})