Skip to content

Installation

@effect-server-utils/cqrs and @effect-server-utils/authz are independent — install either or both. @effect-server-utils/unit-of-work is optional and sits on top of the CQRS package: it supplies the atomicity boundary a write-side use case declares, and gives subscribeAfterCommit a real commit to wait for. Without it the event bus still works, running those handlers at the end of each dispatch.

Terminal window
pnpm add @effect-server-utils/cqrs effect
pnpm add @effect-server-utils/authz effect
# optional, on top of cqrs
pnpm add @effect-server-utils/unit-of-work @effect-server-utils/cqrs effect

effect is declared as an exact peer dependency on the beta these packages are built against:

"peerDependencies": {
"effect": "4.0.0-beta.94"
}

Effect 4 is still in beta and its pre-release versions are not compatible with one another. Pinning exactly is what keeps a mismatched effect in your lockfile from surfacing as a type error deep inside a schema signature. When you move to a newer beta, expect to move every package with it.

@effect-server-utils/unit-of-work pins @effect-server-utils/cqrs the same way, and for the same reason: the two are coupled through the DeferralSink contract, so they are released together.

Every package ships ESM and CJS builds with declarations for each, and uses conditional exports. That means moduleResolution has to be one that reads exports"NodeNext", "Node16", or "Bundler". The classic "Node" resolution will not find the types.

tsconfig.json
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true
}
}

strict is not optional in practice: these packages lean on discriminated unions and conditional types whose whole purpose is to make a wrong wiring fail to compile, and most of that evaporates without it.

Every top-level module is also reachable on its own subpath, which is useful when you want one symbol without pulling the barrel:

import { Command } from "@effect-server-utils/cqrs";
// or
import * as Command from "@effect-server-utils/cqrs/command";

Two subpaths are deliberately not in their barrel:

import { checkSerializable } from "@effect-server-utils/cqrs/testing";
import { PassThroughUnitOfWork } from "@effect-server-utils/unit-of-work/testing";

The CQRS helpers derive sample values with a property-testing runtime; the unit-of-work ones are in-memory stand-ins for a datastore. Reached through a barrel either would ride into a consumer’s production bundle for something that only ever runs in a test, so each lives behind its own entry point. See Testing.

  • CQRS overview — messages, buses, and how an application is composed.
  • Unit of work — the atomicity boundary, the driver you supply, and what it adds to the event bus.
  • AuthZ overview — the config seam and the one call at the boundary.