A community-driven knowledge base of practical, goal-oriented patterns for building robust applications with Effect-TS.
This repository is designed to be a living document that helps developers move from core concepts to advanced architectural strategies by focusing on the "why" behind the code.
Looking for machine-readable rules for AI IDEs and coding agents? See the AI Coding Rules section below.
- Error Management
- Building APIs
- Core Concepts
- Concurrency
- Testing
- Tooling and Debugging
- Domain Modeling
- Modeling Data
- Making HTTP Requests
- Observability
- Resource Management
- File Handling
- Database Connections
- Network Requests
- Building Data Pipelines
- Application Configuration
- Modeling Time
- Project Setup & Execution
- Advanced Dependency Injection
- Custom Layers
- Dependency Injection
- Application Architecture
Pattern | Skill Level | Summary |
---|---|---|
Handle Errors with catchTag, catchTags, and catchAll | π‘ Intermediate | Use catchTag for type-safe recovery from specific tagged errors, and catchAll to recover from any possible failure. |
Mapping Errors to Fit Your Domain | π‘ Intermediate | Use Effect.mapError to transform specific, low-level errors into more general domain errors, creating clean architectural boundaries. |
Control Repetition with Schedule | π‘ Intermediate | Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried. |
Model Optional Values Safely with Option | π‘ Intermediate | Use Option to explicitly represent a value that may or may not exist, eliminating null and undefined errors. |
Define Type-Safe Errors with Data.TaggedError | π‘ Intermediate | Create custom, type-safe error classes by extending Data.TaggedError to make error handling robust, predictable, and self-documenting. |
Leverage Effect's Built-in Structured Logging | π‘ Intermediate | Use Effect's built-in logging functions (Effect.log, Effect.logInfo, etc.) for structured, configurable, and context-aware logging. |
Conditionally Branching Workflows | π‘ Intermediate | Use predicate-based operators like Effect.filter and Effect.if to make decisions and control the flow of your application based on runtime values. |
Retry Operations Based on Specific Errors | π‘ Intermediate | Use Effect.retry and predicate functions to selectively retry an operation only when specific, recoverable errors occur. |
Handle Flaky Operations with Retries and Timeouts | π‘ Intermediate | Use Effect.retry and Effect.timeout to build resilience against slow or intermittently failing operations, such as network requests. |
Distinguish 'Not Found' from Errors | π‘ Intermediate | Use Effect<Option> to clearly distinguish between a recoverable 'not found' case (None) and a true failure (Fail). |
Accumulate Multiple Errors with Either | π‘ Intermediate | Use Either<E, A> to represent computations that can fail, allowing you to accumulate multiple errors instead of short-circuiting on the first one. |
Handle Unexpected Errors by Inspecting the Cause | π Advanced | Use Effect.catchAllCause or Effect.runFork to inspect the Cause of a failure, distinguishing between expected errors (Fail) and unexpected defects (Die). |
Pattern | Skill Level | Summary |
---|---|---|
Handle a GET Request | π’ Beginner | Define a route that responds to a specific HTTP GET request path. |
Send a JSON Response | π’ Beginner | Create and send a structured JSON response with the correct headers and status code. |
Extract Path Parameters | π’ Beginner | Capture and use dynamic segments from a request URL, such as a resource ID. |
Create a Basic HTTP Server | π’ Beginner | Launch a simple, effect-native HTTP server to respond to incoming requests. |
Validate Request Body | π‘ Intermediate | Safely parse and validate an incoming JSON request body against a predefined Schema. |
Provide Dependencies to Routes | π‘ Intermediate | Inject services like database connections into HTTP route handlers using Layer and Effect.Service. |
Handle API Errors | π‘ Intermediate | Translate application-specific errors from the Effect failure channel into meaningful HTTP error responses. |
Make an Outgoing HTTP Client Request | π‘ Intermediate | Use the built-in Effect HTTP client to make safe and composable requests to external services from within your API. |
Pattern | Skill Level | Summary |
---|---|---|
Understand that Effects are Lazy Blueprints | π’ Beginner | An Effect is a lazy, immutable blueprint describing a computation, which does nothing until it is explicitly executed by a runtime. |
Wrap Asynchronous Computations with tryPromise | π’ Beginner | Use Effect.tryPromise to safely convert a function that returns a Promise into an Effect, capturing rejections in the error channel. |
Write Sequential Code with Effect.gen | π’ Beginner | Use Effect.gen with yield* to write sequential, asynchronous code in a style that looks and feels like familiar async/await. |
Transform Effect Values with map and flatMap | π’ Beginner | Use Effect.map for synchronous transformations and Effect.flatMap to chain operations that return another Effect. |
Create Pre-resolved Effects with succeed and fail | π’ Beginner | Use Effect.succeed(value) to create an Effect that immediately succeeds with a value, and Effect.fail(error) for an Effect that immediately fails. |
Solve Promise Problems with Effect | π’ Beginner | Understand how Effect solves the fundamental problems of native Promises, such as untyped errors, lack of dependency injection, and no built-in cancellation. |
Wrap Synchronous Computations with sync and try | π’ Beginner | Use Effect.sync for non-throwing synchronous code and Effect.try for synchronous code that might throw an exception. |
Use .pipe for Composition | π’ Beginner | Use the .pipe() method to chain multiple operations onto an Effect in a readable, top-to-bottom sequence. |
Understand the Three Effect Channels (A, E, R) | π’ Beginner | Learn about the three generic parameters of an Effect: the success value (A), the failure error (E), and the context requirements (R). |
Control Repetition with Schedule | π‘ Intermediate | Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried. |
Conditionally Branching Workflows | π‘ Intermediate | Use predicate-based operators like Effect.filter and Effect.if to make decisions and control the flow of your application based on runtime values. |
Control Flow with Conditional Combinators | π‘ Intermediate | Use combinators like Effect.if, Effect.when, and Effect.cond to handle conditional logic in a declarative, composable way. |
Process Streaming Data with Stream | π‘ Intermediate | Use Stream<A, E, R> to represent and process data that arrives over time, such as file reads, WebSocket messages, or paginated API results. |
Manage Shared State Safely with Ref | π‘ Intermediate | Use Ref to model shared, mutable state in a concurrent environment, ensuring all updates are atomic and free of race conditions. |
Understand Layers for Dependency Injection | π‘ Intermediate | A Layer is a blueprint that describes how to build a service, detailing its own requirements and any potential errors during its construction. |
Use Chunk for High-Performance Collections | π‘ Intermediate | Use Chunk as a high-performance, immutable alternative to JavaScript's Array, especially for data processing pipelines. |
Understand Fibers as Lightweight Threads | π Advanced | A Fiber is a lightweight, virtual thread managed by the Effect runtime, enabling massive concurrency on a single OS thread without the overhead of traditional threading. |
Pattern | Skill Level | Summary |
---|---|---|
Control Repetition with Schedule | π‘ Intermediate | Use Schedule to create composable, stateful policies that define precisely how an effect should be repeated or retried. |
Race Concurrent Effects for the Fastest Result | π‘ Intermediate | Use Effect.race to run multiple effects concurrently and proceed with the result of the one that succeeds first, automatically interrupting the others. |
Manage Shared State Safely with Ref | π‘ Intermediate | Use Ref to model shared, mutable state in a concurrent environment, ensuring all updates are atomic and free of race conditions. |
Run Independent Effects in Parallel with Effect.all | π‘ Intermediate | Use Effect.all to run multiple independent effects concurrently and collect all their results into a single tuple. |
Process a Collection in Parallel with Effect.forEach | π‘ Intermediate | Use Effect.forEach with the concurrency option to process a collection of items in parallel with a fixed limit, preventing resource exhaustion. |
Add Caching by Wrapping a Layer | π Advanced | Implement caching by creating a new layer that wraps a live service, intercepting method calls to add caching logic without modifying the original service. |
Manage Resource Lifecycles with Scope | π Advanced | Use Scope for fine-grained, manual control over resource lifecycles, ensuring cleanup logic (finalizers) is always executed. |
Run Background Tasks with Effect.fork | π Advanced | Use Effect.fork to start a computation in a background fiber, allowing the parent fiber to continue its work without waiting. |
Execute Long-Running Apps with Effect.runFork | π Advanced | Use Effect.runFork at the application's entry point to launch a long-running process as a detached fiber, allowing for graceful shutdown. |
Implement Graceful Shutdown for Your Application | π Advanced | Use Effect.runFork and listen for OS signals (SIGINT, SIGTERM) to trigger a Fiber.interrupt, ensuring all resources are safely released. |
Decouple Fibers with Queues and PubSub | π Advanced | Use Queue for point-to-point work distribution and PubSub for broadcast messaging to enable safe, decoupled communication between concurrent fibers. |
Poll for Status Until a Task Completes | π Advanced | Use Effect.race to run a repeating polling effect alongside a main task, automatically stopping the polling when the main task finishes. |
Understand Fibers as Lightweight Threads | π Advanced | A Fiber is a lightweight, virtual thread managed by the Effect runtime, enabling massive concurrency on a single OS thread without the overhead of traditional threading. |
Pattern | Skill Level | Summary |
---|---|---|
Accessing the Current Time with Clock | π‘ Intermediate | Use the Clock service to access the current time in a testable, deterministic way, avoiding direct calls to Date.now(). |
Write Tests That Adapt to Application Code | π‘ Intermediate | A cardinal rule of testing: Tests must adapt to the application's interface, not the other way around. Never modify application code solely to make a test pass. |
Use the Auto-Generated .Default Layer in Tests | π‘ Intermediate | When testing, always use the MyService.Default layer that is automatically generated by the Effect.Service class for dependency injection. |
Mocking Dependencies in Tests | π‘ Intermediate | Use a test-specific Layer to provide mock implementations of services your code depends on, enabling isolated and deterministic unit tests. |
Model Dependencies as Services | π‘ Intermediate | Abstract external dependencies and capabilities into swappable, testable services using Effect's dependency injection system. |
Create a Testable HTTP Client Service | π‘ Intermediate | Define an HttpClient service with separate 'Live' and 'Test' layers to enable robust, testable interactions with external APIs. |
Organize Layers into Composable Modules | π Advanced | Structure a large application by grouping related services into 'module' layers, which are then composed together with a shared base layer. |
Pattern | Skill Level | Summary |
---|---|---|
Supercharge Your Editor with the Effect LSP | π‘ Intermediate | Install the Effect Language Server (LSP) extension for your editor to get rich, inline type information and enhanced error checking for your Effect code. |
Teach your AI Agents Effect with the MCP Server | π Advanced | Use the Effect MCP server to provide live, contextual information about your application's structure directly to AI coding agents. |
Pattern | Skill Level | Summary |
---|---|---|
Model Optional Values Safely with Option | π‘ Intermediate | Use Option to explicitly represent a value that may or may not exist, eliminating null and undefined errors. |
Use Effect.gen for Business Logic | π‘ Intermediate | Encapsulate sequential business logic, control flow, and dependency access within Effect.gen for improved readability and maintainability. |
Transform Data During Validation with Schema | π‘ Intermediate | Use Schema.transform to safely convert data from one type to another during the parsing phase, such as from a string to a Date. |
Define Type-Safe Errors with Data.TaggedError | π‘ Intermediate | Create custom, type-safe error classes by extending Data.TaggedError to make error handling robust, predictable, and self-documenting. |
Define Contracts Upfront with Schema | π‘ Intermediate | Use Schema to define the types for your data models and function signatures before writing the implementation, creating clear, type-safe contracts. |
Parse and Validate Data with Schema.decode | π‘ Intermediate | Use Schema.decode(schema) to create an Effect that parses and validates unknown data, which integrates seamlessly with Effect's error handling. |
Avoid Long Chains of .andThen; Use Generators Instead | π‘ Intermediate | Prefer Effect.gen over long chains of .andThen for sequential logic to improve readability and maintainability. |
Distinguish 'Not Found' from Errors | π‘ Intermediate | Use Effect<Option> to clearly distinguish between a recoverable 'not found' case (None) and a true failure (Fail). |
Model Validated Domain Types with Brand | π‘ Intermediate | Use Brand to turn primitive types like string or number into specific, validated domain types like Email or PositiveInt, making illegal states unrepresentable. |
Accumulate Multiple Errors with Either | π‘ Intermediate | Use Either<E, A> to represent computations that can fail, allowing you to accumulate multiple errors instead of short-circuiting on the first one. |
Pattern | Skill Level | Summary |
---|---|---|
Comparing Data by Value with Structural Equality | π’ Beginner | Use Data.struct and Equal.equals to safely compare objects by their value instead of their reference, avoiding common JavaScript pitfalls. |
Pattern | Skill Level | Summary |
---|---|---|
Add Custom Metrics to Your Application | π‘ Intermediate | Use Effect's Metric module to instrument your code with counters, gauges, and histograms to track key business and performance indicators. |
Model Dependencies as Services | π‘ Intermediate | Abstract external dependencies and capabilities into swappable, testable services using Effect's dependency injection system. |
Create a Testable HTTP Client Service | π‘ Intermediate | Define an HttpClient service with separate 'Live' and 'Test' layers to enable robust, testable interactions with external APIs. |
Add Caching by Wrapping a Layer | π Advanced | Implement caching by creating a new layer that wraps a live service, intercepting method calls to add caching logic without modifying the original service. |
Build a Basic HTTP Server | π Advanced | Combine Layer, Runtime, and Effect to create a simple, robust HTTP server using Node.js's built-in http module. |
Create a Managed Runtime for Scoped Resources | π Advanced | Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run. |
Pattern | Skill Level | Summary |
---|---|---|
Add Custom Metrics to Your Application | π‘ Intermediate | Use Effect's Metric module to instrument your code with counters, gauges, and histograms to track key business and performance indicators. |
Trace Operations Across Services with Spans | π‘ Intermediate | Use Effect.withSpan to create custom tracing spans, providing detailed visibility into the performance and flow of your application's operations. |
Pattern | Skill Level | Summary |
---|---|---|
Safely Bracket Resource Usage with acquireRelease |
π’ Beginner | Use Effect.acquireRelease to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
Create a Service Layer from a Managed Resource | π‘ Intermediate | Use Layer.scoped with Effect.Service to transform a managed resource into a shareable, application-wide service. |
Compose Resource Lifecycles with Layer.merge |
π‘ Intermediate | Combine multiple resource-managing layers, letting Effect automatically handle the acquisition and release order. |
Manage Resource Lifecycles with Scope | π Advanced | Use Scope for fine-grained, manual control over resource lifecycles, ensuring cleanup logic (finalizers) is always executed. |
Manually Manage Lifecycles with Scope |
π Advanced | Use Scope directly to manage complex resource lifecycles or when building custom layers. |
Implement Graceful Shutdown for Your Application | π Advanced | Use Effect.runFork and listen for OS signals (SIGINT, SIGTERM) to trigger a Fiber.interrupt, ensuring all resources are safely released. |
Create a Managed Runtime for Scoped Resources | π Advanced | Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run. |
Pattern | Skill Level | Summary |
---|---|---|
Safely Bracket Resource Usage with acquireRelease |
π’ Beginner | Use Effect.acquireRelease to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
Pattern | Skill Level | Summary |
---|---|---|
Safely Bracket Resource Usage with acquireRelease |
π’ Beginner | Use Effect.acquireRelease to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
Pattern | Skill Level | Summary |
---|---|---|
Safely Bracket Resource Usage with acquireRelease |
π’ Beginner | Use Effect.acquireRelease to guarantee a resource's cleanup logic runs, even if errors or interruptions occur. |
Pattern | Skill Level | Summary |
---|---|---|
Create a Stream from a List | π’ Beginner | Turn a simple in-memory array or list into a foundational data pipeline using Stream. |
Run a Pipeline for its Side Effects | π’ Beginner | Execute a pipeline for its effects without collecting the results, saving memory. |
Collect All Results into a List | π’ Beginner | Run a pipeline and gather all of its results into an in-memory array. |
Turn a Paginated API into a Single Stream | π‘ Intermediate | Convert a paginated API into a continuous, easy-to-use stream, abstracting away the complexity of fetching page by page. |
Process Items Concurrently | π‘ Intermediate | Perform an asynchronous action for each item in a stream with controlled parallelism to dramatically improve performance. |
Process Items in Batches | π‘ Intermediate | Group items into chunks for efficient bulk operations, like database inserts or batch API calls. |
Process collections of data asynchronously | π‘ Intermediate | Process collections of data asynchronously in a lazy, composable, and resource-safe manner using Effect's Stream. |
Process a Large File with Constant Memory | π‘ Intermediate | Create a data pipeline from a file on disk, processing it line-by-line without loading the entire file into memory. |
Automatically Retry Failed Operations | π‘ Intermediate | Build a self-healing pipeline that can automatically retry failed processing steps using a configurable backoff strategy. |
Manage Resources Safely in a Pipeline | π Advanced | Ensure resources like file handles or connections are safely acquired at the start of a pipeline and always released at the end, even on failure. |
Pattern | Skill Level | Summary |
---|---|---|
Access Configuration from the Context | π‘ Intermediate | Access your type-safe configuration within an Effect.gen block by yielding the Config object you defined. |
Define a Type-Safe Configuration Schema | π‘ Intermediate | Use Effect.Config primitives to define a schema for your application's configuration, ensuring type-safety and separation from code. |
Provide Configuration to Your App via a Layer | π‘ Intermediate | Use Config.layer(schema) to create a Layer that provides your configuration schema to the application's context. |
Pattern | Skill Level | Summary |
---|---|---|
Accessing the Current Time with Clock | π‘ Intermediate | Use the Clock service to access the current time in a testable, deterministic way, avoiding direct calls to Date.now(). |
Representing Time Spans with Duration | π‘ Intermediate | Use the Duration data type to represent time intervals in a type-safe, human-readable, and composable way. |
Beyond the Date Type - Real World Dates, Times, and Timezones | π‘ Intermediate | Use the Clock service for testable access to the current time and prefer immutable primitives for storing and passing timestamps. |
Pattern | Skill Level | Summary |
---|---|---|
Execute Synchronous Effects with Effect.runSync | π’ Beginner | Use Effect.runSync at the 'end of the world' to execute a purely synchronous Effect and get its value directly. |
Execute Asynchronous Effects with Effect.runPromise | π’ Beginner | Use Effect.runPromise at the 'end of the world' to execute an asynchronous Effect and get its result as a JavaScript Promise. |
Set Up a New Effect Project | π’ Beginner | Initialize a new Node.js project with the necessary TypeScript configuration and Effect dependencies to start building. |
Execute Long-Running Apps with Effect.runFork | π Advanced | Use Effect.runFork at the application's entry point to launch a long-running process as a detached fiber, allowing for graceful shutdown. |
Create a Reusable Runtime from Layers | π Advanced | Compile your application's layers into a reusable Runtime object to efficiently execute multiple effects that share the same context. |
Create a Managed Runtime for Scoped Resources | π Advanced | Use Layer.launch to safely manage the lifecycle of layers containing scoped resources, ensuring finalizers are always run. |
Pattern | Skill Level | Summary |
---|---|---|
Manually Manage Lifecycles with Scope |
π Advanced | Use Scope directly to manage complex resource lifecycles or when building custom layers. |
Pattern | Skill Level | Summary |
---|---|---|
Manually Manage Lifecycles with Scope |
π Advanced | Use Scope directly to manage complex resource lifecycles or when building custom layers. |
Pattern | Skill Level | Summary |
---|---|---|
Create a Service Layer from a Managed Resource | π‘ Intermediate | Use Layer.scoped with Effect.Service to transform a managed resource into a shareable, application-wide service. |
Compose Resource Lifecycles with Layer.merge |
π‘ Intermediate | Combine multiple resource-managing layers, letting Effect automatically handle the acquisition and release order. |
Pattern | Skill Level | Summary |
---|---|---|
Create a Service Layer from a Managed Resource | π‘ Intermediate | Use Layer.scoped with Effect.Service to transform a managed resource into a shareable, application-wide service. |
Compose Resource Lifecycles with Layer.merge |
π‘ Intermediate | Combine multiple resource-managing layers, letting Effect automatically handle the acquisition and release order. |