better-effect

Introduction

A small, typed architecture for Result-based TypeScript applications.

Build applications around explicit dependencies

Most application code has two independent questions:

  1. Did this operation succeed?
  2. What does this operation need in order to run?

better-result answers the first question with a typed Result. better-effect answers the second without introducing a second error model, a fiber runtime, or a dependency container that your application has to learn.

Effect.gen / fn   describe a Result workflow and its Service requirements
Layer             describes how Services are provided
Runtime           owns the environment and executes workflows
Scope             owns resources and finalizers
better-result     remains the source of truth for success and failure

The complete path

define a Service
      ↓
write an Effect.fn Program (or an eager Effect.gen workflow)
      ↓
provide implementations with Layer
      ↓
execute through Runtime
      ↓
release resources through Scope

The library is intentionally small. It gives you a composition root and lifetime boundaries while leaving HTTP, queues, scheduling, persistence and dependency injection to ordinary TypeScript libraries or adapters. Browse the package catalog for the complete set of published packages. For the Layer-first HTTP client with buffered responses, NDJSON, SSE, and opt-in Hono streaming, see HTTP client. For provider-neutral validation, see Schema. For Better Auth, Kysely and MQ integrations, see Better Auth, Kysely integration, and the MQ guide.

All integration APIs in the current release are Layer-first. If you are upgrading an older application, start with the migration guide before copying an example from an older version.

Layers expose a simple Layer<Provided, Required> contract. Provided and Required are tagged Service instance unions; after composition, Required contains only dependencies that remain external.

Choose your starting point

Start with Getting Started, then read the Mental Model. You will build one Service, provide it through a Layer, and run it with a Runtime.

What this library is not

better-effect is not a reimplementation of Effect. It does not add fibers, schedulers, streams, queues, a full Context abstraction, or a lazy instruction tree. Its public Effect<A, E, R> is a type-only facade over a better-result Result (or Promise of one), with R carrying declaration-only tagged Service instance requirements.

That smaller boundary is a feature: use the pieces when they solve a concrete problem and keep the rest of your application conventional.

On this page