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
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
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}`})
gen(function* () { const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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
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
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 numbersclass Random extends Context.Tag("MyRandomService")< Random, { readonly next: Effect.Effect<number> }>() {}
// Using the serviceconst 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 successfullyEffect.runPromise(runnable)// Example Output:// random number: 0.8241872233134417
provideService(import Fetch
Fetch.class Fetchexport Fetch
Fetch service for making or mocking HTTP requests.
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.
FetchLive), import Effect
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)
runFork // or Effect.runPromise etc.);Having fetch as a service allows you to easily swap implementations for testing, mocking, or other purposes.
fetchJson
Section titled “fetchJson”The fetchJson function is a helper that combines fetch with Response.readJson. It is syntactic sugar for better ergonomics.
import { import Effect
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
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}`})
gen(function* () { const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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.
fetchJson(const request: Request.Request
request);});fetchText
Section titled “fetchText”The fetchText function is a helper that combines fetch with Response.readText.
It is similar to fetchJson, but reads the body as plain text.
fetchBlob
Section titled “fetchBlob”The fetchBlob function is a helper that combines fetch with Response.readBlob.
It is similar to fetchJson, but reads the body as Blob.
fetchFormData
Section titled “fetchFormData”The fetchFormData function is a helper that combines fetch with Response.readFormData.
It is similar to fetchJson, but reads the body as FormData.
fetchBytes
Section titled “fetchBytes”The fetchBytes function is a helper that combines fetch with Response.readBytes.
It is similar to fetchJson, but reads the body as bytes.
fetchArrayBuffer
Section titled “fetchArrayBuffer”The fetchArrayBuffer function is a helper that combines fetch with Response.readArrayBuffer.
It is similar to fetchJson, but reads the body as ArrayBuffer.
fetchReadableStream
Section titled “fetchReadableStream”The fetchReadableStream function is a helper that combines fetch with Response.readReadableStream.
It is similar to fetchJson, but reads the body as ReadableStream.
fetchJsonWithSchema
Section titled “fetchJsonWithSchema”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
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)
Struct({ id: typeof Schema.Int
id: import Schema
Schema.class Int
Int, name: typeof Schema.String
name: import Schema
Schema.class Stringexport String
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
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}`})
gen(function* () { const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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.
fetchJsonWithSchema(const request: Request.Request
request, const UserSchema: Schema.Struct<{ id: typeof Schema.Int; name: typeof Schema.String;}>
UserSchema);});fetchStream
Section titled “fetchStream”The fetchStream function is a helper that combines fetch with Response.readStream.
import { import Data
Data, import Effect
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.
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
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}`})
gen(function* () { const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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.
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 });});paginatedFetch
Section titled “paginatedFetch”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: AnOptioncontaining the nextRequestto fetch, orNoneif there are no more pages.
import { import Effect
Effect, import Option
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>, {}, {}>
Class<class Person
Person>('Person')({ id: typeof Schema.Number
id: import Schema
Schema.class Numberexport Number
Number, name: typeof Schema.NonEmptyString
name: import Schema
Schema.class NonEmptyString
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)
Struct({ nextToken: Schema.optional<typeof Schema.String>
nextToken: import Schema
Schema.class Stringexport String
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>
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
Array),});
const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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
paginatedFetch( const request: Request.Request
request, import Effect
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.
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
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.
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
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.
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.
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.
paginatedFetchStream
Section titled “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
Effect, import Option
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>, {}, {}>
Class<class Person
Person>('Person')({ id: typeof Schema.Number
id: import Schema
Schema.class Numberexport Number
Number, name: typeof Schema.NonEmptyString
name: import Schema
Schema.class NonEmptyString
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)
Struct({ nextToken: Schema.optional<typeof Schema.String>
nextToken: import Schema
Schema.class Stringexport String
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>
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
Array),});
const const request: Request.Request
request = import Request
Request.function unsafeMake(input: Request.Request.Input): Request.Requestexport unsafeMake
Creates a immutable Request object. Throws an error if the input is invalid.
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
paginatedFetchStream( const request: Request.Request
request, import Effect
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.
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
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.
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
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.
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.
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> }; }));