Skip to content

Response

Response module provides constructors and utilities for managing immutable interpretations of HTTP responses.

A Response represents immutable and clonable HTTP Response data. It is a base element of the Response module.

import {
import Response
Response
} from 'fx-fetch';
type
type Type = Response.Response
Type
=
import Response
Response
.
export Response

Represents immutable HTTP response.

@since0.1.0

Response
;

A Response.Input represents all the possible inputs that can be used to create a Response.

Concrete types that make up the Input union:

import {
import Response
Response
} from 'fx-fetch';
type
type Type = Response.Response | Response.Response.Parts | Response.Response.Options | Response
Type
=
import Response
Response
.
export Response

Represents immutable HTTP response.

@since0.1.0

Response
.
type Response.Input = Response.Response | Response.Response.Parts | Response.Response.Options | Response

@since0.1.0

Input
;
  • globalThis.Response — vanilla JS response
  • Response.Response.Parts — Object with response parts
  • Response.Response — Existing Response can be used too

Creates a Response from any of Response.Input. Throws an IllegalArgumentException if the provided input is invalid.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Response.Response
// ▼
const
const response: Response.Response
response
=
import Response
Response
.
function unsafeMake(input: Response.Response.Input): Response.Response
export unsafeMake

Creates a immutable Response object.

@since0.1.0

unsafeMake
({
body: string
body
: 'Hello, world!',
status: number
status
: 200,
});

Similar to unsafeMake, but returns an Option instead of throwing an error if the input is invalid. If the input is invalid, it returns None. If valid, it returns Some containing the Response.

import {
import Option

@since2.0.0

@since2.0.0

Option
} from 'effect';
import {
import Response
Response
} from 'fx-fetch';
// ┌─── Option.Option<Response.Response>
// ▼
const
const response: Option.Option<Response.Response>
response
=
import Response
Response
.
function make(input: import("/home/runner/work/fx-fetch/fx-fetch/packages/fx-fetch/dist/Response/Response").Response.Input): Option.Option<Response.Response>
export make

Creates a Response from the given input, returning None if the input is invalid.

@since0.1.0

make
({
body: string
body
: 'Hello, world!',
status: number
status
: 200,
});

For creating a Response from an object Response.Parts use same functions make or unsafeMake.

The Parts type is super primitive. It’s basically an object with all the properties of a JS response, but with better DX. Everything except the url is optional.

import {
import Response
Response
} from 'fx-fetch';
const
const parts: Response.Response.Parts
parts
:
import Response
Response
.
export Response

Represents immutable HTTP response.

@since0.1.0

Response
.
type Response.Parts = {
readonly body?: BodyInput;
readonly headers?: HeadersInput;
readonly redirected?: boolean;
readonly status?: number;
readonly statusText?: string;
readonly type?: ResponseType;
readonly url?: Url.Input;
}

@since0.1.0

Parts
= {
body?: BodyInput
body
: 'Hello, world!',
headers?: HeadersInput
headers
: {
'Content-Type': 'text/plain',
'X-Custom-Header': ['CustomValue', 'AnotherValue'],
},
redirected?: boolean
redirected
: false,
status?: number
status
: 200,
statusText?: string
statusText
: 'OK',
type?: ResponseType
type
: 'cors',
url?: Url.Input
url
: 'https://example.com/api',
};
const
const response: Response.Response
response
=
import Response
Response
.
function unsafeMake(input: Response.Response.Input): Response.Response
export unsafeMake

Creates a immutable Response object.

@since0.1.0

unsafeMake
(
const parts: Response.Response.Parts
parts
);

Properties url and headers have flexible input types.

  • url is of type Url.Input. Read more about it in Url module documentation.
  • headers can be almost anything what you can imagine as headers.
    • Record of strings
    • Entries (array of tuples)
    • Map of strings
    • Same as HeadersInit in Fetch API
    • many more…

The readJson function reads the body of a Response and parses it as JSON.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<unknown, MalformedJsonError>
// ▼
const
const payload: Effect<unknown, MalformedJsonError, never>
payload
=
import Response
Response
.
function readJson(response: Response.Response): Effect<unknown, MalformedJsonError, never>
export readJson

Reads a JSON response.

@since0.1.0

readJson
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading JSON, use Fetch.fetchJson.

The readText function reads the body of a Response and parses it as text.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<string, MalformedTextError>
// ▼
const
const payload: Effect<string, MalformedTextError, never>
payload
=
import Response
Response
.
function readText(response: Response.Response): Effect<string, MalformedTextError, never>
export readText

Reads a text response.

@since0.1.0

readText
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading text, use Fetch.fetchText.

The readBlob function reads the body of a Response and parses it as a Blob.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<Blob, MalformedBlobError>
// ▼
const
const payload: Effect<Blob, MalformedBlobError, never>
payload
=
import Response
Response
.
function readBlob(response: Response.Response): Effect<Blob, MalformedBlobError, never>
export readBlob

Reads a Blob response.

@since0.1.0

readBlob
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading blob, use Fetch.fetchBlob.

The readFormData function reads the body of a Response and parses it as form data.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<FormData, MalformedFormDataError>
// ▼
const
const payload: Effect<FormData, MalformedFormDataError, never>
payload
=
import Response
Response
.
function readFormData(response: Response.Response): Effect<FormData, MalformedFormDataError, never>
export readFormData

Reads a FormData response.

@since0.1.0

readFormData
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading form data, use Fetch.fetchFormData.

The readBytes function reads the body of a Response and parses it as bytes.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<Uint8Array<ArrayBuffer>, MalformedBytesError>
// ▼
const
const payload: Effect<Uint8Array<ArrayBufferLike>, MalformedBytesError, never>
payload
=
import Response
Response
.
function readBytes(response: Response.Response): Effect<Uint8Array<ArrayBufferLike>, MalformedBytesError, never>
export readBytes

Reads a Bytes response.

@since0.1.0

readBytes
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading bytes, use Fetch.fetchBytes.

The readArrayBuffer function reads the body of a Response and parses it as an ArrayBuffer.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<ArrayBuffer, MalformedArrayBufferError>
// ▼
const
const payload: Effect<ArrayBuffer, MalformedArrayBufferError, never>
payload
=
import Response
Response
.
function readArrayBuffer(response: Response.Response): Effect<ArrayBuffer, MalformedArrayBufferError, never>
export readArrayBuffer

Reads a ArrayBuffer response.

@since0.1.0

readArrayBuffer
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading bytes, use Fetch.fetchArrayBuffer.

The readReadableStream function reads the body of a Response and parses it as a ReadableStream.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<
// │ ReadableStream<Uint8Array<ArrayBuffer>>,
// │ MalformedBytesError
// │ >
// ▼
const
const payload: Effect<ReadableStream<Uint8Array<ArrayBufferLike>>, MalformedReadableStreamError, never>
payload
=
import Response
Response
.
function readReadableStream(response: Response.Response): Effect<ReadableStream<Uint8Array<ArrayBufferLike>>, MalformedReadableStreamError, never>
export readReadableStream

Reads a ReadableStream response.

@since0.1.0

readReadableStream
(
const response: Response.Response
response
);

If you want a shortcut that combines fetching and reading bytes, use Fetch.fetchReadableStream.

The readJsonWithSchema function reads the body of a Response, parses it as JSON, and validates it against a provided schema.

import {
import Schema
Schema
} from 'effect';
import {
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<
// │ typeof UserSchema.Type,
// │ MalformedJsonError | ParseError
// │ >
// ▼
const
const payload: Effect<{
readonly id: number;
readonly name: string;
}, MalformedJsonError | ParseError, never>
payload
=
import Response
Response
.
readJsonWithSchema<{
readonly id: number;
readonly name: string;
}, {
readonly id: number;
readonly name: string;
}, never>(response: Response.Response, schema: Schema.Schema<{
readonly id: number;
readonly name: string;
}, {
readonly id: number;
readonly name: string;
}, never>): Effect<{
readonly id: number;
readonly name: string;
}, MalformedJsonError | ParseError, never> (+1 overload)
export readJsonWithSchema

Reads a JSON response with the given schema.

@since0.1.0

readJsonWithSchema
(
const response: Response.Response
response
,
const UserSchema: Schema.Struct<{
id: typeof Schema.Int;
name: typeof Schema.String;
}>
UserSchema
);

The readJsonWithStandardSchemaV1 function reads the body of a Response, parses it as JSON, and validates it against a provided Standard Schema compatible schema.

This function works with any schema library that implements the Standard Schema specification, including Zod, Valibot, ArkType, and others.

import { Response } from 'fx-fetch';
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
// ┌─── Effect.Effect<
// │ { id: number; name: string },
// │ MalformedJsonError | ParseError
// │ >
// ▼
const payload = Response.readJsonWithStandardSchemaV1(response, UserSchema);

The readStream function reads the body of a Response and parses it as an Effect Stream.

import {
import Response
Response
} from 'fx-fetch';
// ┌─── Effect<
// │ Stream<Uint8Array<ArrayBufferLike>, MyError>,
// │ MalformedReadableStreamError
// │ >
// ▼
const
const payload: Effect<Stream<Uint8Array<ArrayBufferLike>, MyError, never>, MalformedReadableStreamError, never>
payload
=
import Response
Response
.
readStream<MyError>(response: Response.Response, options: Options<MyError>): Effect<Stream<Uint8Array<ArrayBufferLike>, MyError, never>, MalformedReadableStreamError, never> (+1 overload)
export readStream

Reads a ReadableStream response.

@since0.1.0

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

A NotOkError represents an error that occurs when a Response has a non-OK status code.

The error contains the following properties:

  • response - The Response that caused the error.
  • reason - A string indicating the category of the error based on the status code.
    • 'informational' for status codes 100-199
    • 'redirection' for status codes 300-399
    • 'client-error' for status codes 400-499
    • 'server-error' for status codes 500-599
import {
import Response
Response
} from 'fx-fetch';
function
function fn(error: Response.NotOkError): void
fn
(
error: Response.NotOkError
error
:
import Response
Response
.
class NotOkError

Thrown if the response is not OK. So the status code is not in the range 200-299.

@since0.1.0

NotOkError
) {
error: Response.NotOkError
error
.
response: Response.Response
response
; // ◀︎── Response.Response
error: Response.NotOkError
error
.
reason: "client-error" | "informational" | "redirection" | "server-error"
reason
; // ◀︎── 'client-error' | 'informational' | 'redirection' | 'server-error'
}

Encapsulates both success Response and NotOkError failure of an Effect into an Either type of Response.

Transforms an Effect<Response, NotOkError> into an Effect<Either<Response, Response>>.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Either

@since2.0.0

@since2.0.0

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

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Either.Either<Response.Response, Response.Response>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError, Fetch.Fetch>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Either.Either<Response.Response, Response.Response>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError, 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'
});

@example

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

@since0.1.0

unsafeMake
({
url: string
url
: './my-endpoint' });
// ┌─── Either.Either<
// │ Response.Response, // Ok response 200-399
// │ Response.Response // NotOk response 400-599
// │ >
// ▼
const
const result: Either.Either<Response.Response, Response.Response>
result
= yield*
import Fetch
Fetch
.
function fetch(request: Request.Request): Effect.Effect<Response.Response, Fetch.Fetch.ErrorType, 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
).
Pipeable.pipe<Effect.Effect<Response.Response, Fetch.Fetch.ErrorType, Fetch.Fetch>, Effect.Effect<Either.Either<Response.Response, Response.Response>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError, Fetch.Fetch>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<Response.Response, Fetch.Fetch.ErrorType, Fetch.Fetch>) => Effect.Effect<Either.Either<Response.Response, Response.Response>, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError, Fetch.Fetch>): Effect.Effect<...> (+21 overloads)
pipe
(
import Response
Response
.
function notOkEither<E, R>(self: Effect.Effect<Response.Response, E | Response.NotOkError, R>): Effect.Effect<Either.Either<Response.Response, Response.Response>, E, R>
export notOkEither

Encapsulates both success Response and NotOkError failure of an Effect into an Either type of Response.

@seeNotOkError for more details on the error type.

@since1.1.0

@example

import { Effect, Either } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── Either.Either<
// │ Response.Response, // Ok response 200-399
// │ Response.Response // NotOk response 400-599
// │ >
// ▼
const result = yield* Fetch.fetch(request).pipe(
Response.notOkEither
);
});

notOkEither
);
});