Environment & Config — Cheat Sheet
Full-Stack Integration · 2 topics. Download the PDF or the Instagram carousel and share it.
Cheat Sheet · AiCanCode.org
Environment & Config
Full-Stack Integration2 topicsQuick revision reference
1
Configuration Across Two Deployables
Two apps, two sets of variables, and one hard rule: anything the browser can read is public forever, because it is compiled into the bundle.
- ✓A NEXT_PUBLIC_ or VITE_ variable is inlined at build time and published in every shipped bundle
- ✓Server-only modules should import "server-only" so a client import fails the build instead of leaking a secret
- ✓Validate and coerce the whole environment at startup so a missing value crashes on boot, not on the first request
- ✓Commit .env.example as the onboarding document and keep real values per environment in Vercel and Fly
- ✓A leaked secret must be rotated, not scrubbed — and credentials in a query string end up in access logs
The prefix is a publication decision
# Frontend (.env.local) — NEXT_PUBLIC_ is inlined into the bundle NEXT_PUBLIC_API_URL=https://api.aicancode.org NEXT_PUBLIC_RAZORPAY_KEY_ID=rzp_live_xxx # publishable by design NEXT_PUBLIC_POSTHOG_KEY=phc_xxx # Backend (Fly secrets) — never leaves the server DATABASE_URL=postgres://... JWT_SECRET=... RAZORPAY_KEY_SECRET=... OPENAI_API_KEY=... // The rule: NEXT_PUBLIC_ (or VITE_) is a declaration that this value // is public. It is substituted at BUILD time and lives in the shipped // JavaScript forever — including in every previously deployed bundle. // So a secret placed there is not "leaked if someone looks", it is // published. Rotate it; you cannot unpublish it. // A server-only variable is simply unprefixed, and reading it from a // client component returns undefined by design: process.env.JWT_SECRET // undefined in the browser // The dangerous middle: Next.js server components and route handlers // CAN read secrets — and importing such a module into a client // component is how a secret ends up in the bundle. Mark them: import 'server-only' // build fails if a client imports it
2
Running Both Locally
Two processes, a database and a proxy decision. The goal is that a new developer is productive in one command, and that local behaviour predicts production.
- ✓Run infrastructure in Docker and the apps natively for fast reloads and a usable debugger
- ✓Choose proxy or direct to match production — a mismatch hides cookie and CORS failures until deploy
- ✓Cookies on localhost are same-site even across ports, which is why cookie auth can pass locally and fail in production
- ✓Use a seed script rather than production data, and keep migrations single-headed and in version control
- ✓Case-sensitive filesystems, serverless writes and real latency are the differences that produce production-only bugs
Infra in Docker, apps native, one command
# Terminal 1 — API
cd tool-hub-api && uvicorn app.main:app --reload --port 8000
# Terminal 2 — web
cd ToolHub && npm run dev # :3000
# One command instead, with concurrently or a Makefile
"dev": "concurrently -n api,web -c blue,green \
\"cd ../api && uvicorn app.main:app --reload\" \"next dev\""
# docker compose, when the stack has more moving parts
services:
db: { image: postgres:16, ports: ["5432:5432"],
environment: { POSTGRES_PASSWORD: dev } }
redis: { image: redis:7, ports: ["6379:6379"] }
api: { build: ./api, ports: ["8000:8000"], depends_on: [db, redis],
volumes: ["./api:/app"] } # bind mount for hot reload
# Run infrastructure in Docker and the apps natively: you get fast
# reloads and a real debugger, without installing Postgres by hand.
# The target is a README with three lines:
# cp .env.example .env.local
# docker compose up -d
# npm run devLearn this free with Aria, your AI tutor → AiCanCode.org/learn/full-stack