This documentation resides in the Effect monorepo, which contains the source code for the Effect library and its related packages.
When you need to find any information about the Effect library, only use this
documentation and the source code found in ./packages. Do not use
node_modules or any other external documentation, as it may be outdated or
incorrect.
Note: The examples in this documentation contain comments for illustration purposes. In practice, you would not include these comments in your code.
Prefer writing Effect code with Effect.gen & Effect.fn("name"). Then attach
additional behaviour with combinators. This style is more readable and easier to
maintain than using combinators alone.
Use Effect.gen to write code in an imperative style similar to async await.
You can use yield* to access the result of an effect.
import { Effect, Schema } from "effect"
Effect.gen(function*() {
yield* Effect.log("Starting the file processing...")
yield* Effect.log("Reading file...")
// Always return when raising an error, to ensure typescript understands that
// the function will not continue executing.
return yield* new FileProcessingError({ message: "Failed to read the file" })
}).pipe(
// Add additional functionality with .pipe
Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
Effect.withSpan("fileProcessing", {
attributes: {
method: "Effect.gen"
}
})
)
// Use Schema.TaggedErrorClass to define a custom error
export class FileProcessingError extends Schema.TaggedErrorClass<FileProcessingError>()("FileProcessingError", {
message: Schema.String
}) {}When writing functions that return an Effect, use Effect.fn to use the
generator syntax.
Avoid creating functions that return an Effect.gen, use Effect.fn
instead.
import { Effect, Schema } from "effect"
// Pass a string to Effect.fn, which will improve stack traces and also
// attach a tracing span (using Effect.withSpan behind the scenes).
//
// The name string should match the function name.
//
export const effectFunction = Effect.fn("effectFunction")(
// You can use `Effect.fn.Return` to specify the return type of the function.
// It accepts the same type parameters as `Effect.Effect`.
function*(n: number): Effect.fn.Return<string, SomeError> {
yield* Effect.logInfo("Received number:", n)
// Always return when raising an error, to ensure typescript understands that
// the function will not continue executing.
return yield* new SomeError({ message: "Failed to read the file" })
},
// Add additional functionality by passing in additional arguments.
// **Do not** use .pipe with Effect.fn
Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
Effect.annotateLogs({
method: "effectFunction"
})
)
// Use Schema.TaggedErrorClass to define a custom error
export class SomeError extends Schema.TaggedErrorClass<SomeError>()("SomeError", {
message: Schema.String
}) {}- Creating effects from common sources: Learn how to create effects from various sources, including plain values, synchronous code, Promise APIs, optional values, and callback-based APIs.
Effect services are the most common way to structure Effect code. Prefer using services to encapsulate behaviour over other approaches, as it ensures that your code is modular, testable, and maintainable.
The default way to define a service is to extend ServiceMap.Service,
passing in the service interface as a type parameter.
// file: src/db/Database.ts
import { Effect, Layer, Schema, ServiceMap } from "effect"
// Pass in the service class name as the first type parameter, and the service
// interface as the second type parameter.
export class Database extends ServiceMap.Service<Database, {
query(sql: string): Effect.Effect<Array<unknown>, DatabaseError>
}>()(
// The string identifier for the service, which should include the package
// name and the subdirectory path to the service file.
"myapp/db/Database"
) {
// Attach a static layer to the service, which will be used to provide an
// implementation of the service.
static readonly layer = Layer.effect(
Database,
Effect.gen(function*() {
// Define the service methods using Effect.fn
const query = Effect.fn("Database.query")(function*(sql: string) {
yield* Effect.log("Executing SQL query:", sql)
return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
})
// Return an instance of the service using Database.of, passing in an
// object that implements the service interface.
return Database.of({
query
})
})
)
}
export class DatabaseError extends Schema.TaggedErrorClass<DatabaseError>()("DatabaseError", {
cause: Schema.Defect
}) {}
// If you ever need to access the service type, use `Database["Service"]`
export type DatabaseService = Database["Service"]- ServiceMap.Reference: For defining configuration values, feature flags, or any other service that has a default value.
- Composing services with the Layer module:
Build focused service layers, then compose them with
Layer.provideandLayer.provideMergebased on what services you want to expose. - Creating Layers from configuration and/or Effects: Build a layer dynamically from an Effect / Config with
Layer.unwrap.
Defining custom errors and handling them with Effect.catch and Effect.catchTag.
import { Effect, Schema } from "effect"
// Define custom errors using Schema.TaggedErrorClass
export class ParseError extends Schema.TaggedErrorClass<ParseError>()("ParseError", {
input: Schema.String,
message: Schema.String
}) {}
export class ReservedPortError extends Schema.TaggedErrorClass<ReservedPortError>()("ReservedPortError", {
port: Schema.Number
}) {}
declare const loadPort: (input: string) => Effect.Effect<number, ParseError | ReservedPortError>
export const recovered = loadPort("80").pipe(
// Catch multiple errors with Effect.catchTag, and return a default port number.
Effect.catchTag(["ParseError", "ReservedPortError"], (_) => Effect.succeed(3000))
)
export const withFinalFallback = loadPort("invalid").pipe(
// Catch a specific error with Effect.catchTag
Effect.catchTag("ReservedPortError", (_) => Effect.succeed(3000)),
// Catch all errors with Effect.catch
Effect.catch((_) => Effect.succeed(3000))
)- Catch multiple errors with Effect.catchTags: Use
Effect.catchTagsto handle several tagged errors in one place. - Creating and handling errors with reasons:
Define a tagged error with a tagged
reasonfield, then recover withEffect.catchReason,Effect.catchReasons, or by unwrapping the reason into the error channel withEffect.unwrapReason.
Learn how to safely manage resources in Effect using Scopes and finalizers.
- Acquiring resources with Effect.acquireRelease:
Define a service that uses
Effect.acquireReleaseto manage the lifecycle of a resource, ensuring that it is properly cleaned up when the service is no longer needed. - Creating Layers that run background tasks: Use Layer.effectDiscard to encapsulate background tasks without a service interface.
- Dynamic resources with LayerMap:
Use
LayerMap.Serviceto dynamically build and manage resources that are keyed by some identifier, such as a tenant ID.
- Running effects with NodeRuntime and BunRuntime: Use
NodeRuntime.runMainto run an Effect program as your process entrypoint. - Using Layer.launch as the application entry point: Use
Layer.launchto run a long-running Effect program as your process entrypoint.
Use PubSub when you need one producer to fan out messages to many consumers.
- Broadcasting domain events with PubSub: Build an in-process event bus with
PubSuband expose it as a service.
Effect Streams represent effectful, pull-based sequences of values over time. They let you model finite or infinite data sources.
-
Creating streams from common data sources: Learn how to create streams from various data sources. Includes:
Stream.fromIterablefor arrays and other iterablesStream.fromEffectSchedulefor polling effectsStream.paginatefor paginated APIsStream.fromAsyncIterablefor async iterablesStream.fromEventListenerfor DOM eventsStream.callbackfor any callback-based APINodeStream.fromReadablefor Node.js readable streams
-
Consuming and transforming streams: How to transform and consume streams using operators like
map,flatMap,filter,mapEffect, and variousrun*methods. -
Decoding and encoding streams: Use
Stream.pipeThroughChannelwith theNdjson&Msgpackmodules to decode and encode streams of structured data.
ManagedRuntime bridges Effect programs with non-Effect code. Build one runtime
from your application Layer, then use it anywhere you need imperative execution,
like web handlers, framework hooks, worker queues, or legacy callback APIs.
- Using ManagedRuntime with Hono: Use
ManagedRuntimeto run Effect programs from external frameworks while keeping your domain logic in services and Layers.
Learn how to batch multiple requests into fewer external calls.
- Batching requests with RequestResolver: Define request types with
Request.Class, resolve them in batches withRequestResolver.
Schedules define recurring patterns for retries, repeats and polling.
- Working with the Schedule module: Build schedules, compose them, and use them with
Effect.retryandEffect.repeat.
Effect has built-in support for structured logging, distributed tracing, and
metrics. For exporting telemetry, use the lightweight Otlp modules from
effect/unstable/observability in new projects, or use
@effect/opentelemetry NodeSdk when integrating with an existing OpenTelemetry
setup.
- Customizing logging: Configure loggers & log-level filtering for production applications.
- Setting up tracing with Otlp modules: Configure Otlp tracing + log export with a reusable observability layer.
- Writing Effect tests with @effect/vitest: Using
it.effectfor Effect-based tests. - Testing services with shared layers: How to test Effect services that depend on other services.
Build http clients with the HttpClient module.
- Getting started with HttpClient: Define a service that uses the HttpClient module to fetch data from an external API
HttpApi gives you schema-first, type-safe HTTP APIs with runtime validation, typed clients, and OpenAPI docs from one definition.
- Getting started with HttpApi: Define a schema-first API, implement handlers, secure endpoints with middleware, serve it over HTTP, and call it using a generated typed client.
Use the effect/unstable/process modules to define child processes and run them with `ChildProcessSpawner.
- Working with child processes: This example shows how to collect process output, compose pipelines, and stream long-running command output.
Use the "effect/unstable/cli" modules to build CLI applications. These modules provide utilities for parsing command-line arguments, handling user input, and managing the flow of a CLI application.
- Getting started with Effect CLI modules: Build a command-line app with typed arguments and flags, then wire subcommand handlers into a single executable command.
Effect's AI modules provide a provider-agnostic interface for language models.
You can generate text, decode structured objects with Schema and stream partial
responses.
- Using LanguageModel for text, objects, and streams:
Configure a provider once, then use
LanguageModelfor plain text generation, schema-validated object generation, and streaming responses. - Defining and using AI tools:
Define tools with schemas, group them into toolkits, implement handlers,
and pass them to
LanguageModel.generateText. - Stateful chat sessions:
The AI
Chatmodule maintains conversation history automatically. Build AI agents or chat assistants.
The cluster modules let you model stateful services as entities and distribute them across multiple machines.
- Defining cluster entities: Define distributed entity RPCs and run them in a cluster.