TypeScript catches type errors at compile time. But what about runtime failures — network timeouts, missing env vars, malformed API responses?
Effect-TS is a production-grade toolkit that makes TypeScript applications resilient by design. Think of it as Rust's Result type meets Go's structured concurrency — but for TypeScript.
The Problem Effect Solves
// Typical TypeScript — looks safe, isn't
async function getUser(id: string) {
const response = await fetch(`/api/users/${id}`); // Can throw
const data = await response.json(); // Can throw
return data.user; // Can be undefined
}
This function has 3 hidden failure modes. TypeScript won't warn you about any of them.
// With Effect — every failure is visible in the type system
import { Effect, Schema } from "effect";
const User = Schema.Struct({
id: Schema.String,
name: Schema.String,
email: Schema.String,
});
const getUser = (id: string) =>
Effect.gen(function* () {
const response = yield* Effect.tryPromise({
try: () => fetch(`/api/users/${id}`),
catch: () => new NetworkError({ id }),
});
const json = yield* Effect.tryPromise({
try: () => response.json(),
catch: () => new ParseError({ id }),
});
return yield* Schema.decodeUnknown(User)(json);
});
// Type: Effect<User, NetworkError | ParseError | ParseResult.ParseError>
Now every failure mode is in the return type. The compiler forces you to handle them.
Key Features (All Free)
1. Typed Errors
class NotFound extends Data.TaggedError("NotFound")<{ id: string }> {}
class Unauthorized extends Data.TaggedError("Unauthorized")<{}> {}
const program = getUser("123").pipe(
Effect.catchTag("NotFound", (e) => Effect.succeed(defaultUser)),
Effect.catchTag("Unauthorized", () => Effect.fail(new LoginRequired())),
);
2. Dependency Injection (Services)
class Database extends Context.Tag("Database")<Database, {
readonly query: (sql: string) => Effect.Effect<Row[]>;
}>() {}
const getUsers = Database.pipe(
Effect.flatMap((db) => db.query("SELECT * FROM users")),
);
// Provide real or test implementation
const program = getUsers.pipe(
Effect.provideService(Database, realDatabase),
);
3. Structured Concurrency
const parallel = Effect.all([
fetchUsers,
fetchOrders,
fetchProducts,
], { concurrency: 3 });
// All run in parallel, all errors collected, proper cleanup on failure
4. Built-in Retry & Scheduling
const resilient = fetchData.pipe(
Effect.retry({
times: 3,
schedule: Schedule.exponential("1 second"),
}),
Effect.timeout("30 seconds"),
);
Why Developers Are Switching
- Vercel uses Effect in their internal tools
- Effect has 8,000+ GitHub stars and growing fast
- The ecosystem includes HTTP server, SQL, RPC, AI, and more
Getting Started
npm install effect
import { Effect, Console } from "effect";
const program = Effect.gen(function* () {
yield* Console.log("Hello, Effect!");
return 42;
});
Effect.runPromise(program).then(console.log);
Want to build resilient data pipelines? I create robust scraping and API integration solutions. Contact me at spinov001@gmail.com or browse my tools on Apify.
Top comments (0)