Configuration
Every environment variable, what reads it, and what happens when it is absent.
There is one environment file for local development, .env at the repo root, and every key in it is explained by the comment above it. apps/api loads it through config/env.ts; both Next apps load it in their next.config.ts. Do not create per-app .env files. Production values go in apps/<app>/.env.production — see Deployment.
Required
| Key | What it is |
|---|---|
MONGO_URI | MongoDB connection string — local or Atlas |
JWT_SECRET | Signing key for access tokens. openssl rand -base64 32 |
JWT_EXPIRES_IN | Token lifetime, default 30d |
IMAGEKIT_PRIVATE_KEY + IMAGEKIT_URL_ENDPOINT, or CLOUDINARY_CLOUD_NAME / _API_KEY / _API_SECRET | One image provider — for the demo store and every upload. DEFAULT_UPLOAD_PROVIDER picks it on first run |
INTERNAL_API_KEY | Required in production: the same random value on the API, storefront and dashboard |
App URLs
| Key | Default | Read by |
|---|---|---|
CLIENT_URL | http://localhost:3000 | API — CORS allowlist, email links |
ADMIN_URL | http://localhost:3001 | API — CORS allowlist |
NEXT_PUBLIC_API_URL | http://localhost:8000 | Both frontends — inlined at build time |
NEXT_PUBLIC_WEB_URL | http://localhost:3000 | Storefront canonical URLs, sitemap and JSON-LD; dashboard "View storefront" |
NEXT_PUBLIC_ADMIN_URL | http://localhost:3001 | Storefront links to the seller portal |
CORS_ORIGINS | empty | API — extra allowed origins, comma-separated |
Build-time values
NEXT_PUBLIC_* values are baked into the JavaScript bundle at build time. On Vercel, set them in the project's environment settings *before* the build runs, and rebuild after changing them.Optional services
| Key | Enables | Absent |
|---|---|---|
GOOGLE_CLIENT_ID / _SECRET, OAUTH_<PROVIDER>_CLIENT_ID / _SECRET | Google, Facebook, GitHub, X and LinkedIn sign-in | Only configured providers show a button |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET | Card payments via Stripe Checkout | COD only; card option disabled |
SMTP_HOST / _PORT / _USER / _PASS, EMAIL_FROM | Order, verification and reset emails | Skipped and logged |
REDIS_URL | Background job queue (run pnpm run dev:worker too) | Jobs run inline in the API |
FIREBASE_PROJECT_ID | Legacy mobile Firebase tokens | Web sign-in unaffected |
The Configuration hub
Most behaviour is not an environment variable. The dashboard's Configuration hub (/settings) edits one document the API reads at runtime, so changes apply without a restart:
| Section | Controls |
|---|---|
| Media & uploads | Provider (ImageKit or Cloudinary), fallback, root folder, images per product, upload size and formats, plus a *Test connection* probe |
| Checkout & delivery | Delivery fee, free-delivery threshold, minimum order, customer cancel window, abandoned-basket hours |
| Catalogue | Products per page, low-stock threshold, backorders, hide sold-out, review rules |
| Sellers | Applications open, approval required, commission, products per seller |
| Notifications | Admin alert recipients |
| Email Config | Every email the platform sends, each with a switch; today's count against the daily limit, the last fourteen days, and the latest sends |
| Security & accounts | Registration on/off, password minimum |
| OAuth config | Each sign-in provider on or off, with its key status and callback URL |
| Payments · SEO · Maintenance · Integrations · System | Methods, metadata, holding page, analytics ids, API log level |
Secrets never move there — the hub only reports whether each .env key is set.
Commerce
Dashboard overrides
FREE_DELIVERY_THRESHOLD, DELIVERY_FEE and TAX_RATE are only the first-run defaults. Once the store settings document exists, the dashboard's Settings → Shop values and the default tax rule take over; the storefront cart mirrors whatever checkout will charge.| Key | Default | Meaning |
|---|---|---|
FREE_DELIVERY_THRESHOLD | 40 | Order value above which delivery is free |
DELIVERY_FEE | 3.9 | Flat fee below the threshold |
TAX_RATE | 0 | Applied at order time |
PLATFORM_COMMISSION | 0.05 | Retained on seller sales for the earnings screen |
MAX_PRODUCT_IMAGES | — | Cap on images per product |
MONGODB_STORAGE_QUOTA_MB | 512 (Atlas M0) | Quota shown on the dashboard's DB status |
Session
| Key | Default |
|---|---|
SESSION_COOKIE_NAME | grovia_session |
SESSION_MAX_AGE | Matches JWT_EXPIRES_IN |