1# Effect library documentation
2
3This documentation covers the Effect library and its related packages.
4
5When you need to find information about Effect, use this documentation and the
6Effect source code available in your environment. Avoid unrelated copies of
7Effect or external documentation, as they may be outdated or incorrect.
8
9**Note**: The examples in this documentation contain comments for illustration
10purposes. In practice, you would not include these comments in your code.
11
12## Writing `Effect` code
13
14Prefer `Effect.gen` for inline Effect code. For reusable functions, prefer
15`Effect.fn("name")` when tracing is useful and `Effect.fnUntraced` when it is not,
16particularly in library implementations and hot paths. Avoid functions that only
17wrap and return `Effect.gen`. Attach additional behaviour with combinators; this
18style is more readable and easier to maintain than using combinators alone.
19
20### Using Effect.gen
21
22Use `Effect.gen` to write code in an imperative style similar to async await.
23You can use `yield*` to access the result of an effect.
24
25```ts
26import { Effect, Schema } from "effect"
27
28Effect.gen(function*() {
29 yield* Effect.log("Starting the file processing...")
30 yield* Effect.log("Reading file...")
31
32 // Always return when raising an error, to ensure typescript understands that
33 // the function will not continue executing.
34 return yield* new FileProcessingError({ message: "Failed to read the file" })
35}).pipe(
36 // Add additional functionality with .pipe
37 Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
38 Effect.withSpan("fileProcessing", {
39 attributes: {
40 method: "Effect.gen"
41 }
42 })
43)
44
45// Use Schema.TaggedError to define a custom error
46export class FileProcessingError extends Schema.TaggedError<FileProcessingError>()("FileProcessingError", {
47 message: Schema.String
48}) {}
49```
50
51### Using Effect.fn and Effect.fnUntraced
52
53When writing reusable functions that return an Effect, use `Effect.fn` or
54`Effect.fnUntraced` to use the generator syntax.
55
56Use `Effect.fn("name")` when the function should create a tracing span. Prefer
57`Effect.fnUntraced` when tracing is not needed, particularly for library
58implementations and hot paths.
59
60**Avoid creating functions that only wrap and return an `Effect.gen`**.
61
62```ts
63import { Effect, Schema } from "effect"
64
65// Pass a string to Effect.fn, which will improve stack traces and also
66// attach a tracing span (using Effect.withSpan behind the scenes).
67//
68// The name string should match the function name.
69//
70export const effectFunction = Effect.fn("effectFunction")(
71 // You can use `Effect.fn.Return` to specify the return type of the function.
72 // It accepts the same type parameters as `Effect.Effect`.
73 function*(n: number): Effect.fn.Return<string, SomeError> {
74 yield* Effect.logInfo("Received number:", n)
75
76 // Always return when raising an error, to ensure typescript understands that
77 // the function will not continue executing.
78 return yield* new SomeError({ message: "Failed to read the file" })
79 },
80 // Add additional functionality by passing in additional arguments.
81 // **Do not** use .pipe with Effect.fn
82 Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
83 Effect.annotateLogs({
84 method: "effectFunction"
85 })
86)
87
88// Effect.fnUntraced avoids tracing and stack-frame capture while still reusing
89// the generator body. This is preferred for library functions that do not
90// represent a useful tracing boundary.
91export const validateBatchSize = Effect.fnUntraced(function*(size: number): Effect.fn.Return<number, SomeError> {
92 if (!Number.isInteger(size) || size <= 0) {
93 return yield* new SomeError({ message: "Batch size must be a positive integer" })
94 }
95 return size
96})
97
98// Use Schema.TaggedError to define a custom error
99export class SomeError extends Schema.TaggedError<SomeError>()("SomeError", {
100 message: Schema.String
101}) {}
102```
103
104### More examples
105
106- **[Creating effects from common sources](./ai-docs/src/01_effect/01_basics/10_creating-effects.ts)**:
107 Learn how to create effects from various sources, including plain values,
108 synchronous code, Promise APIs, optional values, and callback-based APIs.
109
110## Defining schemas and domain models
111
112All validation and domain modeling in Effect is done with `Schema`.
113
114**AVOID using predicates or manual parsing**, instead use `Schema` to parse untrusted data and validate it.
115
116For a comprehensive guide, see [SCHEMA.md](https://github.com/Effect-TS/effect/blob/main/packages/effect/SCHEMA.md). Make sure to read the guide in chunks, as it is a large document.
117
118- **[Schema basics](./ai-docs/src/01_effect/02_schema/10_schema-basics.ts)**:
119 Define `Schema.Class`s, decode unknown input into typed values, and
120 encode typed values back into their external representation.
121
122## Writing Effect services
123
124Effect services are the most common way to structure Effect code. Prefer using
125services to encapsulate behaviour over other approaches, as it ensures that your
126code is modular, testable, and maintainable.
127
128### Context.Service
129
130The default way to define a service is to extend `Context.Service`,
131passing in the service interface as a type parameter.
132
133```ts
134// file: src/db/Database.ts
135import { Context, Effect, Layer, Schema } from "effect"
136
137// Pass in the service class name as the first type parameter, and the service
138// interface as the second type parameter.
139export class Database extends Context.Service<Database, {
140 query(sql: string): Effect.Effect<Array<unknown>, DatabaseError>
141}>()(
142 // The string identifier for the service, which should include the package
143 // name and the subdirectory path to the service file.
144 "myapp/db/Database"
145) {
146 // Attach a static layer to the service, which will be used to provide an
147 // implementation of the service.
148 static readonly layer = Layer.effect(
149 Database,
150 Effect.gen(function*() {
151 // Define the service methods using Effect.fn
152 const query = Effect.fn("Database.query")(function*(sql: string) {
153 yield* Effect.log("Executing SQL query:", sql)
154 return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
155 })
156
157 // Return an instance of the service using Database.of, passing in an
158 // object that implements the service interface.
159 return Database.of({
160 query
161 })
162 })
163 )
164}
165
166export class DatabaseError extends Schema.TaggedError<DatabaseError>()("DatabaseError", {
167 cause: Schema.Defect()
168}) {}
169
170// If you ever need to access the service type, use `Database["Service"]`
171export type DatabaseService = Database["Service"]
172```
173
174### More examples
175
176- **[Context.Reference](./ai-docs/src/01_effect/03_services/10_reference.ts)**: For defining configuration values, feature flags, or any other service that has a default value.
177- **[Composing services with the Layer module](./ai-docs/src/01_effect/03_services/20_layer-composition.ts)**:
178 Build focused service layers, then compose them with `Layer.provide` and
179 `Layer.provideMerge` based on what services you want to expose.
180- **[Creating Layers from configuration and/or Effects](./ai-docs/src/01_effect/03_services/20_layer-unwrap.ts)**: Build a layer dynamically from an Effect / Config with `Layer.unwrap`.
181
182## Error handling
183
184### Error handling basics
185
186Defining custom errors and handling them with Effect.catch and Effect.catchTag.
187
188```ts
189import { Effect, Schema } from "effect"
190
191// Define custom errors using Schema.TaggedError
192export class ParseError extends Schema.TaggedError<ParseError>()("ParseError", {
193 input: Schema.String,
194 message: Schema.String
195}) {}
196
197export class ReservedPortError extends Schema.TaggedError<ReservedPortError>()("ReservedPortError", {
198 port: Schema.Int
199}) {}
200
201declare const loadPort: (input: string) => Effect.Effect<number, ParseError | ReservedPortError>
202
203export const recovered = loadPort("80").pipe(
204 // Catch multiple errors with Effect.catchTag, and return a default port number.
205 Effect.catchTag(["ParseError", "ReservedPortError"], (_) => Effect.succeed(3000))
206)
207
208export const withFinalFallback = loadPort("invalid").pipe(
209 // Catch a specific error with Effect.catchTag
210 Effect.catchTag("ReservedPortError", (_) => Effect.succeed(3000)),
211 // Catch all errors with Effect.catch
212 Effect.catch((_) => Effect.succeed(3000))
213)
214```
215
216### More examples
217
218- **[Catch multiple errors with Effect.catchTags](./ai-docs/src/01_effect/04_errors/10_catch-tags.ts)**: Use `Effect.catchTags` to handle several tagged errors in one place.
219- **[Creating and handling errors with reasons](./ai-docs/src/01_effect/04_errors/20_reason-errors.ts)**:
220 Define a tagged error with a tagged `reason` field, then recover with
221 `Effect.catchReason`, `Effect.catchReasons`, or by unwrapping the reason into
222 the error channel with `Effect.unwrapReason`.
223
224## Managing resources and `Scope`s
225
226Learn how to safely manage resources in Effect using `Scope`s and finalizers.
227
228- **[Acquiring resources with Effect.acquireRelease](./ai-docs/src/01_effect/05_resources/10_acquire-release.ts)**:
229 Define a service that uses `Effect.acquireRelease` to manage the lifecycle of
230 a resource, ensuring that it is properly cleaned up when the service is no
231 longer needed.
232- **[Creating Layers that run background tasks](./ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts)**: Use Layer.effectDiscard to encapsulate background tasks without a service interface.
233- **[Dynamic resources with LayerMap](./ai-docs/src/01_effect/05_resources/30_layer-map.ts)**:
234 Use `LayerMap.Service` to dynamically build and manage resources that are
235 keyed by some identifier, such as a tenant ID.
236
237## Running Effect programs
238
239- **[Running effects with NodeRuntime and BunRuntime](./ai-docs/src/01_effect/06_running/10_run-main.ts)**: Use `NodeRuntime.runMain` to run an Effect program as your process entrypoint.
240- **[Using Layer.launch as the application entry point](./ai-docs/src/01_effect/06_running/20_layer-launch.ts)**: Use `Layer.launch` to run a long-running Effect program as your process entrypoint.
241
242## Broadcasting messages with PubSub
243
244Use `PubSub` when you need one producer to fan out messages to many consumers.
245
246- **[Broadcasting domain events with PubSub](./ai-docs/src/01_effect/07_pubsub/10_pubsub.ts)**: Build an in-process event bus with `PubSub` and expose it as a service.
247
248## Working with Streams
249
250Effect Streams represent effectful, pull-based sequences of values over time.
251They let you model finite or infinite data sources.
252
253- **[Creating streams from common data sources](./ai-docs/src/03_stream/10_creating-streams.ts)**:
254 Learn how to create streams from various data sources. Includes:
255
256 - `Stream.fromIterable` for arrays and other iterables
257 - `Stream.fromEffectSchedule` for polling effects
258 - `Stream.paginate` for paginated APIs
259 - `Stream.fromAsyncIterable` for async iterables
260 - `Stream.fromEventListener` for DOM events
261 - `Stream.callback` for any callback-based API
262 - `NodeStream.fromReadable` for Node.js readable streams
263- **[Consuming and transforming streams](./ai-docs/src/03_stream/20_consuming-streams.ts)**: How to transform and consume streams using operators like `map`, `flatMap`, `filter`, `mapEffect`, and various `run*` methods.
264- **[Decoding and encoding streams](./ai-docs/src/03_stream/30_encoding.ts)**:
265 Use `Stream.pipeThroughChannel` with the `Ndjson` and `SchemaBinary` modules to
266 decode and encode streams of structured data.
267
268## Integrating Effect into existing applications
269
270`ManagedRuntime` bridges Effect programs with non-Effect code. Build one runtime
271from your application Layer, then use it anywhere you need imperative execution,
272like web handlers, framework hooks, worker queues, or legacy callback APIs.
273
274- **[Using ManagedRuntime with Hono](./ai-docs/src/04_integration/10_managed-runtime.ts)**: Use `ManagedRuntime` to run Effect programs from external frameworks while keeping your domain logic in services and Layers.
275
276## Batching external requests
277
278Learn how to batch multiple requests into fewer external calls.
279
280- **[Batching requests with RequestResolver](./ai-docs/src/05_batching/10_request-resolver.ts)**: Define request types with `Request.Class`, resolve them in batches with `RequestResolver`.
281
282## Working with Schedules
283
284Schedules define recurring patterns for retries, repeats and polling.
285
286- **[Working with the Schedule module](./ai-docs/src/06_schedule/10_schedules.ts)**: Build schedules, compose them, and use them with `Effect.retry` and `Effect.repeat`.
287
288## Working with DateTime
289
290When working with dates and time, use the `DateTime` module instead of `Date` and `Date.now`.
291
292Use it when your Effect programs need testable current time, safe parsing, stable ISO formatting, time-zone conversion, or calendar arithmetic.
293
294- **[Creating and formatting DateTime values](./ai-docs/src/07_datetime/10_creating-and-formatting.ts)**:
295 Parse incoming date values safely, use Clock-powered current time, and format
296 instants for API payloads or user-facing labels.
297- **[Working with time zones](./ai-docs/src/07_datetime/20_time-zones.ts)**:
298 Attach IANA zones to instants, render zoned ISO strings, and provide a
299 CurrentTimeZone service for code that should use the workspace/user zone.
300
301## Observability
302
303Effect has built-in support for structured logging, distributed tracing, and
304metrics. For exporting telemetry, use the lightweight Otlp modules from
305`effect/observability` in new projects, or use
306`@effect/opentelemetry` NodeSdk when integrating with an existing OpenTelemetry
307setup.
308
309- **[Customizing logging](./ai-docs/src/08_observability/10_logging.ts)**: Configure loggers & log-level filtering for production applications.
310- **[Setting up tracing with Otlp modules](./ai-docs/src/08_observability/20_otlp-tracing.ts)**: Configure Otlp tracing + log export with a reusable observability layer.
311
312## Testing Effect programs
313
314- **[Writing Effect tests with @effect/vitest](./ai-docs/src/09_testing/10_effect-tests.ts)**: Using `it.effect` for Effect-based tests.
315- **[Testing services with shared layers](./ai-docs/src/09_testing/20_layer-tests.ts)**: How to test Effect services that depend on other services.
316
317## Runtime type guards
318
319The `Predicate` module contains small, reusable runtime checks.
320
321**NEVER** write your own helper functions like `isRecord` or `isString`, instead
322use the helpers from the `Predicate` module.
323
324Predicates can be composed with apis such as `Predicate.and`,
325`Predicate.or`, `Predicate.not`, and `Predicate.compose`.
326
327### Using the Predicate module
328
329
330
331```ts
332import { Predicate } from "effect"
333
334const thing: unknown = {
335 a: 1
336}
337
338if (Predicate.isObject(thing)) {
339 if (Predicate.isNumber(thing.a)) {
340 console.log("number", thing.a)
341 }
342}
343```
344
345## Working with SQL databases
346
347Use the `effect/sql` modules together with a driver package such as
348`@effect/sql-sqlite-node` to access SQL databases. Define domain models with
349`Model.Class` to derive schemas for the database and JSON boundaries, run
350migrations, and write type-safe queries.
351
352- **[Getting started with SQL](./ai-docs/src/40_sql/10_basics.ts)**:
353 Define a schema-backed domain model, run migrations against a SQLite
354 database, and expose a derived repository through a service.
355
356## Effect HttpClient
357
358Build http clients with the `HttpClient` module.
359
360- **[Getting started with HttpClient](./ai-docs/src/50_http-client/10_basics.ts)**: Define a service that uses the HttpClient module to fetch data from an external API
361
362## Building HttpApi servers
363
364`HttpApi` gives you schema-first, type-safe HTTP APIs with runtime validation, typed clients, and OpenAPI docs from one definition.
365
366- **[Getting started with HttpApi](./ai-docs/src/51_http-server/10_basics.ts)**:
367 Define a schema-first API, implement handlers, secure endpoints with
368 middleware, serve it over HTTP, and call it using a generated typed client.
369- **[Testing HttpApi implementations](./ai-docs/src/51_http-server/20_testing.ts)**:
370 Test handlers through an in-memory typed client with `HttpApiTest`, without
371 starting an HTTP server or touching a real database.
372
373## Working with child processes
374
375Use the `effect/process` modules to define child processes and run them with `ChildProcessSpawner`.
376
377- **[Working with child processes](./ai-docs/src/60_child-process/10_working-with-child-processes.ts)**: This example shows how to collect process output, compose pipelines, and stream long-running command output.
378
379## Building CLI applications
380
381Use the "effect/cli" modules to build CLI applications. These modules
382provide utilities for parsing command-line arguments, handling user input, and
383managing the flow of a CLI application.
384
385- **[Getting started with Effect CLI modules](./ai-docs/src/70_cli/10_basics.ts)**:
386 Build a command-line app with typed arguments and flags, then wire subcommand
387 handlers into a single executable command.
388
389## Working with AI modules
390
391Effect's AI modules provide a provider-agnostic interface for language models.
392You can generate text, decode structured objects with `Schema` and stream partial
393responses.
394
395- **[Using LanguageModel for text, objects, and streams](./ai-docs/src/71_ai/10_language-model.ts)**:
396 Configure a provider once, then use `LanguageModel` for plain text
397 generation, schema-validated object generation, and streaming responses.
398- **[Defining and using AI tools](./ai-docs/src/71_ai/20_tools.ts)**:
399 Define tools with schemas, group them into toolkits, implement handlers,
400 and pass them to `LanguageModel.generateText`.
401- **[Stateful chat sessions](./ai-docs/src/71_ai/30_chat.ts)**:
402 The AI `Chat` module maintains conversation history automatically. Build
403 AI agents or chat assistants.
404
405## Building distributed applications with cluster
406
407The cluster modules let you model stateful services as entities and distribute
408them across multiple machines.
409
410- **[Defining cluster entities](./ai-docs/src/80_cluster/10_entities.ts)**: Define distributed entity RPCs and run them in a cluster.