Skip to the article
Aniq-UI

E-CommerceEnvironment variables

Environment variables

What each setting in the API's and the frontends' .env files does, and which ones you need.

For the Full Stack package

Where the settings live

FileRead byHolds secrets
back-end/.envThe API, with yarn dev and inside Docker ComposeYes. Never commit it.
storefront/.envThe storefront, at build timeNo. Every value is public.
admin-dashboard/.envThe admin dashboard, at build timeNo. Every value is public.
.env beside docker-compose.ymlDocker Compose, for the full stackNo

Create each file from the .env.example next to it, which documents every variable: cp .env.example .env. The examples work as they are for a local run.

Where the settings live

One file, storefront/.env, read when the storefront is built. Every value in it is public, so it never holds a secret. Leave it out to run on the sample store; create it from .env.example when you connect an API: cp .env.example .env.

Where the settings live

One file, admin-dashboard/.env, read when the dashboard is built. Every value in it is public, so it never holds a secret. Leave it out to run on the sample store; create it from .env.example when you connect an API: cp .env.example .env.

Where the settings live

One file, back-end/.env, read by the API with yarn dev, and by a container when you pass it with --env-file .env. It holds secrets: never commit it. Create it from .env.example, which documents every variable and works as it is for a local run: cp .env.example .env.

API essentials

The API checks JWT_SECRET and CORS_ORIGIN before it starts. If one is missing or unusable, it stops with one line saying what to fix.

VariableWhat it does
NODE_ENVdevelopment locally, which creates and updates the tables on start. production on a live server, which never touches the tables.
PORTThe API's port, 8000. Both frontends point at it.
DB_TYPEsqlite (the default) or mysql.
SQLITE_DATABASEPath of the SQLite file, ./database.sqlite by default.
JWT_SECRETSigns every sign-in. Required. The example value is accepted in development only.
JWT_EXPIRATIONHow long a sign-in lasts, 7d by default.
CORS_ORIGINThe storefront's and the admin's addresses, comma separated. Unset, it falls back to the two local ports; required in production.
FRONTEND_URLThe admin dashboard's address, which its live notifications connect from. Required in production for those notifications.
TRUST_PROXYOptional. How far the API believes X-Forwarded-For when counting sign-in attempts per visitor. Unset reads it only from a proxy on a private address; false never; a number trusts exactly that many proxies.
RESEED_REVIEWSOptional. 1 makes yarn seed rewrite the seeded reviews.

Generate your own JWT_SECRET with:

Terminal
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

Any credential below still holding its exact .env.example value counts as not set, so the feature it belongs to reports itself off instead of failing on its first call.

Use MySQL instead of SQLite

Create an empty database, then set the driver and the connection in back-end/.env:

back-end/.env
DB_TYPE=mysql
DB_HOST=your-mysql-host
DB_PORT=3306
DB_USERNAME=your-mysql-username
DB_PASSWORD=your-mysql-password
DB_DATABASE=your-database-name

Then run yarn seed for the demo store, or yarn db:sync for the tables with no data. The running API only creates and updates the tables itself when NODE_ENV=development, so with NODE_ENV=production one of those commands runs before the first start (yarn db:sync:prod or yarn seed:prod after yarn build).

Storefront and admin dashboard

Every NEXT_PUBLIC_* value is compiled into the JavaScript the browser loads, and anyone who opens the page can read it. Never put a secret in these files, and rebuild after changing one.

VariableAppWhat it does
NEXT_PUBLIC_API_BASE_URLBothThe API's address including /api, for example http://localhost:8000/api. Setting it is what switches the sample store off; empty or missing, the app runs on its sample store.
NEXT_PUBLIC_MEDIA_HOSTNAMEBothYour media bucket's public host, the host of R2_PUBLIC_URL, without https:// or a trailing slash. Leave it empty until you have a bucket.
NEXT_PUBLIC_SITE_URLStorefrontThe storefront's public address, used for canonical and hreflang links, sharing cards, structured data, robots.txt and sitemap.xml. Set it to your real domain before going live.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYStorefrontA real pk_test_… or pk_live_… key. Empty, or anything that is not a real key, turns the card option at checkout off, and shoppers are asked to choose cash on delivery.
NEXT_PUBLIC_DEMO_CHECKOUTStorefronttrue prefills the checkout with a generated buyer, for demos. Leave it false for a real shop.
API_INTERNAL_URLBothOptional, server only. Where the app's own server reaches the API when that differs from the browser's address, as inside Docker Compose. Leave it empty elsewhere.
NEXT_PUBLIC_WEBSOCKET_BASE_URLAdminThe API's address without /api, for live notifications and permission updates. Optional: when empty it is taken from NEXT_PUBLIC_API_BASE_URL.
NEXT_PUBLIC_STOREFRONT_URLAdminWhere the "Go to storefront" link in the dashboard's navbar goes.
BUILD_STANDALONEBothtrue makes yarn build emit a self-contained server, which the Dockerfiles set. Leave it unset otherwise.

The storefront's marketing tags are NEXT_PUBLIC_* values too.

Docker Compose options

Nothing has to be set for a local run. To change something, put it in a .env file next to docker-compose.yml and run docker compose up --build again: the frontends compile these values in.

VariableWhat it does
SITE_PORT, ADMIN_PORT, API_PORTThe ports on your computer: 3030, 3031 and 8000 by default. Set one when another program already uses that port, such as ADMIN_PORT=3041. The addresses below, CORS_ORIGIN and FRONTEND_URL follow them.
SITE_URL, ADMIN_URL, API_URLWhere the browser reaches each app. Set all three when serving the stack on your own domains; the API's CORS_ORIGIN and FRONTEND_URL are built from them.
MEDIA_HOSTNAMEYour bucket's public host, with the R2_* variables in back-end/.env.
SEED_DEMO_DATAfalse starts with empty tables instead of the demo store.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYThe storefront's card form.
NEXT_PUBLIC_GTM_ID, NEXT_PUBLIC_GA4_MEASUREMENT_ID, NEXT_PUBLIC_META_PIXEL_ID, NEXT_PUBLIC_TIKTOK_PIXEL_ID, NEXT_PUBLIC_SNAPCHAT_PIXEL_ID, NEXT_PUBLIC_PINTEREST_TAG_ID, NEXT_PUBLIC_ANALYTICS_CURRENCYThe storefront's marketing tags, all optional.

The API container also reads back-end/.env when it exists, so payments, media and AI keys are set in one place for both yarn dev and Docker. The compose file wins for the few values that differ inside a container: the database path, the port, NODE_ENV=production, CORS_ORIGIN and FRONTEND_URL. The data lives in the ecommerce-data volume.

Media storage

Product photos, category images, avatars, chat attachments, the AI studio's results and the fitting room's photos are uploaded to a Cloudflare R2 bucket, or any S3-compatible one. Set the five variables in back-end/.env and give both frontends the bucket's public host through NEXT_PUBLIC_MEDIA_HOSTNAME (with Docker Compose, MEDIA_HOSTNAME).

back-end/.env
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
R2_BUCKET_NAME=
R2_PUBLIC_URL=https://your-public-url.r2.dev

Without a bucket, yarn seed stores every product and category image as a /mock-media/… path, and both frontends ship those 35 files in public/mock-media/, so the catalogue renders with nothing uploaded. Only uploading new images stops working: it answers 503 until the bucket is set.

With a bucket, the seed uploads its catalogue images there. Keep public/mock-media/ in both frontends for as long as any product still points at it.

Card payments

Card payments run on Stripe: STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in back-end/.env, and NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY for the storefront. Without them no card payment is taken, and cash on delivery works as usual.

back-end/.env
STRIPE_SECRET_KEY=sk_test_your-stripe-secret-key
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret

Email

The template ships no mail transport, so no message is ever emailed. Outside production, a customer's password-reset token is written to the API's log instead; with NODE_ENV=production it goes nowhere. Connect your own mail provider before going live: the hook is forgotPassword in back-end/src/modules/customer-auth/customer-auth.controller.ts.

AI features

Included with your purchase. Sign in to read, or open it in your download.

Turning on the AI features: the provider keys, the models each feature uses, and what each one costs.

The sample store in the frontends

Both frontends run without an API. Started or built without NEXT_PUBLIC_API_BASE_URL, each answers every request from a sample store in the browser and shows a "Sample data" notice. Setting the address is what switches it off.

  • Any email and password sign in as the sample shopper on the storefront. In the dashboard, any valid email with a password of at least 6 characters signs in as the Super Admin.
  • Writes are real and are kept in the browser's storage, so they survive a reload. To start over, run localStorage.removeItem("mock_db_v1"); location.reload(); in the browser console.
  • Checkout on the storefront completes without calling Stripe.
  • In the dashboard, the AI assistant and the AI studio stay unavailable, because both need the API's keys.
  • Once your API is live, yarn remove:mock in either app deletes the sample store and its notice. It leaves public/mock-media/ in place, because a backend seeded without a bucket points its images there.

How the sample store works

Included with your purchase. Sign in to read, or open it in your download.

How requests are routed to the sample store, and how to change or extend it.

Demo mode

Included with your purchase. Sign in to read, or open it in your download.

Running a public demo: the demo switch, per-visitor accounts and what visitors can change.

Going live

Before you deploy anywhere public:

  1. Set NODE_ENV=production and a long random JWT_SECRET of your own in back-end/.env.
  2. Set CORS_ORIGIN to your storefront's and admin's addresses, comma separated, and FRONTEND_URL to the admin's address.
  3. On an empty database, run yarn build, then yarn db:sync:prod once (or yarn seed:prod for the demo store), then yarn start:prod.
  4. Point your host's health check at /api/health.
  5. Build each frontend with NEXT_PUBLIC_API_BASE_URL at your deployed API and the storefront's NEXT_PUBLIC_SITE_URL at its own address.
  6. Change the Super Admin's password, or start from empty tables.

Not for a shop with real orders

yarn railway:setup builds, fetches the background-removal model, drops every table and seeds, every time it runs. It suits a demo deployment, never the build command of a live shop.

Stuck on a step?

Find a fix before you start over.

Troubleshooting

Cookie Preferences

We use cookies to enhance your browsing experience, analyze site traffic, and personalize content. By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising.