Backend — Environment & Configuration

Installing dependencies, .env setup, and database initialization

Step 1 — Install Backend Dependencies

In the VS Code terminal with saas-pos-backend open, run:

bash

This downloads all required packages. It takes 1–3 minutes. Wait for the cursor to return.

Step 2 — Create the .env File

The repository ships a documented template. Copy it and fill in your values:

bash

(On Windows without Git Bash: duplicate .env.example in the VS Code sidebar and rename the copy to .env.)

Every variable is validated at startup (Zod, in src/config/index.ts). A missing or malformed required value stops the server with a descriptive error, so a typo can't cause silent misbehavior.

Full .env Variable Reference

Only DATABASE_URL, JWT_ACCESS_SECRET, and JWT_REFRESH_SECRET are strictly required to boot — everything else has a sensible default — but you should set the first block explicitly for a working local install.

VariableDescriptionRecommended Dev Value
NODE_ENVRuntime mode: development, production, or testdevelopmentOptional
PORTBackend HTTP port. Use 5000 so it matches the frontend's default NEXT_PUBLIC_API_URL (the frontend itself runs on 3000).5000Required
DATABASE_URLPostgreSQL connection stringpostgresql://postgres:pass@localhost:5432/posvelo?schema=publicRequired
REDIS_URLRedis connection string (sessions, rate limiting, job queues)redis://localhost:6379Required
JWT_ACCESS_SECRETAccess-token signing secret — minimum 32 characters64-char hex stringRequired
JWT_REFRESH_SECRETRefresh-token signing secret — minimum 32 characters, different value64-char hex stringRequired
JWT_ACCESS_EXPIRYAccess token lifetime15mOptional
JWT_REFRESH_EXPIRYRefresh token lifetime7dOptional
CORS_ORIGINSComma-separated list of allowed browser origins — set to your frontend URLhttp://localhost:3000Required
SETUP_ACCESS_CODEOne-time code that unlocks the first-run setup wizard (see First-Time Setup)any strong random stringRequired
SUPER_ADMIN_EMAILEmail for the platform Super Admin created by npm run db:seed:super-adminowner@yourdomain.comOptional
SUPER_ADMIN_PASSWORDPassword for that Super Admin accounta strong passwordOptional
LOG_LEVELPino log verbosity (debug, info, warn, error)debugOptional
DEMO_MODEPublic-demo sandbox: periodic data resets, destructive actions blocked. Leave false.falseOptional
BCRYPT_ROUNDSLegacy bcrypt cost factor (argon2 is the primary hasher)12Optional
RATE_LIMIT_MAXMax requests per rate-limit window100Optional
RATE_LIMIT_WINDOW_MSRate-limit window in milliseconds60000Optional
MASTER_ENCRYPTION_KEYKey for encrypting stored third-party credentials — set in production64-char hex stringOptional
Common naming mistakes

The CORS variable is CORS_ORIGINS (not FRONTEND_URL ), and the token lifetimes are JWT_ACCESS_EXPIRY / JWT_REFRESH_EXPIRY (not …_EXPIRES_IN). Cloudinary credentials belong in the frontend .env.local, not here — the backend never talks to Cloudinary. Prisma uses a single DATABASE_URL; there is no DIRECT_URL.

Optional — Billing (Stripe)

These are only needed for the v2.1 platform features. Leave them blank for a plain install: with no Stripe keys, free / $0 plans still work.

VariableDescriptionExample
STRIPE_SECRET_KEYStripe secret key — enables paid billingsk_test_… / sk_live_…Optional
STRIPE_WEBHOOK_SECRETSigning secret for the Stripe webhookwhsec_…Optional
STRIPE_PUBLISHABLE_KEYStripe publishable keypk_test_… / pk_live_…Optional
STRIPE_CURRENCYISO 4217 billing currency for plan pricingusdOptional
Full reference

For the billing setup see the dedicated guide: Subscriptions & Billing (Stripe setup, plans, and plan limits).

Generating Secure Secrets

Run this command three times in any terminal. Use the outputs for JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, and (in production) MASTER_ENCRYPTION_KEY. The two JWT secrets must be different values.

bash

Getting Your PostgreSQL Database URL

Docker (ships with the project)

The backend includes a docker-compose.yml that starts PostgreSQL 16 and Redis 7 locally. Set a password and start both:

bash

Then use:

Neon.tech (hosted)

  1. Go to neon.tech and create a free account. Click Create Project.
  2. After creation, click Connection Details and copy the Connection String.
  3. It looks like: postgresql://user:pass@host.neon.tech/dbname?sslmode=require
  4. Use this exact string for DATABASE_URL.

Local PostgreSQL

Download from postgresql.org/download and install with defaults. Create a database named posdb:

sql

Your local URL will be:

Getting Your Redis URL

If you used the project's Docker compose above, Redis is already running at redis://localhost:6379. Otherwise:

Upstash (hosted)

Go to upstash.com, create a free account, and create a Redis database. Copy the Redis URL from the dashboard — it starts with rediss://.

Local Redis

  • Windows: Install Memurai from memurai.com
  • macOS: brew install redis then brew services start redis
  • Linux: sudo apt install redis-server then sudo systemctl start redis
  • Local URL: redis://localhost:6379

Step 3 — Database Initialization

Run these commands in order in your backend terminal. Do not skip any.

CommandWhat It Does
npm run db:generateGenerates the Prisma client — the type-safe database access layer
npm run db:migrateCreates all tables in the database. When prompted for a name, enter: initial_setup
npm run db:seedPopulates full demo data: a demo tenant with stores, products, customers, employees, and sample sales
npm run db:seed:super-adminCreates the platform Super Admin account from SUPER_ADMIN_EMAIL / SUPER_ADMIN_PASSWORD in your .env
Notes

Run db:migrate only once on a fresh database. For future code updates use npm run db:migrate:prod — it applies only new changes without prompts or data loss. db:seed:super-admin requires SUPER_ADMIN_EMAIL and SUPER_ADMIN_PASSWORD to be set in .env first; it prints a clear error if they are missing. If you prefer to start with an empty database (no demo data), skip db:seed and create your first business through the setup wizard instead — see First-Time Setup.