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 a caret peer dependency on the first Effect 4 release these packages support:

"peerDependencies": {
"effect": "^4.0.2"
}

Any later 4.x satisfies it, so the packages use the one copy of effect your app already installs. Two copies are what to avoid: a schema or service tag from one does not match the other, and the mismatch surfaces as a type error deep inside a signature or a missing service at runtime. The test suite runs against effect 4.0.5.

@effect-server-utils/unit-of-work declares @effect-server-utils/cqrs as a caret peer on the version it was released with. The two are coupled through the DeferralSink contract, and while they are pre-1.0 betas a caret only admits later betas of the same line, so they still move 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.