Create a standalone app
This is the recommended path for teams building production applications on top of Open Saasframe. You get a self-contained project that installs Open Saasframe packages from npm — no monorepo clone required. If you're a framework contributor or want to explore the source, see Local setup for the monorepo path.
The create-saasframe-app CLI scaffolds a fully configured, self-contained Open Saasframe application outside the monorepo. The generated project includes Next.js, all core modules pre-enabled, Docker services, and development tooling — ready to customize and deploy independently.
Prerequisites
- Node.js 26+ — the generated project enforces this via a preinstall check.
- Yarn — enabled through Corepack:
corepack enablecorepack prepare yarn@stable --activate
- Docker & Docker Compose — for PostgreSQL (with pgvector), Redis, and Meilisearch.
- GitHub CLI (
gh) — strongly recommended if you want to create or publish the repository from the standalone splash. Install guide: cli.github.com - Codex CLI — recommended OpenAI terminal workflow for the splash
Start coding with AImenu. Install guide: developers.openai.com/codex/cli - Claude Code — recommended Anthropic terminal workflow for the same splash menu. Install guide: code.claude.com/docs/en/setup
- Visual Studio Code — recommended general-purpose editor for standalone Open Saasframe apps. Download: code.visualstudio.com/Download
- Cursor — recommended AI-first editor if you prefer an IDE workflow over a terminal-only CLI workflow. Download: cursor.com/download
Useful one-time setup commands:
gh auth login
npm i -g @openai/codex
curl -fsSL https://claude.ai/install.sh | bash
Scaffold the app
npx create-saasframe-app my-store
This creates a my-store/ directory with the full application template, including package.json, docker-compose.yml, environment templates, and the src/ tree.
Options
| Option | Description |
|---|---|
--registry <url> | Use a custom npm registry for @saasframe packages. |
--verdaccio | Shorthand for --registry http://localhost:4873 (local Verdaccio). |
--help, -h | Show usage information. |
--version, -v | Print the CLI version. |
App name rules: lowercase alphanumeric characters and hyphens only. Cannot start or end with a hyphen.
Set up and run
cd my-store
# 1. Copy environment template and configure
cp .env.example .env
# Edit .env — set DATABASE_URL, JWT_SECRET, REDIS_URL at minimum
# 2. Start infrastructure
docker compose up -d
# 3. Install dependencies
yarn install
# 4. Bootstrap the app (generates modules, runs migrations, seeds data)
yarn initialize
# 5. Start the dev server
yarn dev
yarn initialize handles code generation (yarn generate) and database migrations (yarn db:migrate) internally — you do not need to run them separately. It also seeds default data and will abort if existing users are found. For upgrades on an existing database, run yarn db:migrate instead of yarn initialize.
If you prefer the old raw terminal output with no splash screen during standalone bootstrap, use yarn setup:classic instead of the compact setup flow.
yarn dev starts the compact development runtime with a splash screen that shows live startup progress. On native local runs the splash is served on http://localhost:4000 by default and auto-opens when supported; the terminal also prints the backend URL at http://localhost:3000/backend. Once the app is ready, the splash can also expose:
Start coding with AIfor detected coding toolsCreate new GitHub repository/Publish to GitHubthroughghin standalone apps
If you want the previous raw passthrough experience instead, run yarn dev:classic.
Useful standalone splash env vars:
SF_DEV_SPLASH_PORT=4100 yarn devto move the splash to a fixed portSF_DEV_AUTO_OPEN=0 yarn devto disable browser auto-openSF_DEV_CREATE_GIT_REPO_FLOW=false yarn devto hide the GitHub publish panelSF_ENABLE_CODING_FLOW_FROM_SPLASH=false yarn devto hide the coding-tools menu
To run several persistent standalone apps against the same PostgreSQL server, pass --database-name[=<name>] to yarn setup or yarn dev:
yarn setup --database-name=client_a # explicit name; offers to update .env (default yes)
yarn setup --database-name # derives name from the current directory
yarn dev --database-name=temp_run --no-update-env # only injects DATABASE_URL into this run
Set SF_DEV_DATABASE_NAME / SF_DEV_DATABASE_UPDATE_ENV for non-interactive automation. Without the flag, yarn setup and yarn dev keep their current behavior — no prompt, no .env mutation.
Navigate to http://localhost:3000/backend and sign in with the credentials printed by yarn initialize.
Test create-saasframe-app locally from the monorepo
If you are contributing to Open Saasframe itself and changing the scaffold, validate it from the monorepo before publishing packages.
Scaffold-only smoke test
Run this from the monorepo root:
yarn test:create-app
This command:
- builds the current branch packages
- scaffolds a fresh standalone app in a temporary directory
- rewrites
@saasframe/*dependencies in that app to local tarballs built from your branch - opens a shell in the generated app directory so you can continue with
cp .env.example .env,yarn install, and the rest of the normal standalone setup
If you only want the generated path and do not want an interactive shell:
yarn test:create-app --no-shell
Full standalone integration parity
To run the ephemeral standalone integration flow against a freshly scaffolded app:
yarn test:create-app:integration
This command scaffolds a temporary standalone app, installs fresh local @saasframe/* tarballs from the current branch, and runs the standalone integration suite through the local CLI. Docker must be available for this flow.
What's in the box
The scaffolded app comes with 27 pre-enabled modules covering the most common commerce and business operations:
- CRM — companies, people, deals, activities, todos
- Catalog — products, categories, variants, pricing, offers
- Sales — orders, quotes, invoices, shipments, payments
- Auth — users, roles, RBAC, session management
- Search — full-text, vector, and token-based search via Meilisearch
- Workflows — step-based automation with visual editor
- Currencies — multi-currency support with exchange rates
- Custom fields — tenant-specific field sets on any entity
- Notifications — in-app notification system
- Content — static pages (privacy, terms)
- Onboarding — setup wizard and tenant provisioning
- AI assistant — MCP-powered AI tools and chat
All modules are listed in src/modules.ts and can be individually disabled or ejected.
Project structure
my-store/
├── .dockerignore
├── .env.example # Environment variable reference
├── .gitignore
├── .yarnrc.yml # Yarn config (registry settings)
├── AGENTS.md # AI agent guidelines (Claude Code)
├── CLAUDE.md # Claude Code project instructions
├── components.json # shadcn/ui component config
├── Dockerfile # Multi-stage production build
├── docker-compose.yml # Services only (PostgreSQL, Redis, Meilisearch)
├── docker-compose.fullapp.yml # Full stack for production-style deploy
├── docker-compose.fullapp.dev.yml # Full stack with hot reload (Windows-friendly)
├── docker/
│ └── scripts/
│ └── dev-entrypoint.sh # Dev container entrypoint
├── next.config.ts # Next.js configuration
├── package.json # Scripts, dependencies
├── postcss.config.mjs # PostCSS / Tailwind config
├── tsconfig.json # TypeScript paths
├── yarn.lock
├── types/ # Type declarations
│ ├── pg/index.d.ts
│ └── react-big-calendar/index.d.ts
├── public/ # Static assets (SVGs)
└── src/
├── bootstrap.ts # App initialization
├── di.ts # DI overrides
├── modules.ts # Enabled modules and their sources
├── proxy.ts # Proxy configuration
├── app/
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Public landing page
│ ├── globals.css # Global styles
│ ├── (backend)/backend/ # Admin panel routes
│ ├── (frontend)/ # Frontend routes
│ └── api/ # API dispatcher + OpenAPI docs
├── components/ # Shared UI components
│ ├── ClientBootstrap.tsx
│ ├── GlobalNoticeBars.tsx
│ ├── NotificationBellWrapper.tsx
│ ├── OrganizationSwitcher.tsx
│ ├── StartPageContent.tsx
│ └── ui/ # shadcn/ui primitives
├── i18n/ # Locale files (en, pl, es, de)
└── modules/ # Your custom modules (from: '@app')
└── auth/
└── __integration__/ # Integration test scaffold
├── TC-AUTH-001.spec.ts
└── helpers/auth.ts
Key files
src/modules.ts— lists every enabled module and where it comes from. Core modules usefrom: '@saasframe/core'; your custom modules usefrom: '@app'.src/modules/— drop custom modules here. Each folder is a full module with the standard structure (index.ts,backend/,api/,data/, etc.).src/di.ts— register app-level DI overrides that run after all module registrars.src/i18n/— locale files (en,pl,es,de). At runtime the active locale is resolved server-side by thelocalecookie, then theAccept-Languageheader, then the default (en).
Forcing a single locale
Set the SF_FORCE_LOCALE env var to pin the whole app to one supported locale (e.g. SF_FORCE_LOCALE=pl for Polish). When set, cookie and Accept-Language detection are bypassed, the in-app language switcher is hidden, and the locale-change endpoint returns 409. Leave it unset (the default) for normal per-user detection.
Docker Compose variants
The scaffolded app includes three Compose files for different scenarios:
| File | When to use |
|---|---|
docker-compose.yml | Local development — starts only infrastructure services (PostgreSQL, Redis, Meilisearch). You run the app with yarn dev on the host. |
docker-compose.fullapp.dev.yml | Containerized development — runs the full stack including the app with hot reload. The app container exposes the dev splash progress page and backend URL on stable host ports. Recommended for Windows or fully containerized workflows. |
docker-compose.fullapp.yml | Production-style deploy — builds and runs the app in production mode inside Docker. Suitable for demos, staging, or deployment. |
AI tooling
The scaffolded project includes AGENTS.md and CLAUDE.md at the root. These files provide context for Claude Code and other AI-assisted development tools, describing the project structure, conventions, and available commands. You can customize them for your team's workflow.
Integration test scaffold
The project includes a starter integration test at src/modules/auth/__integration__/TC-AUTH-001.spec.ts with a helper module for authentication. Use this as a reference when writing Playwright-based integration tests for your custom modules. Run tests with yarn test:integration.
Available scripts
| Command | Description |
|---|---|
yarn setup:classic | Run standalone setup and startup in the legacy raw passthrough mode with no splash screen. |
yarn dev | Start the compact development runtime with hot reload and a splash progress page. |
yarn dev:classic | Start the legacy raw passthrough runtime with no splash screen. |
yarn build | Build the Next.js application for production. |
yarn start | Start the production server. |
yarn generate | Run all code generators (registry, entities, DI, API client). |
yarn db:generate | Generate database migrations from entity diffs. |
yarn db:migrate | Apply pending database migrations. |
yarn db:greenfield | Destructive reset — drops all tables and regenerates. |
yarn initialize | Full bootstrap: generate, migrate, seed roles and data. |
yarn reinstall | Drop everything and re-bootstrap from scratch. |
yarn test | Run the test suite. |
yarn lint | Lint the codebase. |
Add a custom module
Create a new module under src/modules/ and register it in modules.ts:
-
Scaffold the directory:
mkdir -p src/modules/inventory -
Add
src/modules/inventory/index.ts:export const metadata = { title: 'Inventory', group: 'Modules' } -
Register in
src/modules.ts:export const enabledModules: ModuleEntry[] = [// ... existing modules{ id: 'inventory', from: '@app' },] -
Regenerate and run:
yarn generateyarn dev
Add backend pages, API routes, entities, and other module files using the standard module file conventions.
The Build your first Open Saasframe app tutorial series walks through building an inventory module step by step. Those tutorials use monorepo paths (apps/saasframe/src/modules/) but the patterns are identical — in a standalone app, use src/modules/ instead. Everything else (module files, conventions, API routes, backend pages) works the same way.
Eject a core module
When you need to deeply customize a core module — changing its entities, business logic, or UI beyond what overrides allow — you can eject it. Ejecting copies the module's full source into your src/modules/ directory and switches it to from: '@app' so the framework loads your local version.
# See which modules can be ejected
yarn saasframe eject --list
# Eject the currencies module
yarn saasframe eject currencies
# Regenerate after ejection
yarn saasframe generate all
After ejection, the module is fully yours to modify. See the saasframe eject CLI reference for the full list of ejectable modules and detailed usage.
Ejected modules no longer receive automatic updates from package upgrades. You are responsible for merging upstream changes manually when upgrading Open Saasframe versions.
Environment configuration
The .env.example file documents all available variables. Key sections:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string. |
JWT_SECRET | Yes | Secret for signing auth tokens. |
REDIS_URL | Yes | Redis connection for caching and events. |
MEILISEARCH_HOST | No | Meilisearch URL for full-text search. |
OPENAI_API_KEY | No | Enables OpenAI-backed AI features; pair it with SF_DISABLE_VECTOR_SEARCH_AUTOINDEXING=false when you want vector auto-indexing. |
SF_DISABLE_VECTOR_SEARCH_AUTOINDEXING | No | Defaults to true in the example env. Set to false or remove it to enable automatic vector indexing. |
CACHE_STRATEGY | No | memory, sqlite, redis, or jsonfile. Defaults to memory. |
QUEUE_STRATEGY | No | local or async. Use async with Redis for production. |
APP_URL | No | Public URL, used in emails and onboarding. |
Docker services
The generated docker-compose.yml starts three services:
| Service | Image | Default Port |
|---|---|---|
| PostgreSQL | pgvector/pgvector:pg17-trixie | 5432 |
| Redis | redis:7-alpine | 6379 |
| Meilisearch | getmeili/meilisearch:v1.11 | 7700 |
Start them with docker compose up -d and stop with docker compose down. Add -v to remove volumes and reset all data.
Upgrading
To upgrade Open Saasframe packages in your standalone app:
# Update all @saasframe packages to the latest version
yarn up '@saasframe/*'
# Regenerate and apply any new migrations
yarn generate
yarn db:migrate
Review the changelog for breaking changes before upgrading.
Troubleshooting
preinstallcheck fails — make sure you are running Node.js 26 or later. Usenvm use 26orfnm use 24to switch.- Registry errors — if using a private registry, pass
--registry <url>when creating the app or edit.yarnrc.ymlafter scaffolding. - Generators produce empty output — run
yarn installfirst so the@saasframepackages are available innode_modules. - Ejected module errors after upgrade — ejected modules are not updated automatically. Compare your local copy with the upstream source and merge changes manually.
- Docker services not starting — verify Docker is running and ports 5432, 6379, and 7700 are free. Use
docker compose logsto inspect errors.