A full-stack TypeScript monorepo scaffold using the Effect framework as its core architectural backbone. Demonstrates a production-style application with a Node.js HTTP API server, a React web frontend, and a PostgreSQL database — all wired together via Effect's dependency injection (Layer), Schema, and RPC systems.
The sample feature is a Todos CRUD application.
| Layer | Technology |
|---|---|
| Runtime / Package Manager | Bun (>=1.3.10) |
| Language | TypeScript 7.0.2 + @effect/tsgo |
| Core Framework | Effect v4.0.0-beta.107 |
| Monorepo | Turborepo |
| Database | PostgreSQL 18 via Drizzle ORM + @effect/sql-pg |
| API | Effect HttpApi (REST) + Effect Rpc (RPC over HTTP) |
| Auth | Better Auth 1.6 (email/password) |
| Frontend | React 19, TanStack Router, Vite 8 |
| Styling | Tailwind CSS 4.1 |
| Reactive State | @effect/atom-react |
| Linting | oxlint (type-aware) |
| Formatting | oxfmt |
| Testing | Vitest 4.1 + @effect/vitest |
├── apps/
│ ├── server/ # Node.js HTTP API server (composition root)
│ └── web/ # React SPA frontend
├── packages/
│ ├── shared/ # Shared kernel (schemas + tagged errors)
│ ├── contracts/ # HttpApi/Rpc transport contracts
│ ├── domain/ # Business logic (Context services)
│ ├── core/ # Transport handlers
│ ├── db/ # Database layer (Drizzle ORM + Effect)
│ ├── env/ # Typed environment configuration
│ └── auth/ # Better Auth (email/password)
└── repos/
└── effect/ # Vendored Effect library (read-only reference)
# Install dependencies
bun install
# Patch TypeScript/oxlint for tsgo compatibility
bun run prepare# Start PostgreSQL container
bun run docker-dev
# Run database migrations
bun run db-migrate
# Seed admin user + sample todos (dev only)
bun run db-seed
# Start all apps in dev mode
bun run devThe server starts at http://localhost:3101 and the web app at http://localhost:3100.
bun run db-gen # Generate migrations
bun run db-migrate # Run pending migrations
bun run db-push # Push schema to database
bun run db-seed # Seed admin user + sample todos (dev only)
bun run db-studio # Open Drizzle Studiobun run check # Lint + format everythingbun run build # Build all packages/apps
bun run test # Run all testsbun run docker-build # Build and start production containers
bun run docker-down # Stop and remove all containers + volumesThe entire application is composed via Effect Layer composition. Each package exposes a layer (e.g., DatabaseLayer, EnvLive, AppLayer) and dependencies are explicitly provided via Layer.provide(). This makes the dependency graph fully explicit and testable.
EnvLive → DatabaseLayer → AppLayer → Server
The API layer is defined once and served simultaneously as both a REST HTTP API and an RPC-over-HTTP endpoint:
- HTTP REST —
HttpApi/HttpApiBuilder/HttpApiGroupwith automatic schema validation - RPC —
Rpc/RpcGroupserved at/rpcusing NDJSON serialization
The frontend uses the RPC protocol exclusively via AtomRpc.Service.
All API inputs and outputs are defined using Effect Schema. These schemas are shared between HTTP endpoints and RPC procedures, providing automatic request/response validation with full type safety.
Instead of traditional React Query/SWR, the frontend uses @effect/atom-react with AtomRpc.Service to create reactive queries and mutation functions. Atoms automatically track reactivity keys for cache invalidation.
Copy .env.example to .env and configure:
NODE_ENV=development
PORT=3101
HOST=0.0.0.0
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/acme
WEB_PORT=3100
VITE_API_BASE_URL=http://localhost:3101
CORS_ORIGINS=http://localhost:3100
BETTER_AUTH_SECRET=dev-only-change-me-min-32-chars!!
BETTER_AUTH_URL=http://localhost:3101MIT — Wellington Costa