Skip to content

Fetch

Fetch module contains services and helpers for making HTTP requests.

The fetch function is the most basic way to perform requests.

It takes a Request as input and returns an Effect with Response. Automatically handles errors such as network issues, request abortion, and non-OK HTTP responses.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Effect.Effect<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Response.Response, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Response.Response, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>>, void, never>) => Effect.Effect<void, Fetch.FetchError | ... 2 more ... | Response.NotOkError, Fetch.Fetch> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './my-endpoint' });
// ┌─── Response.Response
// ▼
const
const response: Response.Response
response
= yield*
import Fetch
Fetch
.
function fetch(request: Request.Request): Effect.Effect<import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Response/index").Response, import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").FetchError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").AbortError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").NotAllowedError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Response/index").NotOkError, Fetch.Fetch>
export fetch

@since0.1.0

@example

import { Effect } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── Response.Response
// ▼
const response = yield* Fetch.fetch(request);
});
// Run the program
program.pipe(
Effect.provideService(Fetch.Fetch, Fetch.FetchLive),
Effect.runFork // or Effect.runPromise etc.
);

fetch
(
const request: Request.Request
request
);
});

As you can see, using fetch will add a new requirement: Fetch.Fetch. So to run programs that use fetch, you will need to provide an implementation of the Fetch service.

const program: Effect.Effect<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>
program
.
Pipeable.pipe<Effect.Effect<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>, Effect.Effect<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, never>, RuntimeFiber<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => RuntimeFiber<...>): RuntimeFiber<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <Fetch.Fetch, (request: Request.Request) => Effect.Effect<Response.Response, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, never>>(tag: Tag<Fetch.Fetch, (request: Request.Request) => Effect.Effect<Response.Response, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, never>>, service: (request: Request.Request) => Effect.Effect<Response.Response, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Provides an implementation for a service in the context of an effect.

Details

This function allows you to supply a specific implementation for a service required by an effect. Services are typically defined using Context.Tag, which acts as a unique identifier for the service. By using this function, you link the service to its concrete implementation, enabling the effect to execute successfully without additional requirements.

For example, you can use this function to provide a random number generator, a logger, or any other service your effect depends on. Once the service is provided, all parts of the effect that rely on the service will automatically use the implementation you supplied.

Example

import { Effect, Context } from "effect"
// Declaring a tag for a service that generates random numbers
class Random extends Context.Tag("MyRandomService")<
Random,
{ readonly next: Effect.Effect<number> }
>() {}
// Using the service
const program = Effect.gen(function* () {
const random = yield* Random
const randomNumber = yield* random.next
console.log(`random number: ${randomNumber}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const runnable = Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
// Run successfully
Effect.runPromise(runnable)
// Example Output:
// random number: 0.8241872233134417

@seeprovide for providing multiple layers to an effect.

@since2.0.0

provideService
(
import Fetch
Fetch
.
class Fetch
export Fetch

Fetch service for making or mocking HTTP requests.

@example

import { Fetch } from "fx-fetch";
Effect.gen(function* () {
const request = new Request('https://example.com');
const fetch = yield* Fetch.Fetch; // ◀︎── Get the Fetch service
const response = yield* fetch(request);
}).pipe(
Effect.provideService(Fetch.Fetch, Fetch.layer), // ◀︎── Provide built-in Fetch service
Effect.runPromise
);

@since0.1.0

Fetch
,
import Fetch
Fetch
.
const FetchLive: (request: Request.Request) => Effect.Effect<Response.Response, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, never>
export FetchLive

Fetch a request and handle the response.

@since0.1.0

FetchLive
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <A, E>(effect: Effect.Effect<A, E>, options?: RunForkOptions) => RuntimeFiber<A, E>

Runs an effect in the background, returning a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

Details

This function is the foundational way to execute an effect in the background. It creates a "fiber," a lightweight, cooperative thread of execution that can be observed (to access its result), interrupted, or joined. Fibers are useful for concurrent programming and allow effects to run independently of the main program flow.

Once the effect is running in a fiber, you can monitor its progress, cancel it if necessary, or retrieve its result when it completes. If the effect fails, the fiber will propagate the failure, which you can observe and handle.

When to Use

Use this function when you need to run an effect in the background, especially if the effect is long-running or performs periodic tasks. It's suitable for tasks that need to run independently but might still need observation or management, like logging, monitoring, or scheduled tasks.

This function is ideal if you don't need the result immediately or if the effect is part of a larger concurrent workflow.

Example (Running an Effect in the Background)

import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
// or Effect.runPromise etc.
);

Having fetch as a service allows you to easily swap implementations for testing, mocking, or other purposes.

The fetchJson function is a helper that combines fetch with Response.readJson. It is syntactic sugar for better ergonomics.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Effect.Effect<void, MalformedJsonError | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<unknown, MalformedJsonError | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<unknown, MalformedJsonError | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './my-endpoint' });
// ┌─── unknown
// ▼
const
const payload: unknown
payload
= yield*
import Fetch
Fetch
.
function fetchJson(request: Request.Request): Effect.Effect<unknown, import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Cause/index").MalformedJsonError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").FetchError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").AbortError | import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/errors").NotAllowedError | Response.NotOkError, import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Fetch/Fetch").Fetch>
export fetchJson

Fetches and reads a JSON response.

@since0.1.0

@seeResponse.readJson

@example

import { Effect } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedJsonError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── unknown
// ▼
const payload = yield* Fetch.fetchJson(request);
});

fetchJson
(
const request: Request.Request
request
);
});

The fetchText function is a helper that combines fetch with Response.readText. It is similar to fetchJson, but reads the body as plain text.

The fetchBlob function is a helper that combines fetch with Response.readBlob. It is similar to fetchJson, but reads the body as Blob.

The fetchFormData function is a helper that combines fetch with Response.readFormData. It is similar to fetchJson, but reads the body as FormData.

The fetchBytes function is a helper that combines fetch with Response.readBytes. It is similar to fetchJson, but reads the body as bytes.

The fetchArrayBuffer function is a helper that combines fetch with Response.readArrayBuffer. It is similar to fetchJson, but reads the body as ArrayBuffer.

The fetchReadableStream function is a helper that combines fetch with Response.readReadableStream. It is similar to fetchJson, but reads the body as ReadableStream.

The fetchJsonWithSchema function is another helper that combines fetch with Response.readJsonWithSchema. It allows you to validate the payload against a Effect Schema.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schema
Schema
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
const
const UserSchema: Schema.Struct<{
id: typeof Schema.Int;
name: typeof Schema.String;
}>
UserSchema
=
import Schema
Schema
.
function Struct<{
id: typeof Schema.Int;
name: typeof Schema.String;
}>(fields: {
id: typeof Schema.Int;
name: typeof Schema.String;
}): Schema.Struct<{
id: typeof Schema.Int;
name: typeof Schema.String;
}> (+1 overload)

@since3.10.0

Struct
({
id: typeof Schema.Int
id
:
import Schema
Schema
.
class Int

@since3.10.0

Int
,
name: typeof Schema.String
name
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
});
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Effect.Effect<void, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<{
readonly id: number;
readonly name: string;
}, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<{
readonly id: number;
readonly name: string;
}, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './my-endpoint' });
// ┌─── typeof UserSchema.Type
// ▼
const
const payload: {
readonly id: number;
readonly name: string;
}
payload
= yield*
import Fetch
Fetch
.
fetchJsonWithSchema<{
readonly id: number;
readonly name: string;
}, {
readonly id: number;
readonly name: string;
}, never>(request: Request.Request, schema: Schema.Schema<{
readonly id: number;
readonly name: string;
}, {
readonly id: number;
readonly name: string;
}, never>): Effect.Effect<{
readonly id: number;
readonly name: string;
}, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedJsonError | ParseError, Fetch.Fetch> (+1 overload)
export fetchJsonWithSchema

Fetches and reads a JSON response with the given schema.

@since0.1.0

@seeResponse.readJsonWithSchema

@example

import { Effect, Schema } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
const UserSchema = Schema.Struct({
id: Schema.Int,
name: Schema.String,
});
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── typeof UserSchema.Type
// ▼
const payload = yield* Fetch.fetchJsonWithSchema(request, UserSchema);
});

fetchJsonWithSchema
(
const request: Request.Request
request
,
const UserSchema: Schema.Struct<{
id: typeof Schema.Int;
name: typeof Schema.String;
}>
UserSchema
);
});

The fetchStream function is a helper that combines fetch with Response.readStream.

import {
import Data
Data
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
class
class MyError
MyError
extends
import Data
Data
.
const TaggedClass: <"MyError">(tag: "MyError") => new <A>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
readonly _tag: "MyError";
}

Provides a Tagged constructor for a Case Class.

@example

import * as assert from "node:assert"
import { Data, Equal } from "effect"
class Person extends Data.TaggedClass("Person")<{ readonly name: string }> {}
// Creating instances of Person
const mike1 = new Person({ name: "Mike" })
const mike2 = new Person({ name: "Mike" })
const john = new Person({ name: "John" })
// Checking equality
assert.deepStrictEqual(Equal.equals(mike1, mike2), true)
assert.deepStrictEqual(Equal.equals(mike1, john), false)
assert.deepStrictEqual(mike1._tag, "Person")

@since2.0.0

TaggedClass
('MyError') {}
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedReadableStreamError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Effect.Effect<void, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedReadableStreamError, Fetch.Fetch>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Stream<Uint8Array<ArrayBufferLike>, MyError, never>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedReadableStreamError, Fetch.Fetch>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Stream<Uint8Array<ArrayBufferLike>, MyError, never>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedReadableStreamError, Fetch.Fetch>>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './my-endpoint' });
// ┌─── Stream<
// │ Uint8Array<ArrayBufferLike>,
// │ MyError,
// │ never
// │ >,
// ▼
const
const stream: Stream<Uint8Array<ArrayBufferLike>, MyError, never>
stream
= yield*
import Fetch
Fetch
.
fetchStream<MyError>(request: Request.Request, options: Options<MyError>): Effect.Effect<Stream<Uint8Array<ArrayBufferLike>, MyError, never>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError | MalformedReadableStreamError, Fetch.Fetch> (+1 overload)
export fetchStream

Fetches and reads a stream response.

@since0.1.0

@seeResponse.readStream

@example

import { Data, Effect } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
class MyError extends Data.TaggedClass('MyError') {}
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedReadableStreamError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── Stream<
// │ Uint8Array<ArrayBufferLike>,
// │ MyError,
// │ never
// │ >,
// ▼
const stream = yield* Fetch.fetchStream(request, {
onError: (err) => new MyError(),
releaseLockOnEnd: true, // optional
});
});

fetchStream
(
const request: Request.Request
request
, {
onError: (error: unknown) => MyError
onError
: (
err: unknown
err
) => new
constructor MyError<{}>(args: void): MyError
MyError
(),
releaseLockOnEnd?: boolean | undefined
releaseLockOnEnd
: true, // optional
});
});

The paginatedFetch function is used to handle paginated HTTP endpoints. It repeatedly performs fetch requests based on the provided logic until there are no more pages to fetch.

The function takes an initial Request and an onResponse callback. The callback receives the last Response and should return an object containing:

  • pageEmission: The data to emit for the current page.
  • nextRequest: An Option containing the next Request to fetch, or None if there are no more pages.
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Option

@since2.0.0

@since2.0.0

Option
,
import Schema
Schema
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
class
class Person
Person
extends
import Schema
Schema
.
const Class: <Person>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<Person, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<Person, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<Fields>, {}, {}>

@example

import { Schema } from "effect"
class MyClass extends Schema.Class<MyClass>("MyClass")({
someField: Schema.String
}) {
someMethod() {
return this.someField + "bar"
}
}

@since3.10.0

Class
<
class Person
Person
>('Person')({
id: typeof Schema.Number
id
:
import Schema
Schema
.
class Number
export Number

@since3.10.0

Number
,
name: typeof Schema.NonEmptyString
name
:
import Schema
Schema
.
class NonEmptyString

@since3.10.0

NonEmptyString
,
}) {}
const
const PayloadSchema: Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>
PayloadSchema
=
import Schema
Schema
.
function Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>(fields: {
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}): Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}> (+1 overload)

@since3.10.0

Struct
({
nextToken: Schema.optional<typeof Schema.String>
nextToken
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
.
Pipeable.pipe<typeof Schema.String, Schema.optional<typeof Schema.String>>(this: typeof Schema.String, ab: (_: typeof Schema.String) => Schema.optional<typeof Schema.String>): Schema.optional<typeof Schema.String> (+21 overloads)
pipe
(
import Schema
Schema
.
const optional: <S extends Schema.Schema.All>(self: S) => Schema.optional<S>

@since3.10.0

@since3.10.0

optional
),
items: Schema.Array$<typeof Person>
items
:
class Person
Person
.
Pipeable.pipe<typeof Person, Schema.Array$<typeof Person>>(this: typeof Person, ab: (_: typeof Person) => Schema.Array$<typeof Person>): Schema.Array$<typeof Person> (+21 overloads)
pipe
(
import Schema
Schema
.
const Array: <Value extends Schema.Schema.Any>(value: Value) => Schema.Array$<Value>
export Array

@since3.10.0

Array
),
});
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './persons' });
// ┌─── Effect.Effect<
// │ Person[][], // ◀︎── array of pages
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Effect.Effect<readonly (readonly Person[])[], MalformedJsonError | ParseError | Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>
program
=
import Fetch
Fetch
.
paginatedFetch<readonly Person[], MalformedJsonError | ParseError, never>(request: Request.Request, onResponse: OnResponse<readonly Person[], MalformedJsonError | ParseError, never>): Effect.Effect<readonly (readonly Person[])[], MalformedJsonError | ParseError | Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch> (+1 overload)
export paginatedFetch

@since0.1.0

@seepaginatedFetchStream

@example

import { Effect, Option, Schema } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
class Person extends Schema.Class<Person>('Person')({
id: Schema.Number,
name: Schema.NonEmptyString,
}) {}
const PayloadSchema = Schema.Struct({
nextToken: Schema.String.pipe(Schema.optional),
items: Person.pipe(Schema.Array),
});
const request = Request.unsafeMake({ url: './persons' });
// ┌─── Effect.Effect<
// │ Person[][], // ◀︎── array of pages
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Fetch.paginatedFetch(
request,
Effect.fn(function* (lastResponse) {
const payload = yield* Response.readJsonWithSchema(lastResponse, PayloadSchema);
const nextToken = Option.fromNullable(payload.nextToken);
const nextRequest = nextToken.pipe(
Option.map((token) => Request.setUrlSearchParam(request, 'token', token))
);
return {
pageEmission: payload.items, // ◀︎── Person[]
nextRequest, // ◀︎── Option.Option<Request.Request>
};
})
);

paginatedFetch
(
const request: Request.Request
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fn: <YieldWrap<Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never>>, {
pageEmission: readonly Person[];
nextRequest: Option.Option<Request.Request>;
}, [lastResponse: Response.Response]>(body: (lastResponse: Response.Response) => Generator<YieldWrap<Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never>>, {
pageEmission: readonly Person[];
nextRequest: Option.Option<Request.Request>;
}, never>) => (lastResponse: Response.Response) => Effect.Effect<...> (+20 overloads)
fn
(function* (
lastResponse: Response.Response
lastResponse
) {
const
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
= yield*
import Response
Response
.
readJsonWithSchema<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, {
readonly items: readonly {
readonly id: number;
readonly name: string;
}[];
readonly nextToken?: string | undefined;
}, never>(response: Response.Response, schema: Schema.Schema<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, {
readonly items: readonly {
readonly id: number;
readonly name: string;
}[];
readonly nextToken?: string | undefined;
}, never>): Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never> (+1 overload)
export readJsonWithSchema

Reads a JSON response with the given schema.

@since0.1.0

readJsonWithSchema
(
lastResponse: Response.Response
lastResponse
,
const PayloadSchema: Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>
PayloadSchema
);
const
const nextToken: Option.Option<string>
nextToken
=
import Option

@since2.0.0

@since2.0.0

Option
.
const fromNullable: <string | undefined>(nullableValue: string | undefined) => Option.Option<string>

Converts a nullable value into an Option. Returns None if the value is null or undefined, otherwise wraps the value in a Some.

@example

import { Option } from "effect"
console.log(Option.fromNullable(undefined))
// Output: { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullable(null))
// Output: { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullable(1))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }

@since2.0.0

fromNullable
(
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
.
nextToken?: string | undefined
nextToken
);
const
const nextRequest: Option.Option<Request.Request>
nextRequest
=
const nextToken: Option.Option<string>
nextToken
.
Pipeable.pipe<Option.Option<string>, Option.Option<Request.Request>>(this: Option.Option<string>, ab: (_: Option.Option<string>) => Option.Option<Request.Request>): Option.Option<Request.Request> (+21 overloads)
pipe
(
import Option

@since2.0.0

@since2.0.0

Option
.
const map: <string, Request.Request>(f: (a: string) => Request.Request) => (self: Option.Option<string>) => Option.Option<Request.Request> (+1 overload)

Transforms the value inside a Some to a new value using the provided function, while leaving None unchanged.

Details

This function applies a mapping function f to the value inside an Option if it is a Some. If the Option is None, it remains unchanged. The result is a new Option with the transformed value (if it was a Some) or still None.

This utility is particularly useful for chaining transformations in a functional way without needing to manually handle None cases.

@example

import { Option } from "effect"
// Mapping over a `Some`
const someValue = Option.some(2)
console.log(Option.map(someValue, (n) => n * 2))
// Output: { _id: 'Option', _tag: 'Some', value: 4 }
// Mapping over a `None`
const noneValue = Option.none<number>()
console.log(Option.map(noneValue, (n) => n * 2))
// Output: { _id: 'Option', _tag: 'None' }

@since2.0.0

map
((
token: string
token
) =>
import Request
Request
.
function setUrlSearchParam(self: Request.Request, key: string, value: SearchParamValueInput): Request.Request (+1 overload)
export setUrlSearchParam

Sets a search parameter in the URL of a Request.

@example

import { Request } from 'fx-fetch';
const request = Request.make({ url: 'https://api.example.com' });
const requestWithParam = Request.setUrlSearchParam(request, 'page', '1');

@since0.1.0

setUrlSearchParam
(
const request: Request.Request
request
, 'token',
token: string
token
))
);
return {
pageEmission: readonly Person[]
pageEmission
:
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
.
items: readonly Person[]
items
, // ◀︎── Person[]
nextRequest: Option.Option<Request.Request>
nextRequest
, // ◀︎── Option.Option<Request.Request>
};
})
);

If you want a lazier approach that emits each page as it is fetched, you can use paginatedFetchStream.

The paginatedFetchStream function is similar to paginatedFetch, but instead of returning all pages at once, it returns a Stream that emits each page as it is fetched.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Option

@since2.0.0

@since2.0.0

Option
,
import Schema
Schema
,
import Stream
Stream
} from 'effect';
import {
import Fetch
Fetch
,
import Request
Request
,
import Response
Response
} from 'fx-fetch';
class
class Person
Person
extends
import Schema
Schema
.
const Class: <Person>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<Person, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<Person, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<Fields>, {}, {}>

@example

import { Schema } from "effect"
class MyClass extends Schema.Class<MyClass>("MyClass")({
someField: Schema.String
}) {
someMethod() {
return this.someField + "bar"
}
}

@since3.10.0

Class
<
class Person
Person
>('Person')({
id: typeof Schema.Number
id
:
import Schema
Schema
.
class Number
export Number

@since3.10.0

Number
,
name: typeof Schema.NonEmptyString
name
:
import Schema
Schema
.
class NonEmptyString

@since3.10.0

NonEmptyString
,
}) { }
const
const PayloadSchema: Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>
PayloadSchema
=
import Schema
Schema
.
function Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>(fields: {
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}): Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}> (+1 overload)

@since3.10.0

Struct
({
nextToken: Schema.optional<typeof Schema.String>
nextToken
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
.
Pipeable.pipe<typeof Schema.String, Schema.optional<typeof Schema.String>>(this: typeof Schema.String, ab: (_: typeof Schema.String) => Schema.optional<typeof Schema.String>): Schema.optional<typeof Schema.String> (+21 overloads)
pipe
(
import Schema
Schema
.
const optional: <S extends Schema.Schema.All>(self: S) => Schema.optional<S>

@since3.10.0

@since3.10.0

optional
),
items: Schema.Array$<typeof Person>
items
:
class Person
Person
.
Pipeable.pipe<typeof Person, Schema.Array$<typeof Person>>(this: typeof Person, ab: (_: typeof Person) => Schema.Array$<typeof Person>): Schema.Array$<typeof Person> (+21 overloads)
pipe
(
import Schema
Schema
.
const Array: <Value extends Schema.Schema.Any>(value: Value) => Schema.Array$<Value>
export Array

@since3.10.0

Array
),
});
const
const request: Request.Request
request
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@since0.1.0

unsafeMake
({
url: string
url
: './persons' });
// ┌─── Stream.Stream<
// │ Person[], // ◀︎── page emission
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const
const program: Stream.Stream<readonly Person[], MalformedJsonError | ParseError | Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch>
program
=
import Fetch
Fetch
.
paginatedFetchStream<readonly Person[], MalformedJsonError | ParseError, never>(request: Request.Request, onResponse: OnResponse<readonly Person[], MalformedJsonError | ParseError, never>): Stream.Stream<readonly Person[], MalformedJsonError | ParseError | Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | Response.NotOkError, Fetch.Fetch> (+1 overload)
export paginatedFetchStream

@since0.1.0

@example

import { Effect, Option, Schema, Stream } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
class Person extends Schema.Class<Person>('Person')({
id: Schema.Number,
name: Schema.NonEmptyString,
}) { }
const PayloadSchema = Schema.Struct({
nextToken: Schema.String.pipe(Schema.optional),
items: Person.pipe(Schema.Array),
});
const request = Request.unsafeMake({ url: './persons' });
// ┌─── Stream.Stream<
// │ Person[], // ◀︎── page emission
// │ | Fetch.FetchError | Fetch.AbortError | Fetch.NotAllowedError | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Fetch.paginatedFetchStream(
request,
Effect.fn(function* (lastResponse) {
const payload = yield* Response.readJsonWithSchema(lastResponse, PayloadSchema);
const nextToken = Option.fromNullable(payload.nextToken);
const nextRequest = nextToken.pipe(
Option.map((token) => Request.setUrlSearchParam(request, 'token', token))
);
return {
pageEmission: payload.items, // ◀︎── Person[]
nextRequest, // ◀︎── Option.Option<Request.Request>
};
})
);

paginatedFetchStream
(
const request: Request.Request
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fn: <YieldWrap<Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never>>, {
pageEmission: readonly Person[];
nextRequest: Option.Option<Request.Request>;
}, [lastResponse: Response.Response]>(body: (lastResponse: Response.Response) => Generator<YieldWrap<Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never>>, {
pageEmission: readonly Person[];
nextRequest: Option.Option<Request.Request>;
}, never>) => (lastResponse: Response.Response) => Effect.Effect<...> (+20 overloads)
fn
(function* (
lastResponse: Response.Response
lastResponse
) {
const
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
= yield*
import Response
Response
.
readJsonWithSchema<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, {
readonly items: readonly {
readonly id: number;
readonly name: string;
}[];
readonly nextToken?: string | undefined;
}, never>(response: Response.Response, schema: Schema.Schema<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, {
readonly items: readonly {
readonly id: number;
readonly name: string;
}[];
readonly nextToken?: string | undefined;
}, never>): Effect.Effect<{
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}, MalformedJsonError | ParseError, never> (+1 overload)
export readJsonWithSchema

Reads a JSON response with the given schema.

@since0.1.0

readJsonWithSchema
(
lastResponse: Response.Response
lastResponse
,
const PayloadSchema: Schema.Struct<{
nextToken: Schema.optional<typeof Schema.String>;
items: Schema.Array$<typeof Person>;
}>
PayloadSchema
);
const
const nextToken: Option.Option<string>
nextToken
=
import Option

@since2.0.0

@since2.0.0

Option
.
const fromNullable: <string | undefined>(nullableValue: string | undefined) => Option.Option<string>

Converts a nullable value into an Option. Returns None if the value is null or undefined, otherwise wraps the value in a Some.

@example

import { Option } from "effect"
console.log(Option.fromNullable(undefined))
// Output: { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullable(null))
// Output: { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullable(1))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }

@since2.0.0

fromNullable
(
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
.
nextToken?: string | undefined
nextToken
);
const
const nextRequest: Option.Option<Request.Request>
nextRequest
=
const nextToken: Option.Option<string>
nextToken
.
Pipeable.pipe<Option.Option<string>, Option.Option<Request.Request>>(this: Option.Option<string>, ab: (_: Option.Option<string>) => Option.Option<Request.Request>): Option.Option<Request.Request> (+21 overloads)
pipe
(
import Option

@since2.0.0

@since2.0.0

Option
.
const map: <string, Request.Request>(f: (a: string) => Request.Request) => (self: Option.Option<string>) => Option.Option<Request.Request> (+1 overload)

Transforms the value inside a Some to a new value using the provided function, while leaving None unchanged.

Details

This function applies a mapping function f to the value inside an Option if it is a Some. If the Option is None, it remains unchanged. The result is a new Option with the transformed value (if it was a Some) or still None.

This utility is particularly useful for chaining transformations in a functional way without needing to manually handle None cases.

@example

import { Option } from "effect"
// Mapping over a `Some`
const someValue = Option.some(2)
console.log(Option.map(someValue, (n) => n * 2))
// Output: { _id: 'Option', _tag: 'Some', value: 4 }
// Mapping over a `None`
const noneValue = Option.none<number>()
console.log(Option.map(noneValue, (n) => n * 2))
// Output: { _id: 'Option', _tag: 'None' }

@since2.0.0

map
((
token: string
token
) =>
import Request
Request
.
function setUrlSearchParam(self: Request.Request, key: string, value: SearchParamValueInput): Request.Request (+1 overload)
export setUrlSearchParam

Sets a search parameter in the URL of a Request.

@example

import { Request } from 'fx-fetch';
const request = Request.make({ url: 'https://api.example.com' });
const requestWithParam = Request.setUrlSearchParam(request, 'page', '1');

@since0.1.0

setUrlSearchParam
(
const request: Request.Request
request
, 'token',
token: string
token
))
);
return {
pageEmission: readonly Person[]
pageEmission
:
const payload: {
readonly nextToken?: string | undefined;
readonly items: readonly Person[];
}
payload
.
items: readonly Person[]
items
, // ◀︎── Person[]
nextRequest: Option.Option<Request.Request>
nextRequest
, // ◀︎── Option.Option<Request.Request>
};
})
);