Skip to content

Batching

In typical application development, when interacting with external APIs, databases, or other data sources, we often define functions that perform requests and handle their results or failures accordingly.

Here’s a basic model that outlines the structure of our data and possible errors:

// ------------------------------
// Model
// ------------------------------
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}

Let’s define functions that interact with an external API, handling common operations such as fetching todos, retrieving user details, and sending emails.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// API
// ------------------------------
// Fetches a list of todos from an external API
const
const getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/todos").
Promise<Response>.then<Todo[], never>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>
),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
// Retrieves a user by their ID from an external API
const
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
= (
id: number
id
: number) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <User, GetUserError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<User>;
readonly catch: (error: unknown) => GetUserError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<User>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
(`https://api.example.demo/getUserById?id=${
id: number
id
}`).
Promise<Response>.then<User, never>(onfulfilled?: ((value: Response) => User | PromiseLike<User>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface User
User
>
),
catch: (error: unknown) => GetUserError
catch
: () => new
constructor GetUserError(): GetUserError
GetUserError
()
})
// Sends an email via an external API
const
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
= (
address: string
address
: string,
text: string
text
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <void, SendEmailError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<void>;
readonly catch: (error: unknown) => SendEmailError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<void>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/sendEmail", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
address: string
address
,
text: string
text
})
}).
Promise<Response>.then<void, never>(onfulfilled?: ((value: Response) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void>),
catch: (error: unknown) => SendEmailError
catch
: () => new
constructor SendEmailError(): SendEmailError
SendEmailError
()
})
// Sends an email to a user by fetching their details first
const
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
= (
id: number
id
: number,
message: string
message
: string) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
id: number
id
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, SendEmailError, never>>(f: (a: User) => Effect.Effect<void, SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
(
user: User
user
.
User.email: string
email
,
message: string
message
))
)
// Notifies the owner of a todo by sending them an email
const
const notifyOwner: (todo: Todo) => Effect.Effect<void, GetUserError | SendEmailError, never>
notifyOwner
= (
todo: Todo
todo
:
interface Todo
Todo
) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
todo: Todo
todo
.
Todo.ownerId: number
ownerId
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, GetUserError | SendEmailError, never>>(f: (a: User) => Effect.Effect<void, GetUserError | SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
(
user: User
user
.
User.id: number
id
, `hey ${
user: User
user
.
User.name: string
name
} you got a todo!`)
)
)

While this approach is straightforward and readable, it may not be the most efficient. Repeated API calls, especially when many todos share the same owner, can significantly increase network overhead and slow down your application.

While these functions are clear and easy to understand, their use may not be the most efficient. For example, notifying todo owners involves repeated API calls which can be optimized.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// API
// ------------------------------
46 collapsed lines
// Fetches a list of todos from an external API
const
const getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/todos").
Promise<Response>.then<Todo[], never>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>
),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
// Retrieves a user by their ID from an external API
const
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
= (
id: number
id
: number) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <User, GetUserError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<User>;
readonly catch: (error: unknown) => GetUserError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<User>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
(`https://api.example.demo/getUserById?id=${
id: number
id
}`).
Promise<Response>.then<User, never>(onfulfilled?: ((value: Response) => User | PromiseLike<User>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface User
User
>
),
catch: (error: unknown) => GetUserError
catch
: () => new
constructor GetUserError(): GetUserError
GetUserError
()
})
// Sends an email via an external API
const
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
= (
address: string
address
: string,
text: string
text
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <void, SendEmailError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<void>;
readonly catch: (error: unknown) => SendEmailError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<void>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/sendEmail", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
address: string
address
,
text: string
text
})
}).
Promise<Response>.then<void, never>(onfulfilled?: ((value: Response) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void>),
catch: (error: unknown) => SendEmailError
catch
: () => new
constructor SendEmailError(): SendEmailError
SendEmailError
()
})
// Sends an email to a user by fetching their details first
const
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
= (
id: number
id
: number,
message: string
message
: string) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
id: number
id
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, SendEmailError, never>>(f: (a: User) => Effect.Effect<void, SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
(
user: User
user
.
User.email: string
email
,
message: string
message
))
)
// Notifies the owner of a todo by sending them an email
const
const notifyOwner: (todo: Todo) => Effect.Effect<void, GetUserError | SendEmailError, never>
notifyOwner
= (
todo: Todo
todo
:
interface Todo
Todo
) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
todo: Todo
todo
.
Todo.ownerId: number
ownerId
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, GetUserError | SendEmailError, never>>(f: (a: User) => Effect.Effect<void, GetUserError | SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
(
user: User
user
.
User.id: number
id
, `hey ${
user: User
user
.
User.name: string
name
} you got a todo!`)
)
)
// Orchestrates operations on todos, notifying their owners
const
const program: Effect.Effect<void, GetUserError | GetTodosError | SendEmailError, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Todo[], GetTodosError, never>> | YieldWrap<Effect.Effect<void[], GetUserError | SendEmailError, never>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

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

When to Use

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 todos: Todo[]
todos
= yield*
const getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, GetUserError | SendEmailError, never, Todo[]>(self: Todo[], f: (a: Todo, i: number) => Effect.Effect<void, GetUserError | SendEmailError, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
const todos: Todo[]
todos
, (
todo: Todo
todo
) =>
const notifyOwner: (todo: Todo) => Effect.Effect<void, GetUserError | SendEmailError, never>
notifyOwner
(
todo: Todo
todo
), {
concurrency?: Concurrency | undefined
concurrency
: "unbounded"
})
})

This implementation performs an API call for each todo to fetch the owner’s details and send an email. If multiple todos have the same owner, this results in redundant API calls.

Let’s assume that getUserById and sendEmail can be batched. This means that we can send multiple requests in a single HTTP call, reducing the number of API requests and improving performance.

Step-by-Step Guide to Batching

  1. Declaring Requests: We’ll start by transforming our requests into structured data models. This involves detailing input parameters, expected outputs, and possible errors. Structuring requests this way not only helps in efficiently managing data but also in comparing different requests to understand if they refer to the same input parameters.

  2. Declaring Resolvers: Resolvers are designed to handle multiple requests simultaneously. By leveraging the ability to compare requests (ensuring they refer to the same input parameters), resolvers can execute several requests in one go, maximizing the utility of batching.

  3. Defining Queries: Finally, we’ll define queries that utilize these batch-resolvers to perform operations. This step ties together the structured requests and their corresponding resolvers into functional components of the application.

We’ll design a model using the concept of a Request that a data source might support:

Request<Value, Error>

A Request is a construct representing a request for a value of type Value, which might fail with an error of type Error.

Let’s start by defining a structured model for the types of requests our data sources can handle.

import {
import Request
Request
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// Requests
// ------------------------------
// Define a request to get multiple Todo items which might
// fail with a GetTodosError
interface
interface GetTodos
GetTodos
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> {
readonly
GetTodos._tag: "GetTodos"
_tag
: "GetTodos"
}
// Create a tagged constructor for GetTodos requests
const
const GetTodos: Request.Request.Constructor<GetTodos, "_tag">
GetTodos
=
import Request
Request
.
const tagged: <GetTodos>(tag: "GetTodos") => Request.Request<out A, out E = never>.Constructor<GetTodos, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetTodos
GetTodos
>("GetTodos")
// Define a request to fetch a User by ID which might
// fail with a GetUserError
interface
interface GetUserById
GetUserById
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface User
User
,
class GetUserError
GetUserError
> {
readonly
GetUserById._tag: "GetUserById"
_tag
: "GetUserById"
readonly
GetUserById.id: number
id
: number
}
// Create a tagged constructor for GetUserById requests
const
const GetUserById: Request.Request.Constructor<GetUserById, "_tag">
GetUserById
=
import Request
Request
.
const tagged: <GetUserById>(tag: "GetUserById") => Request.Request<out A, out E = never>.Constructor<GetUserById, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetUserById
GetUserById
>("GetUserById")
// Define a request to send an email which might
// fail with a SendEmailError
interface
interface SendEmail
SendEmail
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<void,
class SendEmailError
SendEmailError
> {
readonly
SendEmail._tag: "SendEmail"
_tag
: "SendEmail"
readonly
SendEmail.address: string
address
: string
readonly
SendEmail.text: string
text
: string
}
// Create a tagged constructor for SendEmail requests
const
const SendEmail: Request.Request.Constructor<SendEmail, "_tag">
SendEmail
=
import Request
Request
.
const tagged: <SendEmail>(tag: "SendEmail") => Request.Request<out A, out E = never>.Constructor<SendEmail, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface SendEmail
SendEmail
>("SendEmail")

Each request is defined with a specific data structure that extends from a generic Request type, ensuring that each request carries its unique data requirements along with a specific error type.

By using tagged constructors like Request.tagged, we can easily instantiate request objects that are recognizable and manageable throughout the application.

After defining our requests, the next step is configuring how Effect resolves these requests using RequestResolver:

RequestResolver<A, R>

A RequestResolver requires an environment R and is capable of executing requests of type A.

In this section, we’ll create individual resolvers for each type of request. The granularity of your resolvers can vary, but typically, they are divided based on the batching capabilities of the corresponding API calls.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Request
Request
,
import RequestResolver
RequestResolver
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// Requests
// ------------------------------
29 collapsed lines
// Define a request to get multiple Todo items which might
// fail with a GetTodosError
interface
interface GetTodos
GetTodos
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> {
readonly
GetTodos._tag: "GetTodos"
_tag
: "GetTodos"
}
// Create a tagged constructor for GetTodos requests
const
const GetTodos: Request.Request.Constructor<GetTodos, "_tag">
GetTodos
=
import Request
Request
.
const tagged: <GetTodos>(tag: "GetTodos") => Request.Request<out A, out E = never>.Constructor<GetTodos, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetTodos
GetTodos
>("GetTodos")
// Define a request to fetch a User by ID which might
// fail with a GetUserError
interface
interface GetUserById
GetUserById
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface User
User
,
class GetUserError
GetUserError
> {
readonly
GetUserById._tag: "GetUserById"
_tag
: "GetUserById"
readonly
GetUserById.id: number
id
: number
}
// Create a tagged constructor for GetUserById requests
const
const GetUserById: Request.Request.Constructor<GetUserById, "_tag">
GetUserById
=
import Request
Request
.
const tagged: <GetUserById>(tag: "GetUserById") => Request.Request<out A, out E = never>.Constructor<GetUserById, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetUserById
GetUserById
>("GetUserById")
// Define a request to send an email which might
// fail with a SendEmailError
interface
interface SendEmail
SendEmail
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<void,
class SendEmailError
SendEmailError
> {
readonly
SendEmail._tag: "SendEmail"
_tag
: "SendEmail"
readonly
SendEmail.address: string
address
: string
readonly
SendEmail.text: string
text
: string
}
// Create a tagged constructor for SendEmail requests
const
const SendEmail: Request.Request.Constructor<SendEmail, "_tag">
SendEmail
=
import Request
Request
.
const tagged: <SendEmail>(tag: "SendEmail") => Request.Request<out A, out E = never>.Constructor<SendEmail, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface SendEmail
SendEmail
>("SendEmail")
// ------------------------------
// Resolvers
// ------------------------------
// Assuming GetTodos cannot be batched, we create a standard resolver
const
const GetTodosResolver: RequestResolver.RequestResolver<GetTodos, never>
GetTodosResolver
=
import RequestResolver
RequestResolver
.
const fromEffect: <never, GetTodos>(f: (a: GetTodos) => Effect.Effect<Todo[], GetTodosError, never>) => RequestResolver.RequestResolver<GetTodos, never>

Constructs a data source from an effectual function.

@since2.0.0

fromEffect
(
(
_: GetTodos
_
:
interface GetTodos
GetTodos
):
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 lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<
interface Todo
Todo
[],
class GetTodosError
GetTodosError
> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/todos").
Promise<Response>.then<Todo[], Todo[]>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => Todo[] | PromiseLike<...>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>
),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
)
// Assuming GetUserById can be batched, we create a batched resolver
const
const GetUserByIdResolver: RequestResolver.RequestResolver<GetUserById, never>
GetUserByIdResolver
=
import RequestResolver
RequestResolver
.
const makeBatched: <GetUserById, never>(run: (requests: [GetUserById, ...GetUserById[]]) => Effect.Effect<void, never, never>) => RequestResolver.RequestResolver<GetUserById, never>

Constructs a data source from a function taking a collection of requests.

@since2.0.0

makeBatched
(
(
requests: readonly GetUserById[]
requests
:
interface ReadonlyArray<T>
ReadonlyArray
<
interface GetUserById
GetUserById
>) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <User[], GetUserError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<User[]>;
readonly catch: (error: unknown) => GetUserError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<User[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/getUserByIdBatch", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
users: {
id: number;
}[]
users
:
requests: readonly GetUserById[]
requests
.
ReadonlyArray<GetUserById>.map<{
id: number;
}>(callbackfn: (value: GetUserById, index: number, array: readonly GetUserById[]) => {
id: number;
}, thisArg?: any): {
id: number;
}[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
(({
id: number
id
}) => ({
id: number
id
}))
})
}).
Promise<Response>.then<unknown, User[]>(onfulfilled?: ((value: Response) => unknown) | null | undefined, onrejected?: ((reason: any) => User[] | PromiseLike<User[]>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
()) as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface User
User
>>,
catch: (error: unknown) => GetUserError
catch
: () => new
constructor GetUserError(): GetUserError
GetUserError
()
}).
Pipeable.pipe<Effect.Effect<User[], GetUserError, never>, Effect.Effect<void[], GetUserError, never>, Effect.Effect<void[], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User[], Effect.Effect<void[], never, never>>(f: (a: User[]) => Effect.Effect<void[], never, never>) => <E, R>(self: Effect.Effect<User[], E, R>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
users: User[]
users
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly GetUserById[]>(self: readonly GetUserById[], f: (a: GetUserById, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly GetUserById[]
requests
, (
request: GetUserById
request
,
index: number
index
) =>
import Request
Request
.
const completeEffect: <GetUserById, never>(self: GetUserById, effect: Effect.Effect<User, GetUserError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: GetUserById
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <User>(value: User) => Effect.Effect<User, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
(
users: User[]
users
[
index: number
index
]!))
)
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <GetUserError, void[], never, never>(f: (e: GetUserError) => Effect.Effect<void[], never, never>) => <A, R>(self: Effect.Effect<A, GetUserError, R>) => Effect.Effect<...> (+1 overload)

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

Details

The catchAll 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: catchAll only handles recoverable errors. It will not recover from unrecoverable defects.

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

@example

// Title: 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}`)
)
)

@since2.0.0

catchAll
((
error: GetUserError
error
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly GetUserById[]>(self: readonly GetUserById[], f: (a: GetUserById, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly GetUserById[]
requests
, (
request: GetUserById
request
) =>
import Request
Request
.
const completeEffect: <GetUserById, never>(self: GetUserById, effect: Effect.Effect<User, GetUserError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: GetUserById
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <GetUserError>(error: GetUserError) => Effect.Effect<never, GetUserError, 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

.

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

@example

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

@since2.0.0

fail
(
error: GetUserError
error
))
)
)
)
)
// Assuming SendEmail can be batched, we create a batched resolver
const
const SendEmailResolver: RequestResolver.RequestResolver<SendEmail, never>
SendEmailResolver
=
import RequestResolver
RequestResolver
.
const makeBatched: <SendEmail, never>(run: (requests: [SendEmail, ...SendEmail[]]) => Effect.Effect<void, never, never>) => RequestResolver.RequestResolver<SendEmail, never>

Constructs a data source from a function taking a collection of requests.

@since2.0.0

makeBatched
(
(
requests: readonly SendEmail[]
requests
:
interface ReadonlyArray<T>
ReadonlyArray
<
interface SendEmail
SendEmail
>) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <void, SendEmailError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<void>;
readonly catch: (error: unknown) => SendEmailError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<void>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/sendEmailBatch", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
emails: {
address: string;
text: string;
}[]
emails
:
requests: readonly SendEmail[]
requests
.
ReadonlyArray<SendEmail>.map<{
address: string;
text: string;
}>(callbackfn: (value: SendEmail, index: number, array: readonly SendEmail[]) => {
address: string;
text: string;
}, thisArg?: any): {
address: string;
text: string;
}[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
(({
address: string
address
,
text: string
text
}) => ({
address: string
address
,
text: string
text
}))
})
}).
Promise<Response>.then<void, never>(onfulfilled?: ((value: Response) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void>),
catch: (error: unknown) => SendEmailError
catch
: () => new
constructor SendEmailError(): SendEmailError
SendEmailError
()
}).
Pipeable.pipe<Effect.Effect<void, SendEmailError, never>, Effect.Effect<void[], SendEmailError, never>, Effect.Effect<void[], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Effect.Effect<void[], never, never>>(f: Effect.Effect<void[], never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<void[], E, R> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly SendEmail[]>(self: readonly SendEmail[], f: (a: SendEmail, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly SendEmail[]
requests
, (
request: SendEmail
request
) =>
import Request
Request
.
const completeEffect: <SendEmail, never>(self: SendEmail, effect: Effect.Effect<void, SendEmailError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: SendEmail
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
)
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <SendEmailError, void[], never, never>(f: (e: SendEmailError) => Effect.Effect<void[], never, never>) => <A, R>(self: Effect.Effect<A, SendEmailError, R>) => Effect.Effect<...> (+1 overload)

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

Details

The catchAll 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: catchAll only handles recoverable errors. It will not recover from unrecoverable defects.

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

@example

// Title: 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}`)
)
)

@since2.0.0

catchAll
((
error: SendEmailError
error
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly SendEmail[]>(self: readonly SendEmail[], f: (a: SendEmail, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly SendEmail[]
requests
, (
request: SendEmail
request
) =>
import Request
Request
.
const completeEffect: <SendEmail, never>(self: SendEmail, effect: Effect.Effect<void, SendEmailError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: SendEmail
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <SendEmailError>(error: SendEmailError) => Effect.Effect<never, SendEmailError, 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

.

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

@example

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

@since2.0.0

fail
(
error: SendEmailError
error
))
)
)
)
)

In this configuration:

  • GetTodosResolver handles the fetching of multiple Todo items. It’s set up as a standard resolver since we assume it cannot be batched.
  • GetUserByIdResolver and SendEmailResolver are configured as batched resolvers. This setup is based on the assumption that these requests can be processed in batches, enhancing performance and reducing the number of API calls.

Now that we’ve set up our resolvers, we’re ready to tie all the pieces together to define our This step will enable us to perform data operations effectively within our application.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Request
Request
,
import RequestResolver
RequestResolver
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// Requests
// ------------------------------
29 collapsed lines
// Define a request to get multiple Todo items which might
// fail with a GetTodosError
interface
interface GetTodos
GetTodos
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> {
readonly
GetTodos._tag: "GetTodos"
_tag
: "GetTodos"
}
// Create a tagged constructor for GetTodos requests
const
const GetTodos: Request.Request.Constructor<GetTodos, "_tag">
GetTodos
=
import Request
Request
.
const tagged: <GetTodos>(tag: "GetTodos") => Request.Request<out A, out E = never>.Constructor<GetTodos, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetTodos
GetTodos
>("GetTodos")
// Define a request to fetch a User by ID which might
// fail with a GetUserError
interface
interface GetUserById
GetUserById
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface User
User
,
class GetUserError
GetUserError
> {
readonly
GetUserById._tag: "GetUserById"
_tag
: "GetUserById"
readonly
GetUserById.id: number
id
: number
}
// Create a tagged constructor for GetUserById requests
const
const GetUserById: Request.Request.Constructor<GetUserById, "_tag">
GetUserById
=
import Request
Request
.
const tagged: <GetUserById>(tag: "GetUserById") => Request.Request<out A, out E = never>.Constructor<GetUserById, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetUserById
GetUserById
>("GetUserById")
// Define a request to send an email which might
// fail with a SendEmailError
interface
interface SendEmail
SendEmail
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<void,
class SendEmailError
SendEmailError
> {
readonly
SendEmail._tag: "SendEmail"
_tag
: "SendEmail"
readonly
SendEmail.address: string
address
: string
readonly
SendEmail.text: string
text
: string
}
// Create a tagged constructor for SendEmail requests
const
const SendEmail: Request.Request.Constructor<SendEmail, "_tag">
SendEmail
=
import Request
Request
.
const tagged: <SendEmail>(tag: "SendEmail") => Request.Request<out A, out E = never>.Constructor<SendEmail, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface SendEmail
SendEmail
>("SendEmail")
// ------------------------------
// Resolvers
// ------------------------------
72 collapsed lines
// Assuming GetTodos cannot be batched, we create a standard resolver
const
const GetTodosResolver: RequestResolver.RequestResolver<GetTodos, never>
GetTodosResolver
=
import RequestResolver
RequestResolver
.
const fromEffect: <never, GetTodos>(f: (a: GetTodos) => Effect.Effect<Todo[], GetTodosError, never>) => RequestResolver.RequestResolver<GetTodos, never>

Constructs a data source from an effectual function.

@since2.0.0

fromEffect
(
(
_: GetTodos
_
:
interface GetTodos
GetTodos
):
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 lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<
interface Todo
Todo
[],
class GetTodosError
GetTodosError
> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/todos").
Promise<Response>.then<Todo[], Todo[]>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => Todo[] | PromiseLike<...>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
(
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>
),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
)
// Assuming GetUserById can be batched, we create a batched resolver
const
const GetUserByIdResolver: RequestResolver.RequestResolver<GetUserById, never>
GetUserByIdResolver
=
import RequestResolver
RequestResolver
.
const makeBatched: <GetUserById, never>(run: (requests: [GetUserById, ...GetUserById[]]) => Effect.Effect<void, never, never>) => RequestResolver.RequestResolver<GetUserById, never>

Constructs a data source from a function taking a collection of requests.

@since2.0.0

makeBatched
(
(
requests: readonly GetUserById[]
requests
:
interface ReadonlyArray<T>
ReadonlyArray
<
interface GetUserById
GetUserById
>) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <User[], GetUserError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<User[]>;
readonly catch: (error: unknown) => GetUserError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<User[]>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/getUserByIdBatch", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
users: {
id: number;
}[]
users
:
requests: readonly GetUserById[]
requests
.
ReadonlyArray<GetUserById>.map<{
id: number;
}>(callbackfn: (value: GetUserById, index: number, array: readonly GetUserById[]) => {
id: number;
}, thisArg?: any): {
id: number;
}[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
(({
id: number
id
}) => ({
id: number
id
}))
})
}).
Promise<Response>.then<unknown, User[]>(onfulfilled?: ((value: Response) => unknown) | null | undefined, onrejected?: ((reason: any) => User[] | PromiseLike<User[]>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
()) as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface User
User
>>,
catch: (error: unknown) => GetUserError
catch
: () => new
constructor GetUserError(): GetUserError
GetUserError
()
}).
Pipeable.pipe<Effect.Effect<User[], GetUserError, never>, Effect.Effect<void[], GetUserError, never>, Effect.Effect<void[], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User[], Effect.Effect<void[], never, never>>(f: (a: User[]) => Effect.Effect<void[], never, never>) => <E, R>(self: Effect.Effect<User[], E, R>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
users: User[]
users
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly GetUserById[]>(self: readonly GetUserById[], f: (a: GetUserById, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly GetUserById[]
requests
, (
request: GetUserById
request
,
index: number
index
) =>
import Request
Request
.
const completeEffect: <GetUserById, never>(self: GetUserById, effect: Effect.Effect<User, GetUserError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: GetUserById
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <User>(value: User) => Effect.Effect<User, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
(
users: User[]
users
[
index: number
index
]!))
)
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <GetUserError, void[], never, never>(f: (e: GetUserError) => Effect.Effect<void[], never, never>) => <A, R>(self: Effect.Effect<A, GetUserError, R>) => Effect.Effect<...> (+1 overload)

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

Details

The catchAll 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: catchAll only handles recoverable errors. It will not recover from unrecoverable defects.

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

@example

// Title: 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}`)
)
)

@since2.0.0

catchAll
((
error: GetUserError
error
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly GetUserById[]>(self: readonly GetUserById[], f: (a: GetUserById, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly GetUserById[]
requests
, (
request: GetUserById
request
) =>
import Request
Request
.
const completeEffect: <GetUserById, never>(self: GetUserById, effect: Effect.Effect<User, GetUserError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: GetUserById
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <GetUserError>(error: GetUserError) => Effect.Effect<never, GetUserError, 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

.

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

@example

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

@since2.0.0

fail
(
error: GetUserError
error
))
)
)
)
)
// Assuming SendEmail can be batched, we create a batched resolver
const
const SendEmailResolver: RequestResolver.RequestResolver<SendEmail, never>
SendEmailResolver
=
import RequestResolver
RequestResolver
.
const makeBatched: <SendEmail, never>(run: (requests: [SendEmail, ...SendEmail[]]) => Effect.Effect<void, never, never>) => RequestResolver.RequestResolver<SendEmail, never>

Constructs a data source from a function taking a collection of requests.

@since2.0.0

makeBatched
(
(
requests: readonly SendEmail[]
requests
:
interface ReadonlyArray<T>
ReadonlyArray
<
interface SendEmail
SendEmail
>) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <void, SendEmailError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<void>;
readonly catch: (error: unknown) => SendEmailError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<void>
try
: () =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
("https://api.example.demo/sendEmailBatch", {
RequestInit.method?: string
method
: "POST",
RequestInit.headers?: HeadersInit
headers
: {
"Content-Type": "application/json"
},
RequestInit.body?: BodyInit
body
:
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

stringify
({
emails: {
address: string;
text: string;
}[]
emails
:
requests: readonly SendEmail[]
requests
.
ReadonlyArray<SendEmail>.map<{
address: string;
text: string;
}>(callbackfn: (value: SendEmail, index: number, array: readonly SendEmail[]) => {
address: string;
text: string;
}, thisArg?: any): {
address: string;
text: string;
}[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
(({
address: string
address
,
text: string
text
}) => ({
address: string
address
,
text: string
text
}))
})
}).
Promise<Response>.then<void, never>(onfulfilled?: ((value: Response) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void>),
catch: (error: unknown) => SendEmailError
catch
: () => new
constructor SendEmailError(): SendEmailError
SendEmailError
()
}).
Pipeable.pipe<Effect.Effect<void, SendEmailError, never>, Effect.Effect<void[], SendEmailError, never>, Effect.Effect<void[], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Effect.Effect<void[], never, never>>(f: Effect.Effect<void[], never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<void[], E, R> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly SendEmail[]>(self: readonly SendEmail[], f: (a: SendEmail, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly SendEmail[]
requests
, (
request: SendEmail
request
) =>
import Request
Request
.
const completeEffect: <SendEmail, never>(self: SendEmail, effect: Effect.Effect<void, SendEmailError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: SendEmail
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
)
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchAll: <SendEmailError, void[], never, never>(f: (e: SendEmailError) => Effect.Effect<void[], never, never>) => <A, R>(self: Effect.Effect<A, SendEmailError, R>) => Effect.Effect<...> (+1 overload)

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

Details

The catchAll 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: catchAll only handles recoverable errors. It will not recover from unrecoverable defects.

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

@example

// Title: 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}`)
)
)

@since2.0.0

catchAll
((
error: SendEmailError
error
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const forEach: <void, never, never, readonly SendEmail[]>(self: readonly SendEmail[], f: (a: SendEmail, i: number) => Effect.Effect<void, never, never>, options?: {
readonly concurrency?: Concurrency | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly discard?: false | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => Effect.Effect<...> (+3 overloads)

Executes an effectful operation for each element in an Iterable.

Details

The forEach function applies a provided operation to each element in the iterable, producing a new effect that returns an array of results.

If any effect fails, the iteration stops immediately (short-circuiting), and the error is propagated.

Concurrency

The concurrency option controls how many operations are performed concurrently. By default, the operations are performed sequentially.

Discarding Results

If the discard option is set to true, the intermediate results are not collected, and the final result of the operation is void.

@seeall for combining multiple effects into one.

@example

// Title: Applying Effects to Iterable Elements
import { Effect, Console } from "effect"
const result = Effect.forEach([1, 2, 3, 4, 5], (n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]

@example

// Title: Using discard to Ignore Results import { Effect, Console } from "effect"

// Apply effects but discard the results const result = Effect.forEach( [1, 2, 3, 4, 5], (n, index) => Console.log(Currently at index ${index}).pipe(Effect.as(n * 2)), { discard: true } )

Effect.runPromise(result).then(console.log) // Output: // Currently at index 0 // Currently at index 1 // Currently at index 2 // Currently at index 3 // Currently at index 4 // undefined

@since2.0.0

forEach
(
requests: readonly SendEmail[]
requests
, (
request: SendEmail
request
) =>
import Request
Request
.
const completeEffect: <SendEmail, never>(self: SendEmail, effect: Effect.Effect<void, SendEmailError, never>) => Effect.Effect<void, never, never> (+1 overload)

Complete a Request with the specified effectful computation, failing the request with the error from the effect workflow if it fails, and completing the request with the value of the effect workflow if it succeeds.

@since2.0.0

completeEffect
(
request: SendEmail
request
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <SendEmailError>(error: SendEmailError) => Effect.Effect<never, SendEmailError, 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

.

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

@example

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

@since2.0.0

fail
(
error: SendEmailError
error
))
)
)
)
)
// ------------------------------
// Queries
// ------------------------------
// Defines a query to fetch all Todo items
const
const getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
:
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 lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> =
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const request: <RequestResolver.RequestResolver<GetTodos, never>, GetTodos>(self: GetTodos, dataSource: RequestResolver.RequestResolver<GetTodos, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

request
(
const GetTodos: Request.Request<out A, out E = never>.Constructor
(args: Omit<GetTodos, typeof Request.RequestTypeId | "_tag">) => GetTodos
GetTodos
({}),
const GetTodosResolver: RequestResolver.RequestResolver<GetTodos, never>
GetTodosResolver
)
// Defines a query to fetch a user by their ID
const
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
= (
id: number
id
: number) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const request: <RequestResolver.RequestResolver<GetUserById, never>, GetUserById>(self: GetUserById, dataSource: RequestResolver.RequestResolver<GetUserById, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

request
(
const GetUserById: Request.Request<out A, out E = never>.Constructor
(args: Omit<GetUserById, typeof Request.RequestTypeId | "_tag">) => GetUserById
GetUserById
({
id: number
id
}),
const GetUserByIdResolver: RequestResolver.RequestResolver<GetUserById, never>
GetUserByIdResolver
)
// Defines a query to send an email to a specific address
const
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
= (
address: string
address
: string,
text: string
text
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const request: <RequestResolver.RequestResolver<SendEmail, never>, SendEmail>(self: SendEmail, dataSource: RequestResolver.RequestResolver<SendEmail, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

request
(
const SendEmail: Request.Request<out A, out E = never>.Constructor
(args: Omit<SendEmail, typeof Request.RequestTypeId | "_tag">) => SendEmail
SendEmail
({
address: string
address
,
text: string
text
}),
const SendEmailResolver: RequestResolver.RequestResolver<SendEmail, never>
SendEmailResolver
)
// Composes getUserById and sendEmail to send an email to a specific user
const
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
= (
id: number
id
: number,
message: string
message
: string) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
id: number
id
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, SendEmailError, never>>(f: (a: User) => Effect.Effect<void, SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmail: (address: string, text: string) => Effect.Effect<void, SendEmailError, never>
sendEmail
(
user: User
user
.
User.email: string
email
,
message: string
message
))
)
// Uses getUserById to fetch the owner of a Todo and then sends them an email notification
const
const notifyOwner: (todo: Todo) => Effect.Effect<void, GetUserError | SendEmailError, never>
notifyOwner
= (
todo: Todo
todo
:
interface Todo
Todo
) =>
const getUserById: (id: number) => Effect.Effect<User, GetUserError, never>
getUserById
(
todo: Todo
todo
.
Todo.ownerId: number
ownerId
).
Pipeable.pipe<Effect.Effect<User, GetUserError, never>, Effect.Effect<void, GetUserError | SendEmailError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <User, Effect.Effect<void, GetUserError | SendEmailError, never>>(f: (a: User) => Effect.Effect<void, GetUserError | SendEmailError, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
user: User
user
) =>
const sendEmailToUser: (id: number, message: string) => Effect.Effect<void, GetUserError | SendEmailError, never>
sendEmailToUser
(
user: User
user
.
User.id: number
id
, `hey ${
user: User
user
.
User.name: string
name
} you got a todo!`)
)
)

By using the Effect.request function, we integrate the resolvers with the request model effectively. This approach ensures that each query is optimally resolved using the appropriate resolver.

Although the code structure looks similar to earlier examples, employing resolvers significantly enhances efficiency by optimizing how requests are handled and reducing unnecessary API calls.

const program = Effect.gen(function* () {
const todos = yield* getTodos
yield* Effect.forEach(todos, (todo) => notifyOwner(todo), {
batching: true
})
})

In the final setup, this program will execute only 3 queries to the APIs, regardless of the number of todos. This contrasts sharply with the traditional approach, which would potentially execute 1 + 2n queries, where n is the number of todos. This represents a significant improvement in efficiency, especially for applications with a high volume of data interactions.

Batching can be locally disabled using the Effect.withRequestBatching utility in the following way:

const program = Effect.gen(function* () {
const todos = yield* getTodos
yield* Effect.forEach(todos, (todo) => notifyOwner(todo), {
concurrency: "unbounded"
})
}).pipe(Effect.withRequestBatching(false))

In complex applications, resolvers often need access to shared services or configurations to handle requests effectively. However, maintaining the ability to batch requests while providing the necessary context can be challenging. Here, we’ll explore how to manage context in resolvers to ensure that batching capabilities are not compromised.

When creating request resolvers, it’s crucial to manage the context carefully. Providing too much context or providing varying services to resolvers can make them incompatible for batching. To prevent such issues, the context for the resolver used in Effect.request is explicitly set to never. This forces developers to clearly define how the context is accessed and used within resolvers.

Consider the following example where we set up an HTTP service that the resolvers can use to execute API calls:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import RequestResolver
RequestResolver
,
import Request
Request
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// Requests
// ------------------------------
29 collapsed lines
// Define a request to get multiple Todo items which might
// fail with a GetTodosError
interface
interface GetTodos
GetTodos
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> {
readonly
GetTodos._tag: "GetTodos"
_tag
: "GetTodos"
}
// Create a tagged constructor for GetTodos requests
const
const GetTodos: Request.Request.Constructor<GetTodos, "_tag">
GetTodos
=
import Request
Request
.
const tagged: <GetTodos>(tag: "GetTodos") => Request.Request<out A, out E = never>.Constructor<GetTodos, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetTodos
GetTodos
>("GetTodos")
// Define a request to fetch a User by ID which might
// fail with a GetUserError
interface
interface GetUserById
GetUserById
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface User
User
,
class GetUserError
GetUserError
> {
readonly
GetUserById._tag: "GetUserById"
_tag
: "GetUserById"
readonly
GetUserById.id: number
id
: number
}
// Create a tagged constructor for GetUserById requests
const
const GetUserById: Request.Request.Constructor<GetUserById, "_tag">
GetUserById
=
import Request
Request
.
const tagged: <GetUserById>(tag: "GetUserById") => Request.Request<out A, out E = never>.Constructor<GetUserById, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetUserById
GetUserById
>("GetUserById")
// Define a request to send an email which might
// fail with a SendEmailError
interface
interface SendEmail
SendEmail
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<void,
class SendEmailError
SendEmailError
> {
readonly
SendEmail._tag: "SendEmail"
_tag
: "SendEmail"
readonly
SendEmail.address: string
address
: string
readonly
SendEmail.text: string
text
: string
}
// Create a tagged constructor for SendEmail requests
const
const SendEmail: Request.Request.Constructor<SendEmail, "_tag">
SendEmail
=
import Request
Request
.
const tagged: <SendEmail>(tag: "SendEmail") => Request.Request<out A, out E = never>.Constructor<SendEmail, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface SendEmail
SendEmail
>("SendEmail")
// ------------------------------
// Resolvers With Context
// ------------------------------
class
class HttpService
HttpService
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"HttpService">(id: "HttpService") => <Self, Shape>() => Context.TagClass<Self, "HttpService", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("HttpService")<
class HttpService
HttpService
,
{
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
fetch
: typeof
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
}
>() {}
const
const GetTodosResolver: Effect.Effect<RequestResolver.RequestResolver<GetTodos, never>, never, HttpService>
GetTodosResolver
=
// we create a normal resolver like we did before
import RequestResolver
RequestResolver
.
const fromEffect: <HttpService, GetTodos>(f: (a: GetTodos) => Effect.Effect<Todo[], GetTodosError, HttpService>) => RequestResolver.RequestResolver<...>

Constructs a data source from an effectual function.

@since2.0.0

fromEffect
((
_: GetTodos
_
:
interface GetTodos
GetTodos
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <{
fetch: typeof fetch;
}, never, HttpService, Effect.Effect<Todo[], GetTodosError, never>>(self: Effect.Effect<{
fetch: typeof fetch;
}, never, HttpService>, f: (a: {
fetch: typeof fetch;
}) => Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
class HttpService
HttpService
, (
http: {
fetch: typeof fetch;
}
http
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
http: {
fetch: typeof fetch;
}
http
.
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
fetch
("https://api.example.demo/todos")
.
Promise<Response>.then<Todo[], never>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
)
).
Pipeable.pipe<RequestResolver.RequestResolver<GetTodos, HttpService>, Effect.Effect<RequestResolver.RequestResolver<GetTodos, never>, never, HttpService>>(this: RequestResolver.RequestResolver<...>, ab: (_: RequestResolver.RequestResolver<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
// we list the tags that the resolver can access
import RequestResolver
RequestResolver
.
const contextFromServices: <[typeof HttpService]>(services_0: typeof HttpService) => <R, A>(self: RequestResolver.RequestResolver<A, R>) => Effect.Effect<RequestResolver.RequestResolver<A, Exclude<R, HttpService>>, never, HttpService>

@since2.0.0

contextFromServices
(
class HttpService
HttpService
)
)

We can see now that the type of GetTodosResolver is no longer a RequestResolver but instead it is:

const GetTodosResolver: Effect<
RequestResolver<GetTodos, never>,
never,
HttpService
>

which is an effect that access the HttpService and returns a composed resolver that has the minimal context ready to use.

Once we have such effect we can directly use it in our query definition:

const getTodos: Effect.Effect<Todo[], GetTodosError, HttpService> =
Effect.request(GetTodos({}), GetTodosResolver)

We can see that the Effect correctly requires HttpService to be provided.

Alternatively you can create RequestResolvers as part of layers direcly accessing or closing over context from construction.

Example

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import RequestResolver
RequestResolver
,
import Request
Request
,
import Layer
Layer
} from "effect"
// ------------------------------
// Model
// ------------------------------
25 collapsed lines
interface
interface User
User
{
readonly
User._tag: "User"
_tag
: "User"
readonly
User.id: number
id
: number
readonly
User.name: string
name
: string
readonly
User.email: string
email
: string
}
class
class GetUserError
GetUserError
{
readonly
GetUserError._tag: "GetUserError"
_tag
= "GetUserError"
}
interface
interface Todo
Todo
{
readonly
Todo._tag: "Todo"
_tag
: "Todo"
readonly
Todo.id: number
id
: number
readonly
Todo.message: string
message
: string
readonly
Todo.ownerId: number
ownerId
: number
}
class
class GetTodosError
GetTodosError
{
readonly
GetTodosError._tag: "GetTodosError"
_tag
= "GetTodosError"
}
class
class SendEmailError
SendEmailError
{
readonly
SendEmailError._tag: "SendEmailError"
_tag
= "SendEmailError"
}
// ------------------------------
// Requests
// ------------------------------
29 collapsed lines
// Define a request to get multiple Todo items which might
// fail with a GetTodosError
interface
interface GetTodos
GetTodos
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
> {
readonly
GetTodos._tag: "GetTodos"
_tag
: "GetTodos"
}
// Create a tagged constructor for GetTodos requests
const
const GetTodos: Request.Request.Constructor<GetTodos, "_tag">
GetTodos
=
import Request
Request
.
const tagged: <GetTodos>(tag: "GetTodos") => Request.Request<out A, out E = never>.Constructor<GetTodos, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetTodos
GetTodos
>("GetTodos")
// Define a request to fetch a User by ID which might
// fail with a GetUserError
interface
interface GetUserById
GetUserById
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<
interface User
User
,
class GetUserError
GetUserError
> {
readonly
GetUserById._tag: "GetUserById"
_tag
: "GetUserById"
readonly
GetUserById.id: number
id
: number
}
// Create a tagged constructor for GetUserById requests
const
const GetUserById: Request.Request.Constructor<GetUserById, "_tag">
GetUserById
=
import Request
Request
.
const tagged: <GetUserById>(tag: "GetUserById") => Request.Request<out A, out E = never>.Constructor<GetUserById, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface GetUserById
GetUserById
>("GetUserById")
// Define a request to send an email which might
// fail with a SendEmailError
interface
interface SendEmail
SendEmail
extends
import Request
Request
.
interface Request<out A, out E = never>

A Request<A, E> is a request from a data source for a value of type A that may fail with an E.

@since2.0.0

@since2.0.0

Request
<void,
class SendEmailError
SendEmailError
> {
readonly
SendEmail._tag: "SendEmail"
_tag
: "SendEmail"
readonly
SendEmail.address: string
address
: string
readonly
SendEmail.text: string
text
: string
}
// Create a tagged constructor for SendEmail requests
const
const SendEmail: Request.Request.Constructor<SendEmail, "_tag">
SendEmail
=
import Request
Request
.
const tagged: <SendEmail>(tag: "SendEmail") => Request.Request<out A, out E = never>.Constructor<SendEmail, "_tag">

Constructs a new Request.

@since2.0.0

tagged
<
interface SendEmail
SendEmail
>("SendEmail")
// ------------------------------
// Resolvers With Context
// ------------------------------
21 collapsed lines
class
class HttpService
HttpService
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"HttpService">(id: "HttpService") => <Self, Shape>() => Context.TagClass<Self, "HttpService", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("HttpService")<
class HttpService
HttpService
,
{
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
fetch
: typeof
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
fetch
}
>() {}
const
const GetTodosResolver: Effect.Effect<RequestResolver.RequestResolver<GetTodos, never>, never, HttpService>
GetTodosResolver
=
// we create a normal resolver like we did before
import RequestResolver
RequestResolver
.
const fromEffect: <HttpService, GetTodos>(f: (a: GetTodos) => Effect.Effect<Todo[], GetTodosError, HttpService>) => RequestResolver.RequestResolver<...>

Constructs a data source from an effectual function.

@since2.0.0

fromEffect
((
_: GetTodos
_
:
interface GetTodos
GetTodos
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <{
fetch: typeof fetch;
}, never, HttpService, Effect.Effect<Todo[], GetTodosError, never>>(self: Effect.Effect<{
fetch: typeof fetch;
}, never, HttpService>, f: (a: {
fetch: typeof fetch;
}) => Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
class HttpService
HttpService
, (
http: {
fetch: typeof fetch;
}
http
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <Todo[], GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<Todo[]>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<Todo[]>
try
: () =>
http: {
fetch: typeof fetch;
}
http
.
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
fetch
("https://api.example.demo/todos")
.
Promise<Response>.then<Todo[], never>(onfulfilled?: ((value: Response) => Todo[] | PromiseLike<Todo[]>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface Array<T>
Array
<
interface Todo
Todo
>>),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
)
).
Pipeable.pipe<RequestResolver.RequestResolver<GetTodos, HttpService>, Effect.Effect<RequestResolver.RequestResolver<GetTodos, never>, never, HttpService>>(this: RequestResolver.RequestResolver<...>, ab: (_: RequestResolver.RequestResolver<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
// we list the tags that the resolver can access
import RequestResolver
RequestResolver
.
const contextFromServices: <[typeof HttpService]>(services_0: typeof HttpService) => <R, A>(self: RequestResolver.RequestResolver<A, R>) => Effect.Effect<RequestResolver.RequestResolver<A, Exclude<R, HttpService>>, never, HttpService>

@since2.0.0

contextFromServices
(
class HttpService
HttpService
)
)
// ------------------------------
// Layers
// ------------------------------
class
class TodosService
TodosService
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"TodosService">(id: "TodosService") => <Self, Shape>() => Context.TagClass<Self, "TodosService", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("TodosService")<
class TodosService
TodosService
,
{
getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
:
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 lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
>
}
>() {}
const
const TodosServiceLive: Layer.Layer<TodosService, never, HttpService>
TodosServiceLive
=
import Layer
Layer
.
const effect: <typeof TodosService, never, HttpService>(tag: typeof TodosService, effect: Effect.Effect<{
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}, never, HttpService>) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class TodosService
TodosService
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<HttpService, {
fetch: typeof fetch;
}>>, {
getTodos: Effect.Effect<Todo[], GetTodosError, never>;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

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

When to Use

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 http: {
fetch: typeof fetch;
}
http
= yield*
class HttpService
HttpService
const
const resolver: RequestResolver.RequestResolver<GetTodos, never>
resolver
=
import RequestResolver
RequestResolver
.
const fromEffect: <never, GetTodos>(f: (a: GetTodos) => Effect.Effect<Todo[], GetTodosError, never>) => RequestResolver.RequestResolver<GetTodos, never>

Constructs a data source from an effectual function.

@since2.0.0

fromEffect
((
_: GetTodos
_
:
interface GetTodos
GetTodos
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tryPromise: <any, GetTodosError>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<any>;
readonly catch: (error: unknown) => GetTodosError;
}) => Effect.Effect<...> (+1 overload)

Creates an Effect that represents an asynchronous computation that might fail.

When to Use

In situations where you need to perform asynchronous operations that might fail, such as fetching data from an API, you can use the tryPromise constructor. This constructor is designed to handle operations that could throw exceptions by capturing those exceptions and transforming them into manageable errors.

Error Handling

There are two ways to handle errors with tryPromise:

  1. If you don't provide a catch function, the error is caught and the effect fails with an UnknownException.
  2. If you provide a catch function, the error is caught and the catch function maps it to an error of type E.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seepromise if the effectful computation is asynchronous and does not throw errors.

@example

// Title: Fetching a TODO Item
import { Effect } from "effect"
const getTodo = (id: number) =>
// Will catch any errors and propagate them as UnknownException
Effect.tryPromise(() =>
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
)
// ┌─── Effect<Response, UnknownException, never>
// ▼
const program = getTodo(1)

@example

// Title: Custom Error Handling import { Effect } from "effect"

const getTodo = (id: number) => Effect.tryPromise({ try: () => fetch(https://jsonplaceholder.typicode.com/todos/${id}), // remap the error catch: (unknown) => new Error(something went wrong ${unknown}) })

// ┌─── Effect<Response, Error, never> // ▼ const program = getTodo(1)

@since2.0.0

tryPromise
({
try: (signal: AbortSignal) => PromiseLike<any>
try
: () =>
const http: {
fetch: typeof fetch;
}
http
.
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
fetch
("https://api.example.demo/todos")
.
Promise<Response>.then<any, Todo[]>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => Todo[] | PromiseLike<Todo[]>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
<any,
interface Todo
Todo
[]>((
res: Response
res
) =>
res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
()),
catch: (error: unknown) => GetTodosError
catch
: () => new
constructor GetTodosError(): GetTodosError
GetTodosError
()
})
)
return {
getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const request: <RequestResolver.RequestResolver<GetTodos, never>, GetTodos>(self: GetTodos, dataSource: RequestResolver.RequestResolver<GetTodos, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

request
(
const GetTodos: Request.Request<out A, out E = never>.Constructor
(args: Omit<GetTodos, typeof Request.RequestTypeId | "_tag">) => GetTodos
GetTodos
({}),
const resolver: RequestResolver.RequestResolver<GetTodos, never>
resolver
)
}
})
)
const
const getTodos: Effect.Effect<Todo[], GetTodosError, TodosService>
getTodos
:
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 lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<
interface Array<T>
Array
<
interface Todo
Todo
>,
class GetTodosError
GetTodosError
,
class TodosService
TodosService
> =
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <{
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}, never, TodosService, Effect.Effect<Todo[], GetTodosError, never>>(self: Effect.Effect<{
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}, never, TodosService>, f: (a: {
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}) => Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
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))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
class TodosService
TodosService
, (
service: {
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}
service
) =>
service: {
getTodos: Effect.Effect<Array<Todo>, GetTodosError>;
}
service
.
getTodos: Effect.Effect<Todo[], GetTodosError, never>
getTodos
)

This way is probably the best for most of the cases given that layers are the natural primitive where to wire services together.

While we have significantly optimized request batching, there’s another area that can enhance our application’s efficiency: caching. Without caching, even with optimized batch processing, the same requests could be executed multiple times, leading to unnecessary data fetching.

In the Effect library, caching is handled through built-in utilities that allow requests to be stored temporarily, preventing the need to re-fetch data that hasn’t changed. This feature is crucial for reducing the load on both the server and the network, especially in applications that make frequent similar requests.

Here’s how you can implement caching for the getUserById query:

const getUserById = (id: number) =>
Effect.request(GetUserById({ id }), GetUserByIdResolver).pipe(
Effect.withRequestCaching(true)
)

Assuming you’ve wired everything up correctly:

const program = Effect.gen(function* () {
const todos = yield* getTodos
yield* Effect.forEach(todos, (todo) => notifyOwner(todo), {
concurrency: "unbounded"
})
}).pipe(Effect.repeat(Schedule.fixed("10 seconds")))

With this program, the getTodos operation retrieves the todos for each user. Then, the Effect.forEach function is used to notify the owner of each todo concurrently, without waiting for the notifications to complete.

The repeat function is applied to the entire chain of operations, and it ensures that the program repeats every 10 seconds using a fixed schedule. This means that the entire process, including fetching todos and sending notifications, will be executed repeatedly with a 10-second interval.

The program incorporates a caching mechanism, which prevents the same GetUserById operation from being executed more than once within a span of 1 minute. This default caching behavior helps optimize the program’s execution and reduces unnecessary requests to fetch user data.

Furthermore, the program is designed to send emails in batches, allowing for efficient processing and better utilization of resources.

In real-world applications, effective caching strategies can significantly improve performance by reducing redundant data fetching. The Effect library provides flexible caching mechanisms that can be tailored for specific parts of your application or applied globally.

There may be scenarios where different parts of your application have unique caching requirements—some might benefit from a localized cache, while others might need a global cache setup. Let’s explore how you can configure a custom cache to meet these varied needs.

Here’s how you can create a custom cache and apply it to part of your application. This example demonstrates setting up a cache that repeats a task every 10 seconds, caching requests with specific parameters like capacity and TTL (time-to-live).

const program = Effect.gen(function* () {
const todos = yield* getTodos
yield* Effect.forEach(todos, (todo) => notifyOwner(todo), {
concurrency: "unbounded"
})
}).pipe(
Effect.repeat(Schedule.fixed("10 seconds")),
Effect.provide(
Layer.setRequestCache(
Request.makeCache({ capacity: 256, timeToLive: "60 minutes" })
)
)
)

You can also construct a cache using Request.makeCache and apply it directly to a specific program using Effect.withRequestCache. This method ensures that all requests originating from the specified program are managed through the custom cache, provided that caching is enabled.