
effect-patterns
by 5dlabs
Cognitive Task Orchestrator - GitOps on Bare Metal or Cloud for AI Agents
SKILL.md
name: effect-patterns description: Effect TypeScript patterns including services, layers, schema validation, and Elysia integration. agents: [nova] triggers: [effect, elysia, bun, typescript backend] llm_docs:
- effect
- drizzle
- elysia
- bun
- zod
Effect TypeScript Patterns
Type-safe error handling and composable services using Effect as the core type system for Node.js/Bun backends.
Core Stack
| Component | Library | Purpose |
|---|---|---|
| Runtime | Bun 1.1+ | Fastest Node.js-compatible runtime |
| Framework | Elysia 1.x | End-to-end type safety |
| Type System | Effect 3.x | TypeScript's missing standard library |
| Validation | Effect Schema | Type-safe validation (replaces Zod) |
| HTTP Client | @effect/platform | HTTP operations |
| Database | Drizzle ORM | TypeScript ORM |
| Testing | Vitest, bun:test | Test framework |
Context7 Library IDs
Query these libraries for current best practices:
- Effect:
/effect-ts/effect - Elysia:
elysiajs - Drizzle:
/drizzle-team/drizzle-orm
Effect Documentation
Before implementing Effect code, consult:
- AI Documentation:
https://effect.website/llms.txt - Main Docs:
https://effect.website/docs
Typed Errors
Define errors as classes extending Schema.TaggedError:
import { Schema } from "effect"
class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>("UserNotFoundError")({
userId: Schema.String,
}) {}
class ValidationError extends Schema.TaggedError<ValidationError>("ValidationError")({
message: Schema.String,
field: Schema.String,
}) {}
class DatabaseError extends Schema.TaggedError<DatabaseError>("DatabaseError")({
message: Schema.String,
}) {}
Services with Context.Tag
Define services with typed interfaces:
import { Effect, Context, Layer } from "effect"
class UserRepository extends Context.Tag("UserRepository")<
UserRepository,
{
findById: (id: string) => Effect.Effect<User, UserNotFoundError>
create: (data: CreateUser) => Effect.Effect<User, ValidationError>
}
>() {}
class DatabaseService extends Context.Tag("DatabaseService")<
DatabaseService,
{ query: <T>(sql: string) => Effect.Effect<T[], DatabaseError> }
>() {}
Layer Implementation
Implement services with Layer:
const UserRepositoryLive = Layer.succeed(
UserRepository,
UserRepository.of({
findById: (id) => Effect.tryPromise({
try: () => db.findUser(id),
catch: () => new UserNotFoundError({ userId: id })
}),
create: (data) => Effect.tryPromise({
try: () => db.createUser(data),
catch: (e) => new ValidationError({ message: String(e), field: "unknown" })
})
})
)
const DatabaseServiceLive = Layer.succeed(
DatabaseService,
DatabaseService.of({
query: (sql) => Effect.tryPromise({
try: () => db.query(sql),
catch: (e) => new DatabaseError({ message: String(e) })
})
})
)
Effect.gen for Composition
Use generator syntax for composable logic:
const getUser = (id: string) =>
Effect.gen(function* () {
const repo = yield* UserRepository
const user = yield* repo.findById(id)
return user
})
const createUserWithAudit = (data: CreateUser) =>
Effect.gen(function* () {
const repo = yield* UserRepository
const user = yield* repo.create(data)
yield* Effect.log(`Created user: ${user.id}`)
return user
})
Schema Validation
Use Effect Schema for validation:
import { Schema } from "effect"
const CreateUserSchema = Schema.Struct({
name: Schema.String.pipe(Schema.minLength(1), Schema.maxLength(100)),
email: Schema.String.pipe(Schema.pattern(/^[^@]+@[^@]+\.[^@]+$/)),
age: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.positive())),
})
type CreateUser = Schema.Schema.Type<typeof CreateUserSchema>
// Validate unknown data
const validateUser = Schema.decodeUnknown(CreateUserSchema)
Error Handling with catchTags
Pattern match on typed errors:
const result = await Effect.runPromise(
program.pipe(
Effect.catchTags({
UserNotFoundError: (e) => Effect.succeed({ error: `User ${e.userId} not found` }),
ValidationError: (e) => Effect.succeed({ error: `Invalid ${e.field}: ${e.message}` }),
DatabaseError: (e) => Effect.succeed({ error: `Database error: ${e.message}` }),
})
)
)
Retry with Schedule
import { Effect, Schedule } from "effect"
const fetchWithRetry = Effect.retry(
fetchExternalApi,
Schedule.exponential("1 second").pipe(Schedule.compose(Schedule.recurs(3)))
)
Elysia + Effect Integration
import { Elysia, t } from "elysia"
import { Effect } from "effect"
const app = new Elysia()
.post("/api/users", async ({ body }) => {
const program = Effect.gen(function* () {
const validated = yield* Schema.decodeUnknown(CreateUserSchema)(body)
const repo = yield* UserRepository
return yield* repo.create(validated)
})
return Effect.runPromise(
program.pipe(
Effect.provide(UserRepositoryLive),
Effect.catchAll((e) => Effect.succeed({ error: e.message }))
)
)
}, {
body: t.Object({
name: t.String(),
email: t.String({ format: "email" }),
})
})
Testing with Effect
import { Effect, Layer } from "effect"
const TestUserRepository = Layer.succeed(
UserRepository,
UserRepository.of({
findById: (id) =>
id === "123"
? Effect.succeed({ id: "123", name: "Test User" })
: Effect.fail(new UserNotFoundError({ userId: id })),
create: (data) => Effect.succeed({ id: "new", ...data })
})
)
// In tests
const result = await Effect.runPromise(
getUser("123").pipe(Effect.provide(TestUserRepository))
)
Validation Commands
bun tsc --noEmit
bun eslint src/
bun test
bun build src/index.ts --outdir=dist
Guidelines
- Always define typed errors with Schema.TaggedError
- Use Context.Tag for all services
- Implement services with Layer.succeed
- Compose logic with Effect.gen
- Use Schema for all validation
- Pattern match errors with catchTags
- Provide layers at the edge of the application
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon


