Go from blank folder to production-ready project in 30 seconds. Backend-only API, AI/ML app, mobile, full-stack, infra setup — pick what you need and get it wired with auth, database, Docker, CI/CD, hooks, and tests. All optional. All yours.
npx create-projx my-appNo SDK lock-in. No runtime dependency on Projx. Just clean code in your repo that you own forever.
Every new project starts with the same week of plumbing:
- Wire up auth (again).
- Configure the database and migrations (again).
- Write the Dockerfile and
docker-compose.yml(again). - Set up CI, linting, formatting, pre-commit hooks (again).
- Build the same login + CRUD scaffolding (again).
- Realize at 11pm that something's broken and you don't remember why (again).
You ship features two weeks late because the first two weeks were boilerplate.
Ask an LLM to "scaffold a full-stack app" and you get 50 files of plausible-looking code that breaks on first run. Wrong import paths. Outdated package versions. Auth that doesn't actually authenticate. You end up debugging machine-generated boilerplate, which is worse than writing it yourself.
npx create-projx my-app # interactive — pick exactly what you need
cd my-app
./scripts/setup.sh # installs everything you pickedPick any combination of components — they're all optional:
# AI/ML backend only
npx create-projx vision-api --components fastapi -y
# Node API + React frontend
npx create-projx saas --components fastify,frontend -y
# Minimal Express API + React frontend
npx create-projx api-app --components express,frontend -y
# Drizzle-backed Node API + React frontend
npx create-projx ledger --components express,frontend --orm drizzle -y
# Mobile app with backend
npx create-projx field-app --components fastapi,mobile -y
# Full-stack with infra and E2E
npx create-projx prod-app --components fastify,frontend,e2e,infra -y
# Just the infra
npx create-projx platform --components infra -y30 seconds. No matter what you pick, you get auth, Docker, CI/CD, hooks, and tests wired up for it.
If this saves you even one hour, it's already paid for itself. (It's free.)
- It actually runs. Every template is tested in CI before release. No "looks right" surprises.
- Tests ship with the code.
gen entitywrites integration tests alongside every model — 11 tests per entity, against a real database. You start green, not scrambling. - Auto-entity pattern. Define a data model, get CRUD routes, validation, OpenAPI docs, and a typed UI for free. Backend, frontend, and mobile all stay in sync.
- Updates don't nuke your code.
projx updatedoes a 3-tier merge — your custom controllers, pages, and config survive template upgrades. No rewrites. - No lock-in. Projx generates files and walks away. Delete the
.projxconfig and it's just a normal repo. - Adopt incrementally. Already have a project?
projx initadds CI, hooks, and Docker without touching your code. - Pick your package manager. npm, pnpm, yarn, or bun. The choice propagates everywhere — scripts, Docker, CI, docs.
- Pick your Node ORM. Prisma is the auto-CRUD default. Drizzle, Sequelize, and TypeORM ship as first-class addons with identical runtime surface (auto-routes, pagination, filtering, search, lifecycle hooks).
- AI-agent friendly. Ships with SKILL.md so Claude, Cursor, and other agents call Projx instead of hand-writing broken scaffolds.
| Component | Stack | What it gives you |
|---|---|---|
fastapi |
Python, SQLAlchemy, Alembic | Auto-entity CRUD, JWT auth, migrations, OpenAPI docs |
fastify |
Node.js, Prisma / Drizzle / Sequelize / TypeORM, TypeBox | Auto-entity CRUD, JWT auth, typed schemas, OpenAPI docs |
express |
Express 5, TypeScript, Prisma / Drizzle / Sequelize / TypeORM | Auto-entity CRUD, JWT auth, validation, security middleware, health checks |
frontend |
React 19, TypeScript, Vite | Auth, theming, design tokens, light/dark mode |
mobile |
Flutter, Riverpod, GoRouter | Auth, biometric, theming, GoRouter shell |
e2e |
Playwright | Page object model, auth fixtures, accessibility scans |
infra |
Terraform, AWS | EKS, RDS, VPC, ALB, CodePipeline, multi-environment |
Plus, in every project: Docker Compose for dev + prod, GitHub Actions CI per component (path-filtered), pre-commit hooks, secret detection, VS Code settings, and 80% test coverage enforced.
All optional. Pick any combination.
Components are the floor. Features are the opt-in modules that ride on top — same standard, same CI, same skip-list discipline. Today there's one, and it's the one everyone rewrites: auth.
# Fastify + Prisma (default ORM)
npx create-projx my-app --components fastify --auth fastify
# Express + Drizzle
npx create-projx my-app --components express --orm drizzle --auth express
# FastAPI + SQLAlchemy
npx create-projx my-app --components fastapi --auth fastapi
# Multiple backends, one flag — comma-separable targets
npx create-projx my-app --components fastify,express --auth fastify,express- Email + password signup — first user auto-promoted to admin
- Login with JWT access token and refresh-token rotation, with replay detection
- Account lockout after 5 failed logins (15-minute cooldown)
- MFA via TOTP authenticator app — enroll via otpauth URL (client renders the QR), verify on challenge
- MFA recovery codes — generate, single-use consume, regenerate
- MFA lockout on repeated bad codes with time-based unlock
- Password reset via emailed single-use token (30-minute TTL)
- Email verification with resend endpoint (24-hour TTL token)
- Authenticated password change — revokes all other sessions
- Active session listing
- Current-user lookup via
/me - Role-based permissions baked into the JWT payload
- SMTP mailer for verification and reset emails — falls back to logging the link when SMTP is unset
- Cron-driven cleanup of expired tokens (toggle via
AUTH_BACKGROUND_JOBS) - Centralized error responses with
request_idpropagation
Sixteen endpoints across signup, login, MFA challenge/enroll/disable, recovery-code regen, refresh, logout, change-password, sessions, forgot/reset password, verify/resend email, me. Same surface on every backend — mounted at /auth/* on fastify and express, /api/v1/auth/* on fastapi.
| Backend | Prisma | Drizzle | Sequelize | TypeORM | SQLAlchemy |
|---|---|---|---|---|---|
fastify |
yes | yes | yes | yes | — |
express |
yes | yes | yes | yes | — |
fastapi |
— | — | — | — | yes |
Nine combinations, one external contract. ORM-specific bits live under features/auth/ (per-stack, per-ORM subdirectories); the shared surface per stack lives under each stack's common/ subdirectory. Env vars the feature reads: JWT_SECRET, FRONTEND_URL, AUTH_BACKGROUND_JOBS (all backends); JWT_ALGORITHMS, MFA_ISSUER, AUTH_CLEANUP_INTERVAL_SECONDS (fastapi).
Full feature-template spec — manifests, patches, anchors, idempotency — in docs/feature-templates.md.
Projx is a shared operating system for teams that ship with both:
- Developers get a readable layout, stable conventions, and safe updates.
- AI agents get a deterministic CLI to call instead of guessing folder structures.
- Teams get consistency across repos, less drift, and fewer surprise refactors.
If you're an AI agent reading this, see SKILL.md for trigger conditions, decision rules, and the canonical invocation.
# Interactive — pick your stack
npx create-projx my-app
# Non-interactive — specify components
npx create-projx my-app --components fastify,frontend,e2e
# Use Drizzle for Node backends instead of Prisma
npx create-projx my-app --components express,frontend --orm drizzle
# Accept defaults (Fastify + Frontend + E2E)
npx create-projx my-app -yProjx supports npm, pnpm, yarn, and bun. During create, you're prompted to pick one. The choice is stored in .projx and used everywhere — scripts/setup.sh, Docker, CI, pre-commit hooks, and README.
{ "packageManager": "pnpm" }For init, the package manager is auto-detected from lockfiles (pnpm-lock.yaml → pnpm, yarn.lock → yarn, bun.lockb → bun). Falls back to a prompt if no lockfile is found.
npx create-projx my-appInteractive prompt lets you pick components. Or specify them directly:
npx create-projx my-app --components fastapi,fastify,frontend,mobile,e2e,infraAlready have a project? Initialize projx to get the scaffolding (CI, hooks, docker-compose) without overwriting your code:
cd my-existing-app
npx create-projx initAuto-detects components by scanning for fastapi in pyproject.toml, react/fastify in package.json, flutter in pubspec.yaml, and .tf files. Confirms each mapping, creates a projx/baseline branch with the template, and merges it — preserving all your existing code while establishing the ancestry link that makes future updates work.
cd my-app
npx create-projx add frontend mobileCopies the new component directories, regenerates shared files (docker-compose, CI, pre-commit hooks) to include them, and installs dependencies.
Need a second backend service alongside an existing one (e.g. an SMTP listener next to your CRUD API)? Use --name <dir>:
npx create-projx add fastify --name email-ingestorCreates email-ingestor/ with the fastify scaffold and a .projx-component marker. Each instance gets its own job in .github/workflows/ci.yml, its own section in .githooks/pre-commit, and its own install step in scripts/setup.sh. update keeps every instance refreshed on every run.
When templates improve, update your project:
cd my-app
npx create-projx@latest updateUpdates use a 3-tier merge strategy:
- Git merge — if the template merges cleanly with your code, it's auto-committed. Done.
- 3-way merge — if git merge fails, each file is merged individually using
git merge-file. Your additions (extra deps, env vars, custom config) are preserved alongside template updates. Clean merges are auto-staged; only true conflicts need review. - Direct copy — if no merge baseline exists, template files are written directly. You pick which changes to keep via an interactive prompt, and discarded files are automatically added to your skip list.
Your custom files (controllers, pages, middleware) are never deleted. Files you created that don't exist in the template are always preserved.
Common user-owned files are default-skipped automatically — template updates won't touch them:
| Scope | Default skips |
|---|---|
Root (.projx) |
docker-compose.yml, README.md, .githooks/pre-commit, .github/workflows/ci.yml, scripts/setup.sh, scripts/setup-docker.sh, scripts/setup-ssl.sh |
| fastapi | pyproject.toml |
| fastify / frontend / e2e | package.json |
| mobile | pubspec.yaml |
Defaults are applied once on first update and saved to the skip array. To skip additional files, add them to skip in .projx (root-level) or .projx-component (per-component):
// .projx — root skip
{
"version": "x.y.z",
"skip": ["docker-compose.yml", "README.md", "my-custom-config.yml"]
}// fastapi/.projx-component — component skip
{
"component": "fastapi",
"origin": "init",
"skip": ["pyproject.toml", "src/custom_middleware.py"]
}To opt back in to updates for a skipped file, use npx create-projx unpin <file>.
npx create-projx <name> [options]
npx create-projx init
npx create-projx add <components...>
npx create-projx add <type> --name <dir>
npx create-projx update
npx create-projx diff
npx create-projx pin <patterns...>
npx create-projx unpin <patterns...>
npx create-projx pin --list
npx create-projx doctor [--fix]
npx create-projx gen entity <name> [--ai | --backend]
--components <list> Comma-separated: fastapi,fastify,express,frontend,mobile,e2e,infra
--name <dir> Custom directory for `add <type>` (multi-instance)
--ai Target fastapi (AI/ML) for gen entity
--backend Target fastify (API backend) for gen entity
--no-git Skip git init
--no-install Skip dependency installation
-y, --yes Accept defaults (fastify + frontend + e2e)
-h, --help Show help
See what update would change before applying:
cd my-app
npx create-projx diffShows file-by-file analysis: clean updates, files needing merge, user-only changes, and skipped files.
Skip files from future template updates without editing JSON:
npx create-projx pin backend/pyproject.toml # skip this file
npx create-projx pin "backend/src/**" # skip with glob
npx create-projx unpin backend/pyproject.toml # allow updates again
npx create-projx pin --list # show all pinned filesFiles inside a component directory are added to that component's .projx-component skip list. Root-level files are added to .projx skip.
Diagnose issues with your projx setup:
npx create-projx doctor # check everything
npx create-projx doctor --fix # auto-fix what's possibleChecks: config validity, component markers, baseline ref, stale worktrees, skip pattern coverage.
Scaffold a new entity in your primary backend + typed models for frontend/mobile:
npx create-projx gen entity invoice # interactive
npx create-projx gen entity invoice --fields "name:string,amount:number" # non-interactive
npx create-projx gen entity embedding --ai --fields "name:string,vector:json" # target AI backend
npx create-projx gen entity invite --fields "email:string,code:string:unique:generated" # server-fills `code`Field modifiers (Fastify/Express only, after the type): unique adds a Prisma @unique constraint; generated (alias: server, server-generated) marks the field as server-populated — it's omitted from the create-schema and a beforeCreate hook stub is emitted that fills it before persist (override the body of generateXxx() with your slug/code/UUID logic). Use both together for server-issued unique identifiers (invite codes, slugs, short URLs).
When both fastapi and fastify exist, the entity generates in the primary backend only (not both). First run prompts you to choose and saves to .projx:
{ "primaryBackend": "fastify" }Override with --ai (fastapi) or --backend (fastify).
| Component | Generated |
|---|---|
| Primary backend (fastapi) | src/entities/<name>/_model.py + tests/test_<name>_entity.py — model + 11 CRUD/auth tests |
| Primary backend (fastify) | src/modules/<name>/schemas.ts + index.ts + Prisma model + tests/modules/<name>.test.ts |
frontend |
src/types/<name>.ts — TypeScript interface + Create/Update variants |
Tests included: every gen entity writes a working integration test file alongside the model — 11 tests for FastAPI (extending BaseEntityApiTest), 11 tests for Fastify (via describeCrudEntity). Both run against a real database (Postgres). New entities ship green from day one — no scrambling to bolt on tests at go-live.
No migrations — run alembic revision --autogenerate or prisma migrate dev (via your package manager) when ready.
Pick an ORM at create-time with --orm <provider>. Default: prisma. Supported: prisma, drizzle, sequelize, typeorm.
| ORM | Schema home | gen entity produces |
Sync schema to DB |
|---|---|---|---|
prisma |
prisma/schema.prisma |
Prisma model + Fastify/Express module (schemas.ts, index.ts) + integration test |
prisma migrate dev |
drizzle |
src/db/schema.ts |
Typed pgTable + Fastify/Express router wired via _base/ + CRUD test |
drizzle-kit push --force |
sequelize |
src/models/<name>.ts |
Sequelize Model class + aggregator entry + Fastify/Express router + CRUD test |
pnpm db:sync (uses sequelize.sync) |
typeorm |
src/entities/<name>.ts |
Decorated @Entity class + aggregator entry + Fastify/Express router + CRUD test |
pnpm db:sync (uses dataSource.synchronize) |
All four ORMs scaffold equivalent runtime behavior: _base/auto-routes.ts wires POST/GET/PATCH/DELETE/bulk routes with pagination, equality filters, ILIKE search, and the lifecycle hook contract (beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete).
ORM-specific scaffolding lives in cli/src/addons/orms/ — each ORM is a self-contained folder with a manifest.json (deps, file removals, scripts), shared/ files, per-framework overlays, and gen-entity/ templates. Adding a new ORM means adding a new folder there; no CLI core changes.
Rename fastapi/ to backend/? Just rename the folder — the .projx-component marker file moves with it. The update command auto-discovers where each component lives by scanning for these markers. No config changes needed.
backend/.projx-component → { "components": ["fastapi"] }
web/.projx-component → { "components": ["frontend"] }
CI, scripts/setup.sh, pre-commit hooks, and docker-compose are all regenerated with your custom directory names.
my-app/
├── fastapi/ # Auto-entity CRUD backend
│ └── .projx-component # Identifies this as the fastapi component
├── frontend/ # React + Vite shell
│ └── .projx-component
├── e2e/ # Playwright E2E tests
│ └── .projx-component
├── docker-compose.yml # Production (backend + frontend + SSL)
├── .github/workflows/ # CI per component (runs only on changes)
├── .githooks/pre-commit # Format + lint on commit
├── .vscode/ # Editor settings + recommended extensions
├── scripts/ # setup.sh, setup-docker.sh, setup-ssl.sh
└── .projx # Components list + version
Only the components you selected appear. Shared files (docker-compose, CI, hooks) are generated to match your selection.
The core idea: define a data model, get everything else for free.
Backend — Drop a model file. The registry auto-discovers it and generates CRUD routes, schemas, pagination, filtering, sorting, search, FK expansion, and OpenAPI docs.
Field privacy — Sensitive columns (password_hash, secret, api_key, mfa_secret, etc.) are automatically stripped from API responses via a built-in baseline. Add project-specific hidden fields per entity (__hidden_fields__ in FastAPI, hiddenFields in Fastify). Mark entire entities as __private__ / private: true to hide them from the API entirely — no routes registered.
Frontend — Ships a React shell with auth, theming, and design tokens. Build your own pages using the generated types from gen entity.
Mobile — Ships a Flutter shell with auth, biometric, and GoRouter scaffolding. Build screens using the generated Dart models from gen entity.
Both backends ship with a service_configs table for storing third-party credentials (SMTP, OAuth, S3, etc.) encrypted at rest with AES-256-GCM. The purpose column is a free-form string — pick whatever taxonomy fits your project.
// Fastify
import { setServiceConfig, getServiceConfig } from "./lib/service-config.js";
await setServiceConfig(prisma, "smtp", { host, port, user, password });
const cfg = await getServiceConfig<{ host: string }>(prisma, "smtp");# FastAPI
from src.entities.service_config._repository import ServiceConfigRepository
repo = ServiceConfigRepository(session)
await repo.set_config('smtp', {'host': ..., 'port': 587})
cfg = await repo.get_config('smtp')Encryption key resolves from CRED_ENCRYPTION_KEY (32-byte base64). If absent, derived from JWT_SECRET for development. Set an explicit key in production. Reads are cached in-memory for 10 minutes; invalidate(purpose) clears.
The Fastify backend ships with @fastify/rate-limit registered globally. Defaults: 200 requests per minute per user (or per IP for unauthenticated requests). Tune via RATE_LIMIT_MAX and RATE_LIMIT_WINDOW in .env.
Override per route for sensitive endpoints:
fastify.post(
"/auth/resend-verification",
{
config: {
rateLimit: {
max: 5,
timeWindow: "1 hour",
keyGenerator: (req) => (req.body?.email ?? req.ip).toLowerCase(),
},
},
},
handler,
);For the FastAPI service, edge rate limits are enforced by the frontend nginx (auth_limit and api_limit zones in frontend/nginx.conf) — no application-level limiter is wired by default since the service is internal.
Contributing to Projx itself:
git clone https://github.com/ukanhaupa/projx.git
cd projx
./scripts/setup.shThe CLI lives in cli/. Templates are the root-level component directories (fastapi/, frontend/, etc.).
cd cli
pnpm test # run tests
pnpm build # build CLIYou're still reading. Stop reading. Run this:
npx create-projx my-appPick whatever you need from the menu — backend-only, AI app, mobile, full-stack, just infra. 30 seconds. Free. No signup. If you don't like it, rm -rf my-app and we never speak of this again.
Add this to your project's README:
[](https://github.com/ukanhaupa/projx)MIT
