Skip to main content

Create a standalone app

Recommended for production apps

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 enable
    corepack 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 AI menu. 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

OptionDescription
--registry <url>Use a custom npm registry for @saasframe packages.
--verdaccioShorthand for --registry http://localhost:4873 (local Verdaccio).
--help, -hShow usage information.
--version, -vPrint 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
info

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 AI for detected coding tools
  • Create new GitHub repository / Publish to GitHub through gh in 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 dev to move the splash to a fixed port
  • SF_DEV_AUTO_OPEN=0 yarn dev to disable browser auto-open
  • SF_DEV_CREATE_GIT_REPO_FLOW=false yarn dev to hide the GitHub publish panel
  • SF_ENABLE_CODING_FLOW_FROM_SPLASH=false yarn dev to 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 use from: '@saasframe/core'; your custom modules use from: '@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 the locale cookie, then the Accept-Language header, 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:

FileWhen to use
docker-compose.ymlLocal development — starts only infrastructure services (PostgreSQL, Redis, Meilisearch). You run the app with yarn dev on the host.
docker-compose.fullapp.dev.ymlContainerized 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.ymlProduction-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

CommandDescription
yarn setup:classicRun standalone setup and startup in the legacy raw passthrough mode with no splash screen.
yarn devStart the compact development runtime with hot reload and a splash progress page.
yarn dev:classicStart the legacy raw passthrough runtime with no splash screen.
yarn buildBuild the Next.js application for production.
yarn startStart the production server.
yarn generateRun all code generators (registry, entities, DI, API client).
yarn db:generateGenerate database migrations from entity diffs.
yarn db:migrateApply pending database migrations.
yarn db:greenfieldDestructive reset — drops all tables and regenerates.
yarn initializeFull bootstrap: generate, migrate, seed roles and data.
yarn reinstallDrop everything and re-bootstrap from scratch.
yarn testRun the test suite.
yarn lintLint the codebase.

Add a custom module

Create a new module under src/modules/ and register it in modules.ts:

  1. Scaffold the directory:

    mkdir -p src/modules/inventory
  2. Add src/modules/inventory/index.ts:

    export const metadata = { title: 'Inventory', group: 'Modules' }
  3. Register in src/modules.ts:

    export const enabledModules: ModuleEntry[] = [
    // ... existing modules
    { id: 'inventory', from: '@app' },
    ]
  4. Regenerate and run:

    yarn generate
    yarn dev

Add backend pages, API routes, entities, and other module files using the standard module file conventions.

Following the tutorials

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.

caution

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:

VariableRequiredDescription
DATABASE_URLYesPostgreSQL connection string.
JWT_SECRETYesSecret for signing auth tokens.
REDIS_URLYesRedis connection for caching and events.
MEILISEARCH_HOSTNoMeilisearch URL for full-text search.
OPENAI_API_KEYNoEnables OpenAI-backed AI features; pair it with SF_DISABLE_VECTOR_SEARCH_AUTOINDEXING=false when you want vector auto-indexing.
SF_DISABLE_VECTOR_SEARCH_AUTOINDEXINGNoDefaults to true in the example env. Set to false or remove it to enable automatic vector indexing.
CACHE_STRATEGYNomemory, sqlite, redis, or jsonfile. Defaults to memory.
QUEUE_STRATEGYNolocal or async. Use async with Redis for production.
APP_URLNoPublic URL, used in emails and onboarding.

Docker services

The generated docker-compose.yml starts three services:

ServiceImageDefault Port
PostgreSQLpgvector/pgvector:pg17-trixie5432
Redisredis:7-alpine6379
Meilisearchgetmeili/meilisearch:v1.117700

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

  • preinstall check fails — make sure you are running Node.js 26 or later. Use nvm use 26 or fnm use 24 to switch.
  • Registry errors — if using a private registry, pass --registry <url> when creating the app or edit .yarnrc.yml after scaffolding.
  • Generators produce empty output — run yarn install first so the @saasframe packages are available in node_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 logs to inspect errors.