What it is
Effect is a library for building typed, composable programs where errors, dependencies and asynchrony all appear in the type signature.
An Effect<Success, Error, Requirements> is a description of a computation, not a running one. Nothing executes until you run it, which makes retries, timeouts and cancellation composable.
Installation
npm install effectGetting started
The smallest useful thing you can do with it, and what each part means.
import { Effect, Data } from 'effect';
class NotFound extends Data.TaggedError('NotFound')<{ id: string }> {}
class NetworkError extends Data.TaggedError('NetworkError')<{ cause: unknown }> {}
const getBook = (id: string): Effect.Effect<Book, NotFound | NetworkError> =>
Effect.tryPromise({
try: () => fetch(`/api/books/${id}`).then((r) => r.json()),
catch: (cause) => new NetworkError({ cause }),
}).pipe(
Effect.flatMap((book) =>
book ? Effect.succeed(book) : Effect.fail(new NotFound({ id })),
),
);const program = getBook('42').pipe(
Effect.retry({ times: 3 }),
Effect.timeout('5 seconds'),
Effect.catchTag('NotFound', () => Effect.succeed(defaultBook)),
Effect.tap((book) => Effect.log(`loaded ${book.title}`)),
);
// Nothing has run yet — this is a description.
const book = await Effect.runPromise(program);Advanced usage
Where the library earns its place over a simpler alternative.
class Database extends Effect.Service<Database>()('Database', {
effect: Effect.gen(function* () {
const pool = yield* acquirePool;
return { query: (sql: string) => Effect.promise(() => pool.query(sql)) };
}),
}) {}
const load = Effect.gen(function* () {
const db = yield* Database; // dependency appears in the type
const [books, authors] = yield* Effect.all(
[db.query('SELECT * FROM books'), db.query('SELECT * FROM authors')],
{ concurrency: 'unbounded' }, // run together
);
return { books, authors };
});
await Effect.runPromise(load.pipe(Effect.provide(Database.Default)));Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Nothing happens when the effect is created
- Effects are descriptions. They only execute under runPromise, runSync or runFork.
- Type error about a missing requirement R
- A service the effect needs was not provided. Add Effect.provide with the corresponding layer.
Best practices
- Adopt it at a module boundary, not incrementally everywhere — half-Effect code is confusing.
- Use Data.TaggedError so catchTag can discriminate failures.
- Prefer Effect.gen over long pipe chains once there is branching.
- Be honest about the learning curve; it is the steepest of any library here.
Background
Why it exists, and what it was reacting to.
Inspired by Scala's ZIO, Effect gives TypeScript a single abstraction covering async, error handling, dependency injection, retries, concurrency and resource safety — at the cost of a substantial new vocabulary.
