Environment Variables
Complete reference for every environment variable and the feature it controls.
The boilerplate is configured entirely through environment variables. Copy the template and fill in the values you need:
cp .env.example .env.localOnly the Firebase web credentials and the email sign-in redirect URL are required for a working default deployment. Everything else is optional and feature-gated: leaving an optional variable empty disables that feature gracefully, with no crash and no build failure. The store's default mode is quote-based (browse → cart → submit a quote request), so payments, transactional email, and SQL backends all stay off until you opt in.
Variables prefixed with NEXT_PUBLIC_ are inlined into the browser bundle at
build time and are public by design. Never give that prefix to a secret. All
other variables are server-only and never reach the client.
Quick reference
| Variable | Required | Feature |
|---|---|---|
NEXT_PUBLIC_FIREBASE_API_KEY | Yes | Firebase / Auth / Firestore |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN | Yes | Firebase Auth |
NEXT_PUBLIC_FIREBASE_PROJECT_ID | Yes | Firebase project |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET | Yes | Firebase Storage |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID | Yes | Firebase |
NEXT_PUBLIC_FIREBASE_APP_ID | Yes | Firebase |
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID | Optional | Firebase Analytics |
NEXT_PUBLIC_EMAIL_SIGNIN_REDIRECT_URL | Yes | Passwordless email sign-in |
RESEND_API_KEY | Optional | Quote/status email notifications |
RESEND_FROM_EMAIL | Optional | Email sender address |
QUOTE_NOTIFICATION_EMAIL | Optional | New-quote notification inbox |
FIREBASE_SERVICE_ACCOUNT | Optional | npm run seed, server-side Firestore |
STRIPE_SECRET_KEY | Optional | Stripe Checkout |
STRIPE_WEBHOOK_SECRET | Optional | Stripe webhook verification |
FIREBASE_SERVICE_ACCOUNT_JSON | Optional | Webhook / server-side Firestore writes |
NEXT_PUBLIC_COMMERCE_BACKEND | Optional | Browser commerce adapter (default firebase) |
COMMERCE_BACKEND | Optional | Server commerce adapter |
SUPABASE_URL | Optional | Supabase backend |
SUPABASE_SERVICE_ROLE_KEY | Optional | Supabase backend |
DATABASE_URL | Optional | Postgres/Prisma backend |
Firebase web credentials
Firebase is the default identity provider and the default commerce backend.
These seven values come from the Firebase console → Project settings → Your
apps. They are NEXT_PUBLIC_* because Firebase web credentials are meant to be
public — access control lives in your Firestore security rules, not in these keys.
| Variable | Required | Example | Description |
|---|---|---|---|
NEXT_PUBLIC_FIREBASE_API_KEY | Yes | (from console) | Web API key for the Firebase JS SDK. |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN | Yes | your-project.firebaseapp.com | Auth domain used by the sign-in flow. |
NEXT_PUBLIC_FIREBASE_PROJECT_ID | Yes | your-project | Firebase / Google Cloud project ID. |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET | Yes | your-project.appspot.com | Storage bucket for uploaded assets. |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID | Yes | (from console) | Cloud Messaging sender ID. |
NEXT_PUBLIC_FIREBASE_APP_ID | Yes | (from console) | Firebase web app ID. |
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID | Optional | (from console) | Google Analytics measurement ID; omit to skip analytics. |
Email sign-in redirect
The storefront uses Firebase passwordless (email-link) sign-in. After a user clicks the link in their inbox, Firebase redirects them back to this absolute URL to complete authentication.
| Variable | Required | Example | Description |
|---|---|---|---|
NEXT_PUBLIC_EMAIL_SIGNIN_REDIRECT_URL | Yes | http://localhost:3000/complete-login | Absolute URL the email sign-in link returns to. Consumed by src/lib/firebase/authService.ts. |
The host in this URL must be listed under Firebase Auth → Settings → Authorized
domains, or the redirect is rejected. In production, point it at your deployed
domain's /complete-login route.
Resend email notifications (optional)
Transactional email is powered by Resend and is entirely
optional. When RESEND_API_KEY is empty, the POST /api/quotes/notify and
POST /api/notifications/status endpoints skip sending and respond
{ skipped: true } — the quote flow itself is unaffected.
| Variable | Required | Default | Description |
|---|---|---|---|
RESEND_API_KEY | Optional | (empty → email disabled) | Resend API key. Leaving it empty disables all outgoing email. |
RESEND_FROM_EMAIL | Optional | onboarding@resend.dev | Verified sender address. Falls back to Resend's shared testing sender when unset. |
QUOTE_NOTIFICATION_EMAIL | Optional | siteConfig.contact.email | Inbox that receives new-quote notifications. Falls back to the site contact email. |
The fallbacks are applied server-side in src/app/api/quotes/notify/route.ts:
const from = process.env.RESEND_FROM_EMAIL || "onboarding@resend.dev";
const adminRecipient =
process.env.QUOTE_NOTIFICATION_EMAIL || siteConfig.contact.email;Seed service account (optional)
npm run seed (see scripts/seed.mjs) needs Firebase Admin credentials to write
seed data into Firestore. Point this at a service-account JSON file downloaded
from the Firebase console → Project settings → Service accounts → Generate new
private key.
| Variable | Required | Example | Description |
|---|---|---|---|
FIREBASE_SERVICE_ACCOUNT | Optional | ./firebase-service-account.json | Filesystem path to a service-account JSON key. Also used by firebase-admin for server-side Firestore writes and ID-token verification. |
Never commit the service-account file, and never expose it with a
NEXT_PUBLIC_ prefix — it grants privileged, RLS-bypassing access to your
project. Keep it server-side only.
Stripe Checkout (optional)
Stripe adds a real "Checkout" button alongside the quote flow. The Stripe client
is instantiated lazily (src/lib/stripe.ts), so when STRIPE_SECRET_KEY is
unset the store stays quote-only and the Checkout button simply does not appear.
export function isStripeEnabled(): boolean {
return Boolean(process.env.STRIPE_SECRET_KEY);
}| Variable | Required | Description |
|---|---|---|
STRIPE_SECRET_KEY | Optional | Stripe secret key. Enables Checkout; leave empty to keep the store quote-only. |
STRIPE_WEBHOOK_SECRET | Optional | Signing secret used to verify the POST /api/webhooks/stripe endpoint (from stripe listen or the dashboard). |
FIREBASE_SERVICE_ACCOUNT_JSON | Optional | Service-account JSON contents (not a path). Lets the Stripe webhook record orders server-side. Vercel-friendly; alternatively rely on FIREBASE_SERVICE_ACCOUNT. |
FIREBASE_SERVICE_ACCOUNT_JSON holds the raw private-key JSON. Treat it as a
secret: server-only, never NEXT_PUBLIC_, never committed.
Commerce backend selection
The commerce data layer (src/lib/commerce) is backend-agnostic. Firebase is the
default and runs entirely client-side. Selecting a SQL backend routes the browser
through the /api/commerce RPC route, where a privileged server adapter runs.
Firebase Auth stays the identity provider for every backend — its ID token is
verified server-side via firebase-admin, so the FIREBASE_SERVICE_ACCOUNT*
credentials are required for the SQL backends too.
Two variables must agree:
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_COMMERCE_BACKEND | Optional | firebase | Which adapter the browser uses. |
COMMERCE_BACKEND | Optional | (empty → firebase) | Which server adapter the /api/commerce RPC route resolves. |
firebase(or unset): pure client-side Firebase — no RPC route, no SQL variables needed.supabase|postgres: set both variables to the same value and provide the matching credentials below.
NEXT_PUBLIC_COMMERCE_BACKEND and COMMERCE_BACKEND must name the same backend.
The public one tells the browser which adapter to load; the server one gates the
RPC route. A mismatch means the browser talks to a route that refuses its
requests.
Supabase adapter
Set when COMMERCE_BACKEND=supabase. Values come from Supabase → Project
Settings → API. See the Supabase adapter guide.
| Variable | Required | Example | Description |
|---|---|---|---|
SUPABASE_URL | With Supabase | https://your-project-ref.supabase.co | Supabase project URL. |
SUPABASE_SERVICE_ROLE_KEY | With Supabase | (from dashboard) | Service-role key used by the server adapter. |
Never prefix the Supabase variables with NEXT_PUBLIC_. The service-role key
bypasses Row Level Security and must never reach the browser.
Postgres / Prisma adapter
Set when COMMERCE_BACKEND=postgres. Read by both the Prisma CLI
(prisma.config.ts) and the runtime adapter (src/lib/commerce/server/prisma.ts).
See the Postgres/Prisma adapter guide.
| Variable | Required | Example | Description |
|---|---|---|---|
DATABASE_URL | With Postgres | postgresql://user:password@localhost:5432/ecommerce | Plain PostgreSQL connection string (local, Supabase pooler, Neon, RDS, …). |
This project does not use Prisma Postgres / Accelerate — you never need a
Prisma account or API key. Set DATABASE_URL to a real postgresql:// URL
before running npm run prisma:generate, otherwise the Prisma CLI drops into
its hosted-DB onboarding and asks for an API key. Not using Postgres? Leave it
blank.