This document provides AI coding assistants with context about the Unleash codebase. It is tool-agnostic and can be referenced by any AI assistant configuration.
Unleash is an open-source feature flagging platform. This repository (OSS) is a monorepo containing:
- Backend: Node.js/TypeScript REST API (
/src) - Frontend: React/TypeScript single-page application (
/frontend)
Enterprise extends OSS via a separate repository (unleash-enterprise) using a hook-based architecture. Enterprise does not fork OSS; it injects additional functionality through preRouterHook. If the user says that this is an enterprise feature, you need to ask where the enterprise repository is located and work across both this repository and the enterprise repository.
The backend follows a CSR (Controller, Service, Repository/Store) pattern. We promote packages by feature, not by layer (see feature-based modules below), although the legacy components are still packed by layer:
- Controllers (
/src/lib/routes/): Handle HTTP requests, validate input, delegate business logic to services, and demarcate transactions. - Services (
/src/lib/services/): Business logic layer, emit events, manage transactions - Stores (
/src/lib/db/): Data access layer using Knex query builder
Feature-based modules live in /src/lib/features/ where each domain contains its own controller, service, store, and types. Examples: feature-toggle, project, segment, change-request, release-plans.
Key Patterns:
- Audit-log: Services emit typed events (
FeatureCreatedEvent, etc.) for audit trails and read model updates - Transaction wrapper: Use
withTransactional()for atomic operations across services. The common pattern is initiating transactions at the controller level. - Fake implementations: Every store/service has a Fake variant for testing (prefer over mocking)
- Internal feature flags:
flagResolver.isEnabled()controls operational features
Stack: Express, PostgreSQL with Knex, TypeScript with ES modules
The frontend is a React SPA communicating with the backend via REST API.
- Components:
/frontend/src/component/- React components organized by feature domain - Hooks:
/frontend/src/hooks/- 71+ custom hooks for data fetching and mutations - Contexts:
AccessContext(permissions),UIContext(toasts, theme)
Key Patterns:
- Data fetching: SWR-based
useApiGetterhooks for GET requests with caching - Mutations:
useApihook for POST/PUT/DELETE with error handling - Route gating: Routes support
flag,enterprise, andconfigFlagproperties - Styling: MUI
styled()components with emotion, usesxfor one-offs
Stack: React 18+, Vite, Material-UI (MUI), SWR for server state
Enterprise extends OSS through hooks without forking:
- Entry:
unleash-enterprise/src/index.tswraps OSSstart()/create() - Hook:
preRouterHookruns after OSS init, before route binding - Extension: Adds 50+ services, 30+ stores, 50+ controllers
- Gating: License middleware restricts enterprise features
Enterprise-Only Features: Change Requests, SSO (SAML/OIDC), Service Accounts, Signals & Actions, Insights, SCIM, Private Projects, Release Plans, Safeguards
Combined Interfaces: IEnterpriseServices extends IUnleashServices, IUnleashEnterpriseStores extends IUnleashStores
All dependencies are wired at application startup, not scattered throughout the codebase. All services have a dedicated composition root function to stand up the service.:
OSS Composition:
/src/lib/db/index.ts→createStores()instantiates all stores with Knex connection/src/lib/services/index.ts→createServices()instantiates all services with stores + config/src/lib/server-impl.ts→ Orchestrates: DB → Stores → Services → App
Enterprise Composition:
enterprise/src/util/setup-stores.ts→ Creates enterprise stores, merges with OSS storesenterprise/src/util/setup-services.ts→ Creates enterprise services with combined storesenterprise/src/create-enterprise-routes.ts→ Wires everything inpreRouterHook
Why this matters: Never new a service/store inline. Always receive dependencies through constructor injection. This enables testing with fakes and keeps the dependency graph explicit.
To avoid overloading stores with complex queries, we separate read and write concerns:
Write Models (Stores): Handle CRUD operations on single entities
- Keep queries simple: insert, update, delete, getById
- Located in
/src/lib/db/or feature directories - Example:
FeatureToggleStorehandles basic feature CRUD
Read Models: Handle complex queries, aggregations, cross-domain queries and denormalized views
- Optimized for specific read use cases (dashboards, lists, reports)
- Located in feature
/read-models/directories - Example:
FeatureStrategiesReadModel,ProjectOwnersReadModel,FeatureSearchReadModel
When to use Read Models:
- Query spans multiple tables with complex joins
- Need denormalized data for performance
- Building dashboard/overview endpoints
- Query doesn't map to a single entity's lifecycle
- You don't want to expose the entire write model and only need one value from another module
Pattern: Services coordinate between stores (writes) and read models (reads). Controllers call services or read models, never stores directly.
We follow three core principles:
- Test code always - We test our code and prefer automation over manual testing
- Write maintainable code - Code is communication; clarity and readability are paramount
- Think before committing
Detailed standards are documented as Architectural Decision Records (ADRs). They can be located:
- /contributing/ADRs/back-end/
- /contributing/ADRs/front-end/
- /contributing/ADRs/overarching/
Instead of !!someVariable prefer Boolean(someVariable).
- Migrations live in
/src/migrations/ - Never modify a merged migration; create a new one instead
- Each migration needs
upanddownmethods - Use
pnpm db-migrate create <name>to create new migrations
- Backend: Vitest + Supertest for API testing; fake stores for isolation
- Frontend: Vitest + Testing Library
- E2E: Cypress (
/frontend/cypress/)
Run tests with:
pnpm test # All tests
pnpm test:frontend # Frontend only
pnpm test:backend # Backend only| File | Purpose |
|---|---|
/src/server.ts |
Main entry point |
/src/lib/app.ts |
Express app setup, middleware stack |
/src/lib/routes/index.ts |
Route registration |
/src/lib/services/index.ts |
Service factory |
/src/lib/db/index.ts |
Store factory |
/src/lib/types/index.js |
Importing types |
| Pattern | Example Location |
|---|---|
| Controller | /src/lib/features/feature-toggle/feature-toggle-controller.ts |
| Service | /src/lib/features/feature-toggle/feature-toggle-service.ts |
| Store (write model) | /src/lib/features/feature-toggle/feature-toggle-store.ts |
| Read Model | /src/lib/features/feature-search/feature-search-read-model.ts |
| Composition Root | /src/lib/services/index.ts |
| API Hook (GET) | /frontend/src/hooks/api/getters/useFeature/useFeature.ts |
| API Hook (mutation) | /frontend/src/hooks/api/actions/useFeatureApi.ts |
| Fake Store | /src/test/fixtures/fake-feature-toggle-store.ts |