If you’re using EffectTS to build your applications and make HTTP requests,
you’ll find FxFetch to be a perfect fit. It provides a type-safe, ergonomic API for handling fetch operations.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
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:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
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.
Example (Fetching a TODO Item)
import { Effect } from"effect"
const getTodo = (id:number) =>
// Will catch any errors and propagate them as UnknownException
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
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:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
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.
Example (Fetching a TODO Item)
import { Effect } from"effect"
const getTodo = (id:number) =>
// Will catch any errors and propagate them as UnknownException
flatMap lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap used with arrays but works
specifically with Effect instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap always returns a new effect instead of
changing the original one.
When to Use
Use flatMap when you need to chain multiple effects, ensuring that each
step produces a new Effect while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from"effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total:number,
discountRate:number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(newError("Discount rate cannot be zero"))
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import { Effect } from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
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:
If you don't provide a catch function, the error is caught and the
effect fails with an UnknownException.
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.
Example (Fetching a TODO Item)
import { Effect } from"effect"
const getTodo = (id:number) =>
// Will catch any errors and propagate them as UnknownException
Wrapper for TypeError. It is almost impossible to categorize all possible errors, as it depends on the environment (browser, Node.js, Deno, etc.), location, network, and more.
Possible reasons:
Blocked by a permissions policy.
Invalid header name.
Invalid header value. The header object must contain exactly two elements.
Invalid URL or scheme, or using a scheme that fetch does not support, or using a scheme that is not supported for a particular request mode.
URL includes credentials.
Invalid referrer URL.
Invalid modes (navigate and websocket).
If the request cache mode is "only-if-cached" and the request mode is other than "same-origin".
If the request method is an invalid name token or one of the forbidden headers ('CONNECT', 'TRACE' or 'TRACK').
If the request mode is "no-cors" and the request method is not a CORS-safe-listed method ('GET', 'HEAD', or 'POST').
If the request method is 'GET' or 'HEAD' and the body is non-null or not undefined.
Catches and handles specific errors by their _tag field, which is used as a
discriminator.
When to Use
catchTag is useful when your errors are tagged with a readonly _tag field
that identifies the error type. You can use this function to handle specific
error types by matching the _tag value. This allows for precise error
handling, ensuring that only specific errors are caught and handled.
The error type must have a readonly _tag field to use catchTag. This
field is used to identify and match errors.
Creates an effect that terminates a fiber with a RuntimeException
containing the specified message.
Details
This function is used to signal a defect, representing a critical and
unexpected error in the code. When invoked, it produces an effect that
terminates the fiber with a RuntimeException carrying the given message.
The resulting effect has an error channel of type never, indicating it does
not handle or recover from the error.
When to Use
Use this function when you want to terminate a fiber due to an unrecoverable
defect and include a clear explanation in the message.
Example (Terminating on Division by Zero with a Specified Message)
import { Effect } from"effect"
const divide = (a:number, b:number) =>
b === 0
? Effect.dieMessage("Cannot divide by zero")
: Effect.succeed(a / b)
// ┌─── Effect<number, never, never>
// ▼
const program = divide(1, 0)
Effect.runPromise(program).catch(console.error)
// Output:
// (FiberFailure) RuntimeException: Cannot divide by zero
// ...stack trace...
@see ― die for a variant that throws a specified error.
@see ― dieSync for a variant that throws a specified error, evaluated
lazily.
@since ― 2.0.0
dieMessage('Unknown error occurred during fetch request')
flatMap lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap used with arrays but works
specifically with Effect instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap always returns a new effect instead of
changing the original one.
When to Use
Use flatMap when you need to chain multiple effects, ensuring that each
step produces a new Effect while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from"effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total:number,
discountRate:number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(newError("Discount rate cannot be zero"))
Creates an Effect that represents a recoverable error.
When to Use
Use this function to explicitly signal an error in an Effect. The error
will keep propagating unless it is handled. You can handle the error with
functions like
catchAll
or
catchTag
.
Example (Creating a Failed Effect)
import { Effect } from"effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
newError("Operation failed due to network error")
)
@see ― succeed to create an effect that represents a successful value.
Some logic to process the response. Just a placeholder.
processResponse(
const response:Response
response); // Some logic here
45
});
Missing promise cancellation
Is the response OK? What if it’s a 4XX or 5XX?
Why does it fail?
Nice! We did it. It is perfect, right? Nope.
Moving from a non-effect world to an effect world can be tedious, verbose and error-prone.
It’s similar to using untyped JavaScript in TypeScript. It’s better if the hard work is done by someone else.
In the untyped npm ecosystem, we have the @types/* initiative.
In this case, we have FxFetch!
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
EffectTS is built on functional programming principles. All building blocks (Effect, Option, Either, DateTime, …) are
immutable and clonable by design.
For a library to truly be in symbiosis
with EffectTS, it must adhere to the same principles.
EffectTS excels at parallel actions and concurrency.
Without using immutable and clonable structures, unexpected issues may appear.
Working with immutable objects can be challenging without proper tools — that’s
why we built fx-fetch.
Real-world examples of why immutability and clonability matter:
Reusing the same Request for paginated API calls
Appending general headers to already-created Request objects
Retrying failed requests without worrying about side effects
Even when they work correctly, they don’t solve DX issues or provide key
features like reading request/response properties multiple times without side
effects.
1
const
const req:Request
req = new
module globalThis
globalThis.
var Request: new (requestInfo:string, init?:RequestInit) => Request(+2overloads)
Request("./endpoint", {
2
method?: string
method: "POST",
3
body?: Bun.BodyInit|null|undefined
body: "Hello World", // ◀︎── string
4
});
5
6
var console:Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = newconsole.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).