Skip to main content
Never commit .env or .env.local files to source control. Add them to .gitignore before your first commit.

Frontend variables

Create a .env.local file in the project root (next to package.json).
.env.local
NEXT_PUBLIC_API_URL=http://localhost:5000/api
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
VariableRequiredDefaultDescription
NEXT_PUBLIC_API_URLYeshttp://localhost:5000/apiFull URL of the Express backend API, including the /api prefix. Change this to your hosted backend URL in production.
NEXT_PUBLIC_SUPABASE_URLYesYour Supabase project URL. Found in Project Settings → API in the Supabase dashboard.
NEXT_PUBLIC_SUPABASE_ANON_KEYYesYour Supabase project’s anon/public key. Found in Project Settings → API in the Supabase dashboard.
Variables prefixed with NEXT_PUBLIC_ are exposed to the browser bundle. Do not put secrets in NEXT_PUBLIC_ variables.

Backend variables

Create a .env file inside the backend/ directory.
backend/.env
DATABASE_URL="postgresql://user:password@localhost:5432/inventory_db"
JWT_SECRET="your-super-secret-jwt-key"
PORT=5000
NODE_ENV=development
VariableRequiredDefaultDescription
DATABASE_URLYesPostgreSQL connection string. Format: postgresql://user:password@host:port/database
JWT_SECRETYesSecret key used to sign and verify JWTs. Must be a long random string in production.
PORTNo5000Port the Express server listens on.
NODE_ENVNodevelopmentRuntime environment. Set to production for deployed instances.

DATABASE_URL

Prisma reads DATABASE_URL directly from the environment to connect to PostgreSQL:
backend/prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
Local example:
DATABASE_URL="postgresql://postgres:password@localhost:5432/inventory_db"
For a hosted database, replace the host, port, user, password, and database name with the values from your provider.

JWT_SECRET

The auth middleware signs and verifies every JWT using this value:
backend/src/middleware/auth.ts
const decoded = jwt.verify(token, process.env.JWT_SECRET || "your-secret-key") as any
req.userId = decoded.userId
req.tenantId = decoded.tenantId
Generate a strong secret for production:
openssl rand -base64 32
Copy the output and set it as JWT_SECRET. Never reuse a development secret in production.

PORT

The Express server defaults to 5000 if PORT is not set:
backend/src/index.ts
const PORT = process.env.PORT || 5000
// ...
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})
If you change PORT, update NEXT_PUBLIC_API_URL in .env.local to match.

Complete example files

NEXT_PUBLIC_API_URL=http://localhost:5000/api
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key

Build docs developers (and LLMs) love