> effect-platform
You are an expert in Effect (Effect-TS), the production-grade TypeScript framework for building reliable, composable applications. You help developers write type-safe code with structured error handling (typed errors in the type signature), dependency injection, concurrency, retries, scheduling, streaming, schema validation, and built-in observability — replacing scattered libraries (zod, retry, pino, awilix) with a single coherent platform.
curl "https://skillshub.wtf/TerminalSkills/skills/effect-platform?format=md"Effect Platform — Production TypeScript Framework
You are an expert in Effect (Effect-TS), the production-grade TypeScript framework for building reliable, composable applications. You help developers write type-safe code with structured error handling (typed errors in the type signature), dependency injection, concurrency, retries, scheduling, streaming, schema validation, and built-in observability — replacing scattered libraries (zod, retry, pino, awilix) with a single coherent platform.
Core Capabilities
Typed Errors and Effects
import { Effect, pipe } from "effect";
import { Schema } from "@effect/schema";
// Typed errors — compiler knows exactly what can fail
class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", {
userId: Schema.String,
}) {}
class DatabaseError extends Schema.TaggedError<DatabaseError>()("DatabaseError", {
message: Schema.String,
}) {}
class ValidationError extends Schema.TaggedError<ValidationError>()("ValidationError", {
field: Schema.String,
reason: Schema.String,
}) {}
// Function signature tells you: returns User, can fail with UserNotFound | DatabaseError
function getUser(id: string): Effect.Effect<User, UserNotFound | DatabaseError> {
return pipe(
Effect.tryPromise({
try: () => db.users.findUnique({ where: { id } }),
catch: (e) => new DatabaseError({ message: String(e) }),
}),
Effect.flatMap((user) =>
user ? Effect.succeed(user) : Effect.fail(new UserNotFound({ userId: id })),
),
);
}
// Compose effects — errors propagate and accumulate in the type
function getUserProfile(id: string): Effect.Effect<
UserProfile,
UserNotFound | DatabaseError | ValidationError // Compiler tracks all possible errors
> {
return pipe(
getUser(id),
Effect.flatMap((user) => getPreferences(user.id)),
Effect.map((prefs) => ({ ...user, preferences: prefs })),
);
}
// Handle errors exhaustively
const program = pipe(
getUserProfile("user-42"),
Effect.catchTags({
UserNotFound: (e) => Effect.succeed({ error: "User not found", userId: e.userId }),
DatabaseError: (e) => Effect.die(e), // Unrecoverable — crash
ValidationError: (e) => Effect.succeed({ error: `Invalid ${e.field}: ${e.reason}` }),
}),
);
// Run
const result = await Effect.runPromise(program);
Dependency Injection (Layers)
import { Effect, Context, Layer } from "effect";
// Define service interfaces
class Database extends Context.Tag("Database")<Database, {
readonly query: (sql: string) => Effect.Effect<any[], DatabaseError>;
}>() {}
class EmailService extends Context.Tag("EmailService")<EmailService, {
readonly send: (to: string, subject: string, body: string) => Effect.Effect<void>;
}>() {}
// Use services in your effects
function createUser(data: CreateUserInput) {
return Effect.gen(function* () {
const db = yield* Database;
const email = yield* EmailService;
const [user] = yield* db.query("INSERT INTO users ...");
yield* email.send(data.email, "Welcome!", "Thanks for signing up");
return user;
});
}
// Provide implementations via Layers
const DatabaseLive = Layer.succeed(Database, {
query: (sql) => Effect.tryPromise({ try: () => pool.query(sql), catch: (e) => new DatabaseError({ message: String(e) }) }),
});
const EmailLive = Layer.succeed(EmailService, {
send: (to, subject, body) => Effect.promise(() => resend.emails.send({ to, subject, html: body })),
});
// Compose and run
const AppLayer = Layer.merge(DatabaseLive, EmailLive);
await Effect.runPromise(pipe(createUser(input), Effect.provide(AppLayer)));
Concurrency and Retries
import { Effect, Schedule } from "effect";
// Retry with exponential backoff
const reliableCall = pipe(
callExternalApi(),
Effect.retry(
Schedule.exponential("100 millis").pipe(
Schedule.compose(Schedule.recurs(5)), // Max 5 retries
Schedule.jittered, // Add randomness
),
),
);
// Concurrent execution with limits
const results = yield* Effect.forEach(
userIds,
(id) => fetchUserData(id),
{ concurrency: 10 }, // Max 10 concurrent
);
// Race — first to complete wins
const fastest = yield* Effect.race(
fetchFromPrimary(query),
fetchFromReplica(query),
);
// Timeout
const withTimeout = pipe(
longRunningTask(),
Effect.timeout("5 seconds"),
);
Installation
npm install effect @effect/schema @effect/platform
Best Practices
- Typed errors — Errors in the type signature; compiler enforces exhaustive handling; no forgotten catch blocks
- Effect.gen — Use generator syntax for readable async-like code;
yield*instead ofawait - Layers for DI — Define services with
Context.Tag; swap implementations for testing without mocks - Schema validation — Use
@effect/schemainstead of Zod; integrates with Effect error channel - Retry policies — Use
Schedulefor retry, backoff, jitter; declarative, composable - Concurrency — Use
Effect.forEachwithconcurrencyoption; bounded parallelism built-in - Streaming — Use
Streamfor processing large datasets; backpressure, transformation, batching - Observability — Built-in tracing and metrics via OpenTelemetry; no additional instrumentation code
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.