Skip to content

Structured errors

Here is an example how to handle structured error payloads using Fx-Fetch.

It is good practice to have structured error payloads from your backend API. This allows you to make decisions based on error codes and additional details provided in the error response.

For example, consider the following error payload returned from the backend:

{
"error": {
"code": "BOOK_NOT_FOUND", // or "AUTHOR_NOT_FOUND"
"message": "The book with the given ID does not exist.",
}
}

We would like to handle these structured errors through the Effect error channel.

But we want to avoid boilerplate code for parsing the error response manually for each request.

Final solution should be used like this:

// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MyAuthorNotFoundError
// │ | MyBookNotFoundError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: 'https://example.com?authorId=123&bookId=456' });
const response = yield* Fetch.fetch(request).pipe(
withStructuredError('BOOK_NOT_FOUND', () => new MyBookNotFoundError()),
withStructuredError('AUTHOR_NOT_FOUND', () => new MyAuthorNotFoundError()),
);
});

Let’s go!

  1. Define withStructuredError function.

    withStructuredError.ts
    import {
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    } from 'effect';
    function
    function withStructuredErrorFn(self: Effect.Effect<unknown>, errorCode: string, onError: () => unknown): Effect.Effect<unknown>
    withStructuredErrorFn
    (
    self: Effect.Effect<unknown, never, never>
    self
    :
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <unknown>,
    errorCode: string
    errorCode
    : string,
    onError: () => unknown
    onError
    : () => unknown
    ):
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <unknown> {
    // TODO: Implement
    }

    But type signatures are not correct yet. If we want to have proper types, we need to use generics 😍.

    withStructuredError.ts
    import {
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    } from 'effect';
    import {
    import Response
    Response
    } from 'fx-fetch';
    function
    function withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    withStructuredErrorFn
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    ,
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    >
    (
    self: Effect.Effect<A, E | Response.NotOkError, R>
    self
    :
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    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
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    >, // ◀︎── Input must accept Response.NotOkError
    errorCode: string
    errorCode
    : string,
    onError: () => TError
    onError
    : () =>
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ):
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    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
    |
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    > { // ◀︎── Same as input plus TError in the union
    // TODO: Implement
    }
    • A for Effect success type
    • E for Effect error type
    • R for Effect required dependencies
    • TError for error which represents the structured error
  2. Declare schema of the structured error response.

    withStructuredError.ts
    import {
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    ,
    import Schema
    Schema
    } from 'effect';
    const
    const ErrorSchema: Schema.Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }>
    ErrorSchema
    =
    import Schema
    Schema
    .
    function Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }>(fields: {
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }): Schema.Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }> (+1 overload)

    @since3.10.0

    Struct
    ({
    errorCode: typeof Schema.String
    errorCode
    :
    import Schema
    Schema
    .
    class String
    export String

    @since3.10.0

    String
    ,
    message: typeof Schema.String
    message
    :
    import Schema
    Schema
    .
    class String
    export String

    @since3.10.0

    String
    ,
    });
    function
    function withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    withStructuredErrorFn
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    ,
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    >(
    self: Effect.Effect<A, any, R>
    self
    :
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    Response
    .
    type Response.NotOkError = /*unresolved*/ any
    NotOkError
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    >,
    errorCode: string
    errorCode
    : string,
    onError: () => TError
    onError
    : () =>
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ):
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    Response
    .
    type Response.NotOkError = /*unresolved*/ any
    NotOkError
    |
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    > {
    // TODO: Implement
    }

    The schema reflects the structure of the error payload we expect from the backend.

  3. Implement the function logic.

    And what the function should do?

    1. Process an error channel of Effect.
    2. Check if the error is of type Response.NotOkError.
    3. If so, parse the response body using the defined schema.
    4. If the parsed error code matches the provided errorCode, invoke the onError callback.
    5. Clean up after ourselves.
    6. Add the resulting error to the Effect error channel.
    withStructuredError.ts
    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 Response
    Response
    } from 'fx-fetch';
    const
    const ErrorSchema: Schema.Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }>
    ErrorSchema
    =
    import Schema
    Schema
    .
    function Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }>(fields: {
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }): Schema.Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }> (+1 overload)

    @since3.10.0

    Struct
    ({
    errorCode: typeof Schema.String
    errorCode
    :
    import Schema
    Schema
    .
    class String
    export String

    @since3.10.0

    String
    ,
    message: typeof Schema.String
    message
    :
    import Schema
    Schema
    .
    class String
    export String

    @since3.10.0

    String
    ,
    });
    function
    function withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    withStructuredErrorFn
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    ,
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    >(
    self: Effect.Effect<A, E | Response.NotOkError, R>
    self
    :
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    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
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    >,
    errorCode: string
    errorCode
    : string,
    onError: () => TError
    onError
    : () =>
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ):
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    interface Effect<out A, out E = never, out R = never>

    The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

    Details

    The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

    To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

    @since2.0.0

    @since2.0.0

    Effect
    <
    function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    A
    ,
    function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    E
    |
    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
    |
    function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    TError
    ,
    function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
    R
    > {
    return
    self: Effect.Effect<A, E | Response.NotOkError, R>
    self
    .
    Pipeable.pipe<Effect.Effect<A, E | Response.NotOkError, R>, Effect.Effect<A, E | TError | Response.NotOkError, R>>(this: Effect.Effect<A, E | Response.NotOkError, R>, ab: (_: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | TError | Response.NotOkError, R>): Effect.Effect<A, E | TError | Response.NotOkError, R> (+21 overloads)
    pipe
    (
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const catchAll: <E | Response.NotOkError, never, E | TError | Response.NotOkError, never>(f: (e: E | Response.NotOkError) => Effect.Effect<never, E | TError | Response.NotOkError, never>) => <A, R>(self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | TError | Response.NotOkError, R> (+1 overload)

    Handles all errors in an effect by providing a fallback effect.

    Details

    This function catches any errors that may occur during the execution of an effect and allows you to handle them by specifying a fallback effect. This ensures that the program continues without failing by recovering from errors using the provided fallback logic.

    Note: This function only handles recoverable errors. It will not recover from unrecoverable defects.

    Example (Providing Recovery Logic for Recoverable Errors)

    import { Effect, Random } from "effect"
    class HttpError {
    readonly _tag = "HttpError"
    }
    class ValidationError {
    readonly _tag = "ValidationError"
    }
    // ┌─── Effect<string, HttpError | ValidationError, never>
    // ▼
    const program = Effect.gen(function* () {
    const n1 = yield* Random.next
    const n2 = yield* Random.next
    if (n1 < 0.5) {
    yield* Effect.fail(new HttpError())
    }
    if (n2 < 0.5) {
    yield* Effect.fail(new ValidationError())
    }
    return "some result"
    })
    // ┌─── Effect<string, never, never>
    // ▼
    const recovered = program.pipe(
    Effect.catchAll((error) =>
    Effect.succeed(`Recovering from ${error._tag}`)
    )
    )

    @seecatchAllCause for a version that can recover from both recoverable and unrecoverable errors.

    @since2.0.0

    catchAll
    ((
    selfErr: E | Response.NotOkError
    selfErr
    ) =>
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const gen: <YieldWrap<Option.None<TError>> | YieldWrap<Option.Some<TError>> | YieldWrap<Effect.Effect<{
    readonly errorCode: string;
    readonly message: string;
    }, MalformedJsonError | ParseError, never>>, TError>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Option.None<TError>> | YieldWrap<Option.Some<TError>> | YieldWrap<Effect.Effect<{
    readonly errorCode: string;
    readonly message: string;
    }, MalformedJsonError | ParseError, never>>, TError, 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* () {
    if (!(
    selfErr: E | Response.NotOkError
    selfErr
    instanceof
    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
    )) {
    // If it's not a NotOkError, we can't handle it here
    return yield*
    import Option

    @since2.0.0

    @since2.0.0

    Option
    .
    const none: <never>() => Option.Option<never>

    Represents the absence of a value by creating an empty Option.

    Option.none returns an Option<never>, which is a subtype of Option<A>. This means you can use it in place of any Option<A> regardless of the type A.

    Example (Creating an Option with No Value)

    import { Option } from "effect"
    // An Option holding no value
    //
    // ┌─── Option<never>
    // ▼
    const noValue = Option.none()
    console.log(noValue)
    // Output: { _id: 'Option', _tag: 'None' }

    @seesome for the opposite operation.

    @since2.0.0

    none
    ();
    }
    const
    const response: Response.Response
    response
    =
    selfErr: Response.NotOkError
    selfErr
    .
    response: Response.Response
    response
    ;
    const
    const payload: {
    readonly errorCode: string;
    readonly message: string;
    }
    payload
    = yield*
    import Response
    Response
    .
    readJsonWithSchema<{
    readonly errorCode: string;
    readonly message: string;
    }, {
    readonly errorCode: string;
    readonly message: string;
    }, never>(response: Response.Response, schema: Schema.Schema<{
    readonly errorCode: string;
    readonly message: string;
    }, {
    readonly errorCode: string;
    readonly message: string;
    }, never>): Effect.Effect<{
    readonly errorCode: string;
    readonly message: 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 ErrorSchema: Schema.Struct<{
    errorCode: typeof Schema.String;
    message: typeof Schema.String;
    }>
    ErrorSchema
    );
    if (
    const payload: {
    readonly errorCode: string;
    readonly message: string;
    }
    payload
    .
    errorCode: string
    errorCode
    !==
    errorCode: string
    errorCode
    ) {
    // Error code does not match, do not handle
    return yield*
    import Option

    @since2.0.0

    @since2.0.0

    Option
    .
    const none: <never>() => Option.Option<never>

    Represents the absence of a value by creating an empty Option.

    Option.none returns an Option<never>, which is a subtype of Option<A>. This means you can use it in place of any Option<A> regardless of the type A.

    Example (Creating an Option with No Value)

    import { Option } from "effect"
    // An Option holding no value
    //
    // ┌─── Option<never>
    // ▼
    const noValue = Option.none()
    console.log(noValue)
    // Output: { _id: 'Option', _tag: 'None' }

    @seesome for the opposite operation.

    @since2.0.0

    none
    ();
    }
    return yield*
    import Option

    @since2.0.0

    @since2.0.0

    Option
    .
    const some: <TError>(value: TError) => Option.Option<TError>

    Wraps the given value into an Option to represent its presence.

    Example (Creating an Option with a Value)

    import { Option } from "effect"
    // An Option holding the number 1
    //
    // ┌─── Option<number>
    // ▼
    const value = Option.some(1)
    console.log(value)
    // Output: { _id: 'Option', _tag: 'Some', value: 1 }

    @seenone for the opposite operation.

    @since2.0.0

    some
    (
    onError: () => TError
    onError
    ());
    }).
    Pipeable.pipe<Effect.Effect<TError, NoSuchElementException | MalformedJsonError | ParseError, never>, Effect.Effect<TError, E | Response.NotOkError, never>, Effect.Effect<never, E | TError | Response.NotOkError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<TError, NoSuchElementException | MalformedJsonError | ParseError, never>) => Effect.Effect<TError, E | Response.NotOkError, never>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
    pipe
    (
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const catchAll: <NoSuchElementException | MalformedJsonError | ParseError, never, E | Response.NotOkError, never>(f: (e: NoSuchElementException | MalformedJsonError | ParseError) => Effect.Effect<never, E | Response.NotOkError, never>) => <A, R>(self: Effect.Effect<A, NoSuchElementException | MalformedJsonError | ParseError, R>) => Effect.Effect<...> (+1 overload)

    Handles all errors in an effect by providing a fallback effect.

    Details

    This function catches any errors that may occur during the execution of an effect and allows you to handle them by specifying a fallback effect. This ensures that the program continues without failing by recovering from errors using the provided fallback logic.

    Note: This function only handles recoverable errors. It will not recover from unrecoverable defects.

    Example (Providing Recovery Logic for Recoverable Errors)

    import { Effect, Random } from "effect"
    class HttpError {
    readonly _tag = "HttpError"
    }
    class ValidationError {
    readonly _tag = "ValidationError"
    }
    // ┌─── Effect<string, HttpError | ValidationError, never>
    // ▼
    const program = Effect.gen(function* () {
    const n1 = yield* Random.next
    const n2 = yield* Random.next
    if (n1 < 0.5) {
    yield* Effect.fail(new HttpError())
    }
    if (n2 < 0.5) {
    yield* Effect.fail(new ValidationError())
    }
    return "some result"
    })
    // ┌─── Effect<string, never, never>
    // ▼
    const recovered = program.pipe(
    Effect.catchAll((error) =>
    Effect.succeed(`Recovering from ${error._tag}`)
    )
    )

    @seecatchAllCause for a version that can recover from both recoverable and unrecoverable errors.

    @since2.0.0

    catchAll
    ((
    _: NoSuchElementException | MalformedJsonError | ParseError
    _
    ) =>
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const fail: <E | Response.NotOkError>(error: E | Response.NotOkError) => Effect.Effect<never, E | Response.NotOkError, never>

    Creates an Effect that represents a recoverable error.

    When to Use

    Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

    catchAll

    or

    catchTag

    .

    Example (Creating a Failed Effect)

    import { Effect } from "effect"
    // ┌─── Effect<never, Error, never>
    // ▼
    const failure = Effect.fail(
    new Error("Operation failed due to network error")
    )

    @seesucceed to create an effect that represents a successful value.

    @since2.0.0

    fail
    (
    selfErr: E | Response.NotOkError
    selfErr
    )), // Reassign original error if parsing fails
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const flatMap: <TError, never, TError, never>(f: (a: TError) => Effect.Effect<never, TError, never>) => <E, R>(self: Effect.Effect<TError, E, R>) => Effect.Effect<never, TError | E, R> (+1 overload)

    Chains effects to produce new Effect instances, useful for combining operations that depend on previous results.

    Syntax

    const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))
    // or
    const flatMappedEffect = Effect.flatMap(myEffect, transformation)
    // or
    const flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))

    Details

    flatMap lets you sequence effects so that the result of one effect can be used in the next step. It is similar to flatMap used with arrays but works specifically with Effect instances, allowing you to avoid deeply nested effect structures.

    Since effects are immutable, flatMap always returns a new effect instead of changing the original one.

    When to Use

    Use flatMap when you need to chain multiple effects, ensuring that each step produces a new Effect while flattening any nested effects that may occur.

    Example

    import { pipe, Effect } from "effect"
    // Function to apply a discount safely to a transaction amount
    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)
    // Simulated asynchronous task to fetch a transaction amount from database
    const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
    // Chaining the fetch and discount application using `flatMap`
    const finalAmount = pipe(
    fetchTransactionAmount,
    Effect.flatMap((amount) => applyDiscount(amount, 5))
    )
    Effect.runPromise(finalAmount).then(console.log)
    // Output: 95

    @seetap for a version that ignores the result of the effect.

    @since2.0.0

    flatMap
    (
    import Effect

    @since2.0.0

    @since2.0.0

    @since2.0.0

    Effect
    .
    const fail: <E>(error: E) => Effect.Effect<never, E>

    Creates an Effect that represents a recoverable error.

    When to Use

    Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

    catchAll

    or

    catchTag

    .

    Example (Creating a Failed Effect)

    import { Effect } from "effect"
    // ┌─── Effect<never, Error, never>
    // ▼
    const failure = Effect.fail(
    new Error("Operation failed due to network error")
    )

    @seesucceed to create an effect that represents a successful value.

    @since2.0.0

    fail
    ) // Fail with the custom error
    )
    )
    );
    }
  4. Add dual API support

    Adding dual API support is super simple because of the EffectTS Function module. Unfortunately only for runtime (JavaScript). The typescript types must be provided manually.

That’s it! You now have a reusable withStructuredError function that can be applied to any Fx-Fetch request to handle structured error payloads.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Function
Function
,
import Option

@since2.0.0

@since2.0.0

Option
,
import Schema
Schema
} from 'effect';
import {
import Response
Response
} from 'fx-fetch';
const
const ErrorSchema: Schema.Struct<{
errorCode: typeof Schema.String;
message: typeof Schema.String;
}>
ErrorSchema
=
import Schema
Schema
.
function Struct<{
errorCode: typeof Schema.String;
message: typeof Schema.String;
}>(fields: {
errorCode: typeof Schema.String;
message: typeof Schema.String;
}): Schema.Struct<{
errorCode: typeof Schema.String;
message: typeof Schema.String;
}> (+1 overload)

@since3.10.0

Struct
({
2 collapsed lines
errorCode: typeof Schema.String
errorCode
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
message: typeof Schema.String
message
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
});
function
function withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
withStructuredErrorFn
<
function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
,
function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
,
function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
>(
27 collapsed lines
self: Effect.Effect<A, E | Response.NotOkError, R>
self
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
,
function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
>,
errorCode: string
errorCode
: string,
onError: () => TError
onError
: () =>
function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
|
function (type parameter) TError in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
,
function (type parameter) R in withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
> {
return
self: Effect.Effect<A, E | Response.NotOkError, R>
self
.
Pipeable.pipe<Effect.Effect<A, E | Response.NotOkError, R>, Effect.Effect<A, E | TError | Response.NotOkError, R>>(this: Effect.Effect<A, E | Response.NotOkError, R>, ab: (_: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | TError | Response.NotOkError, R>): Effect.Effect<A, E | TError | Response.NotOkError, R> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <E | Response.NotOkError, never, E | TError | Response.NotOkError, never>(f: (e: E | Response.NotOkError) => Effect.Effect<never, E | TError | Response.NotOkError, never>) => <A, R>(self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | TError | Response.NotOkError, R> (+1 overload)

Handles all errors in an effect by providing a fallback effect.

Details

This function catches any errors that may occur during the execution of an effect and allows you to handle them by specifying a fallback effect. This ensures that the program continues without failing by recovering from errors using the provided fallback logic.

Note: This function only handles recoverable errors. It will not recover from unrecoverable defects.

Example (Providing Recovery Logic for Recoverable Errors)

import { Effect, Random } from "effect"
class HttpError {
readonly _tag = "HttpError"
}
class ValidationError {
readonly _tag = "ValidationError"
}
// ┌─── Effect<string, HttpError | ValidationError, never>
// ▼
const program = Effect.gen(function* () {
const n1 = yield* Random.next
const n2 = yield* Random.next
if (n1 < 0.5) {
yield* Effect.fail(new HttpError())
}
if (n2 < 0.5) {
yield* Effect.fail(new ValidationError())
}
return "some result"
})
// ┌─── Effect<string, never, never>
// ▼
const recovered = program.pipe(
Effect.catchAll((error) =>
Effect.succeed(`Recovering from ${error._tag}`)
)
)

@seecatchAllCause for a version that can recover from both recoverable and unrecoverable errors.

@since2.0.0

catchAll
((
selfErr: E | Response.NotOkError
selfErr
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Option.None<TError>> | YieldWrap<Option.Some<TError>> | YieldWrap<Effect.Effect<{
readonly errorCode: string;
readonly message: string;
}, MalformedJsonError | ParseError, never>>, TError>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Option.None<TError>> | YieldWrap<Option.Some<TError>> | YieldWrap<Effect.Effect<{
readonly errorCode: string;
readonly message: string;
}, MalformedJsonError | ParseError, never>>, TError, 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* () {
if (!(
selfErr: E | Response.NotOkError
selfErr
instanceof
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
)) {
// If it's not a NotOkError, we can't handle it here
return yield*
import Option

@since2.0.0

@since2.0.0

Option
.
const none: <never>() => Option.Option<never>

Represents the absence of a value by creating an empty Option.

Option.none returns an Option<never>, which is a subtype of Option<A>. This means you can use it in place of any Option<A> regardless of the type A.

Example (Creating an Option with No Value)

import { Option } from "effect"
// An Option holding no value
//
// ┌─── Option<never>
// ▼
const noValue = Option.none()
console.log(noValue)
// Output: { _id: 'Option', _tag: 'None' }

@seesome for the opposite operation.

@since2.0.0

none
();
}
const
const response: Response.Response
response
=
selfErr: Response.NotOkError
selfErr
.
response: Response.Response
response
;
const
const payload: {
readonly errorCode: string;
readonly message: string;
}
payload
= yield*
import Response
Response
.
readJsonWithSchema<{
readonly errorCode: string;
readonly message: string;
}, {
readonly errorCode: string;
readonly message: string;
}, never>(response: Response.Response, schema: Schema.Schema<{
readonly errorCode: string;
readonly message: string;
}, {
readonly errorCode: string;
readonly message: string;
}, never>): Effect.Effect<{
readonly errorCode: string;
readonly message: 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 ErrorSchema: Schema.Struct<{
errorCode: typeof Schema.String;
message: typeof Schema.String;
}>
ErrorSchema
);
if (
const payload: {
readonly errorCode: string;
readonly message: string;
}
payload
.
errorCode: string
errorCode
!==
errorCode: string
errorCode
) {
// Error code does not match, do not handle
return yield*
import Option

@since2.0.0

@since2.0.0

Option
.
const none: <never>() => Option.Option<never>

Represents the absence of a value by creating an empty Option.

Option.none returns an Option<never>, which is a subtype of Option<A>. This means you can use it in place of any Option<A> regardless of the type A.

Example (Creating an Option with No Value)

import { Option } from "effect"
// An Option holding no value
//
// ┌─── Option<never>
// ▼
const noValue = Option.none()
console.log(noValue)
// Output: { _id: 'Option', _tag: 'None' }

@seesome for the opposite operation.

@since2.0.0

none
();
}
return yield*
import Option

@since2.0.0

@since2.0.0

Option
.
const some: <TError>(value: TError) => Option.Option<TError>

Wraps the given value into an Option to represent its presence.

Example (Creating an Option with a Value)

import { Option } from "effect"
// An Option holding the number 1
//
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }

@seenone for the opposite operation.

@since2.0.0

some
(
onError: () => TError
onError
());
}).
Pipeable.pipe<Effect.Effect<TError, NoSuchElementException | MalformedJsonError | ParseError, never>, Effect.Effect<TError, E | Response.NotOkError, never>, Effect.Effect<never, E | TError | Response.NotOkError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<TError, NoSuchElementException | MalformedJsonError | ParseError, never>) => Effect.Effect<TError, E | Response.NotOkError, never>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <NoSuchElementException | MalformedJsonError | ParseError, never, E | Response.NotOkError, never>(f: (e: NoSuchElementException | MalformedJsonError | ParseError) => Effect.Effect<never, E | Response.NotOkError, never>) => <A, R>(self: Effect.Effect<A, NoSuchElementException | MalformedJsonError | ParseError, R>) => Effect.Effect<...> (+1 overload)

Handles all errors in an effect by providing a fallback effect.

Details

This function catches any errors that may occur during the execution of an effect and allows you to handle them by specifying a fallback effect. This ensures that the program continues without failing by recovering from errors using the provided fallback logic.

Note: This function only handles recoverable errors. It will not recover from unrecoverable defects.

Example (Providing Recovery Logic for Recoverable Errors)

import { Effect, Random } from "effect"
class HttpError {
readonly _tag = "HttpError"
}
class ValidationError {
readonly _tag = "ValidationError"
}
// ┌─── Effect<string, HttpError | ValidationError, never>
// ▼
const program = Effect.gen(function* () {
const n1 = yield* Random.next
const n2 = yield* Random.next
if (n1 < 0.5) {
yield* Effect.fail(new HttpError())
}
if (n2 < 0.5) {
yield* Effect.fail(new ValidationError())
}
return "some result"
})
// ┌─── Effect<string, never, never>
// ▼
const recovered = program.pipe(
Effect.catchAll((error) =>
Effect.succeed(`Recovering from ${error._tag}`)
)
)

@seecatchAllCause for a version that can recover from both recoverable and unrecoverable errors.

@since2.0.0

catchAll
((
_: NoSuchElementException | MalformedJsonError | ParseError
_
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <E | Response.NotOkError>(error: E | Response.NotOkError) => Effect.Effect<never, E | Response.NotOkError, never>

Creates an Effect that represents a recoverable error.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

Example (Creating a Failed Effect)

import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@seesucceed to create an effect that represents a successful value.

@since2.0.0

fail
(
selfErr: E | Response.NotOkError
selfErr
)), // Re-assign original error if parsing fails
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const flatMap: <TError, never, TError, never>(f: (a: TError) => Effect.Effect<never, TError, never>) => <E, R>(self: Effect.Effect<TError, E, R>) => Effect.Effect<never, TError | E, R> (+1 overload)

Chains effects to produce new Effect instances, useful for combining operations that depend on previous results.

Syntax

const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))
// or
const flatMappedEffect = Effect.flatMap(myEffect, transformation)
// or
const flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))

Details

flatMap lets you sequence effects so that the result of one effect can be used in the next step. It is similar to flatMap used with arrays but works specifically with Effect instances, allowing you to avoid deeply nested effect structures.

Since effects are immutable, flatMap always returns a new effect instead of changing the original one.

When to Use

Use flatMap when you need to chain multiple effects, ensuring that each step produces a new Effect while flattening any nested effects that may occur.

Example

import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
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)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`
const finalAmount = pipe(
fetchTransactionAmount,
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 95

@seetap for a version that ignores the result of the effect.

@since2.0.0

flatMap
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <E>(error: E) => Effect.Effect<never, E>

Creates an Effect that represents a recoverable error.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

Example (Creating a Failed Effect)

import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@seesucceed to create an effect that represents a successful value.

@since2.0.0

fail
) // Fail with the custom error
)
)
);
}
export const
const withStructuredError: {
<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>;
<A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>;
}
withStructuredError
: {
// Uncurried version
<
function (type parameter) A in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
,
function (type parameter) R in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
,
function (type parameter) TError in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
>(
self: Effect.Effect<A, Response.NotOkError | E, R>
self
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
,
function (type parameter) R in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
>,
errorCode: string
errorCode
: string,
onError: () => TError
onError
: () =>
function (type parameter) TError in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
|
function (type parameter) TError in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
,
function (type parameter) R in <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
R
>;
// Curried version
<
function (type parameter) A in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
E
,
function (type parameter) R in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
R
,
function (type parameter) TError in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
>(
errorCode: string
errorCode
: string,
onError: () => TError
onError
: () =>
function (type parameter) TError in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
): (
self: Effect.Effect<A, Response.NotOkError | E, R>
self
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
,
function (type parameter) R in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
R
>
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<
function (type parameter) A in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
A
,
function (type parameter) E in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
E
|
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
|
function (type parameter) TError in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
TError
,
function (type parameter) R in <A, E, R, TError>(errorCode: string, onError: () => TError): (self: Effect.Effect<A, E | Response.NotOkError, R>) => Effect.Effect<A, E | Response.NotOkError | TError, R>
R
>;
} =
import Function
Function
.
const dual: <(...args: Array<any>) => any, <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError) => Effect.Effect<A, E | Response.NotOkError | TError, R>>(arity: 3, body: <A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError) => Effect.Effect<A, E | Response.NotOkError | TError, R>) => ((...args: Array<any>) => any) & (<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError) => Effect.Effect<A, E | Response.NotOkError | TError, R>) (+1 overload)

Creates a function that can be used in a data-last (aka pipeable) or data-first style.

The first parameter to dual is either the arity of the uncurried function or a predicate that determines if the function is being used in a data-first or data-last style.

Using the arity is the most common use case, but there are some cases where you may want to use a predicate. For example, if you have a function that takes an optional argument, you can use a predicate to determine if the function is being used in a data-first or data-last style.

You can pass either the arity of the uncurried function or a predicate which determines if the function is being used in a data-first or data-last style.

Example (Using arity to determine data-first or data-last style)

import { dual, pipe } from "effect/Function"
const sum = dual<
(that: number) => (self: number) => number,
(self: number, that: number) => number
>(2, (self, that) => self + that)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5

Example (Using call signatures to define the overloads)

import { dual, pipe } from "effect/Function"
const sum: {
(that: number): (self: number) => number
(self: number, that: number): number
} = dual(2, (self: number, that: number): number => self + that)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5

Example (Using a predicate to determine data-first or data-last style)

import { dual, pipe } from "effect/Function"
const sum = dual<
(that: number) => (self: number) => number,
(self: number, that: number) => number
>(
(args) => args.length === 2,
(self, that) => self + that
)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5

@since2.0.0

dual
(3,
function withStructuredErrorFn<A, E, R, TError>(self: Effect.Effect<A, E | Response.NotOkError, R>, errorCode: string, onError: () => TError): Effect.Effect<A, E | Response.NotOkError | TError, R>
withStructuredErrorFn
);

Of course, you can enhance this further by adding a custom sub-schema for detailed error information or HTTP status code checks.


As you can see, most of the code is related to EffectTS itself. Fx-Fetch gives you a freedom to focus on handling the error payloads without thinking about how to not shoot yourself in the foot 🦶🔫.