Loading...
Loading...
Effect-TS best practices for services, errors, layers, schemas, and testing. Use when writing/reviewing Effect code, implementing services, handling errors, or composing layers.
npx skill4agent add michaelvessia/nixos-config effectanyas TypeSchema.make()Schema.decodeUnknown()catchAllneverErrorSchema.TaggedError{ disableValidation: true }Effect.try()mapErrorcatchAllCause| Pattern | DON'T | DO |
|---|---|---|
| Service definition | | |
| Error types | Generic | |
| Branded IDs | Raw | |
| Running effects | | Return |
| Logging | | |
| Configuration | | |
| Method tracing | Manual spans | |
| Nullable results | | |
| State | Mutable variables | |
| Time | | |
class UserService extends Effect.Service<UserService>()("UserService", {
dependencies: [DatabaseService.Default],
effect: Effect.gen(function* () {
const db = yield* DatabaseService
return {
findById: Effect.fn("UserService.findById")(
(id: UserId) => db.query(/* ... */)
),
}
}),
}) {}
// Usage - dependencies auto-provided
UserService.findById(userId)// Define domain-specific errors
class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String }
) {}
// Handle with catchTag (preserves type info)
effect.pipe(
Effect.catchTag("UserNotFoundError", (e) => /* handle */),
Effect.catchTag("AuthExpiredError", (e) => /* handle */)
)// Branded ID
const UserId = Schema.String.pipe(Schema.brand("@App/UserId"))
// Domain entity with Schema.Class
class User extends Schema.Class<User>("User")({
id: UserId,
email: Schema.String,
createdAt: Schema.DateFromSelf,
}) {
get displayName() { return this.email.split("@")[0] }
}// Declare dependencies in service, not at usage
const MainLayer = Layer.mergeAll(
UserServiceLive,
AuthServiceLive,
DatabaseLive
)
// Run program
Effect.runPromise(program.pipe(Effect.provide(MainLayer)))