Effect is a TypeScript library for building robust, composable applications. Think of it as a type-safe runtime for managing errors, concurrency, dependencies, and resources.
The Effect Type: Errors as Values
import { Effect, pipe } from "effect";
// Effect<Success, Error, Requirements>
const fetchUser = (id: string): Effect.Effect<User, HttpError | NotFoundError> =>
pipe(
Effect.tryPromise({
try: () => fetch(`/api/users/${id}`).then(r => r.json()),
catch: () => new HttpError(),
}),
Effect.flatMap((data) =>
data ? Effect.succeed(data) : Effect.fail(new NotFoundError(id))
)
);
// Errors are tracked in the type system!
// const result: Effect<User, HttpError | NotFoundError>
Pipe: Compose Everything
const program = pipe(
fetchUser("123"),
Effect.map(user => user.email),
Effect.flatMap(email => sendWelcomeEmail(email)),
Effect.retry({ times: 3 }),
Effect.timeout("5 seconds"),
Effect.catchTag("NotFoundError", () => Effect.succeed("default@email.com")),
Effect.tap(result => Effect.log(`Result: ${result}`)),
);
// Run it
const result = await Effect.runPromise(program);
Concurrency: Structured and Safe
// Run in parallel with concurrency limit
const scrapeAll = Effect.forEach(
urls,
(url) => scrapeUrl(url),
{ concurrency: 10 } // Max 10 concurrent
);
// Race — first to succeed wins
const fastest = Effect.raceAll([
fetchFromCDN1(url),
fetchFromCDN2(url),
fetchFromCDN3(url),
]);
// All must succeed
const [users, products, orders] = await Effect.runPromise(
Effect.all([fetchUsers, fetchProducts, fetchOrders], { concurrency: "unbounded" })
);
Dependency Injection: Layer System
import { Context, Layer } from "effect";
// Define a service
class Database extends Context.Tag("Database")<Database, {
query: (sql: string) => Effect.Effect<Row[]>;
}>() {}
// Implement it
const PostgresLayer = Layer.succeed(Database, {
query: (sql) => Effect.tryPromise(() => pool.query(sql).then(r => r.rows)),
});
// Use it (type-safe requirement)
const getUsers = Database.pipe(
Effect.flatMap(db => db.query("SELECT * FROM users")),
);
// Type: Effect<Row[], SqlError, Database> — requires Database!
// Provide the implementation
const program = getUsers.pipe(Effect.provide(PostgresLayer));
// Type: Effect<Row[], SqlError> — Database requirement satisfied!
Schedule: Retry and Repeat
import { Schedule } from "effect";
// Exponential backoff with jitter
const retryPolicy = pipe(
Schedule.exponential("100 millis"),
Schedule.jittered,
Schedule.compose(Schedule.recurs(5)),
);
const robust = pipe(
scrapeUrl(url),
Effect.retry(retryPolicy),
);
// Repeat on interval
const monitor = pipe(
checkPrice(productUrl),
Effect.repeat(Schedule.spaced("1 hour")),
);
Stream: Process Data Incrementally
import { Stream } from "effect";
const dataStream = pipe(
Stream.fromIterable(urls),
Stream.mapEffect((url) => scrapeUrl(url), { concurrency: 5 }),
Stream.filter(result => result.price < 50),
Stream.tap(result => Effect.log(`Found: ${result.title}`)),
Stream.runCollect,
);
Build robust scraping pipelines? My Apify tools handle the scraping, Effect handles the orchestration.
Custom pipeline? Email spinov001@gmail.com
Top comments (0)