dotenv.config() does NOT overwrite existing environment variables - the first value loaded wins.
const dotenv = require("dotenv");
const path = require("path");
// Load .env files in correct order: most generic → most specific
// First value loaded wins, so start with system-wide defaults
// 1. System-wide defaults (development1/.env) - API keys, ROOTDIR, ENVIRONMENT
dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
// 2. Project defaults (projecOne/.env) - APP name, project settings
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
// 3. App/service specific (projecOne/api/.env) - PORT, specific endpoints
dotenv.config({ path: path.resolve(__dirname, "../.env") });
// 4. Local overrides (current directory) - developer-specific settings
dotenv.config({ path: path.resolve(__dirname, ".env") });- System-wide (
development1/.env) loads first → Sets defaults likeREDIS_STREAM_HOST, API keys,ENVIRONMENT - Project level (
projecOne/.env) can override → Different Redis per project if needed - Service level (
projecOne/api/.env) can override → Specific service configuration (ports, endpoints) - Local (
.envin current directory) overrides everything → Developer-specific settings (not committed to git)
development1/
├── .env # System-wide: API keys, default Redis servers
├── .env.example # Template (committed to git)
│
├── standard/
│ └── controllers/
│ ├── .env.example # Controller-specific overrides (rarely needed)
│ ├── redisStream.js # Uses system-wide Redis config
│ ├── localredis.js # Uses system-wide Redis config
│ └── logger.js
│
├── projecOne/
│ ├── .env # Project-level overrides
│ ├── .env.example # Template
│ │
│ └── api/
│ ├── .env # Service-level config (port, endpoints)
│ ├── .env.example # Template
│ └── index.js # Loads all .env files in correct order
│
└── project2/
├── .env # Project-level overrides
├── .env.example # Template
│
├── api/
│ ├── .env # API service config
│ ├── .env.example
│ └── index.js
│
└── web/
├── .env # Web service config
├── .env.example
└── index.js
Purpose: Shared defaults for all projects
- API keys (Jellyfin, Liddar, Zulip, etc.)
- Default Redis servers
- Root directory paths
- Environment identifier (DEV, PROD)
- Shared service URLs
Example:
ROOTDIR=/home/gerrit/workspace/code/development1
ENVIRONMENT=DEV
REDIS_STREAM_HOST=192.168.2.13
REDIS_STREAM_PORT=6379
REDIS_DATA_HOST=192.168.2.40
REDIS_DATA_PORT=6379
LIDDAR_API_KEY=your-key-here
JELLYFIN_API_KEY=your-key-here
ZULIP_SERVER=https://chat.loener.nlPurpose: Project-specific configuration that might differ from system defaults
- Application name
- Project-specific Redis server (if different)
- Project version
Example:
APP=ProjectOne
# Override Redis if this project needs different server
# REDIS_STREAM_HOST=192.168.2.50Purpose: Service-specific configuration
- Port numbers
- API endpoints
- Rate limits
- Timeouts
- Service-specific credentials
Example:
EXPRESS_PORT=3001
API_ENDPOINT=/api/v1
API_RATE_LIMIT=100- ✅
.env.examplefiles (templates without secrets) - ✅ All source code
- ✅
package.jsonfiles
- ❌
.envfiles (contain actual API keys, passwords) - ❌
node_modules/directories - ❌ Log files
- ❌ Any file with actual credentials
.env
.env.local
.env.*.local
!.env.example
node_modules/
logs/
*.log
System-wide modules (used by standard controllers):
cd /home/gerrit/workspace/code/development1
npm install dotenv redis winstonProject-specific modules:
cd /home/gerrit/workspace/code/development1/projecOne
npm install expressService-specific modules:
cd /home/gerrit/workspace/code/development1/projecOne/api
npm install body-parser corsStandard Controllers (development1/standard/controllers):
dotenv- Environment configurationredis- Redis clientwinston- Logging
API Services:
express- Web frameworkdotenv- Environment config- Any project-specific packages
// In projecOne/api/index.js
const dotenv = require("dotenv");
const path = require("path");
// Load environment in correct order
dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
dotenv.config({ path: path.resolve(__dirname, "../.env") });
dotenv.config({ path: path.resolve(__dirname, ".env") });
// Now import standard controllers - they'll use the loaded env variables
const redisStream = require("../../standard/controllers/redisStream");
const logger = require("../../standard/controllers/logger");
// Access environment variables
console.log(process.env.REDIS_STREAM_HOST); // From development1/.env
console.log(process.env.EXPRESS_PORT); // From projecOne/api/.env
console.log(process.env.APP); // From projecOne/.env- Check the order - most generic should load first
- Verify file paths with
path.resolve(__dirname, "...") - Print loaded variables to debug:
console.log(process.env)
- Ensure they're in
development1/.env(system-wide) - Check
.envfile is not in.gitignorelocation - Verify no typos in variable names
- Check
REDIS_STREAM_HOSTandREDIS_DATA_HOSTindevelopment1/.env - Verify Redis server is running:
redis-cli -h <host> -p <port> ping - Check network connectivity and firewall rules
This project is already initialized as a git repository with best practices:
- User configured:
[email protected] - Branch:
main(modern convention) - Initial commit created
.gitignoreconfigured
-
Create a new project on GitLab:
- Go to https://gitlab.loener.nl
- Click "New Project" → "Create blank project"
- Enter project name (e.g., "development1")
- Choose visibility level (Private/Internal/Public)
- Do NOT initialize with README (already exists)
- Click "Create project"
-
Connect local repository to GitLab:
cd /home/gerrit/workspace/code/development1 git remote add origin [email protected]:your-username/your-project-name.git git push -u origin main
-
Verify connection:
git remote -v
Check status:
git statusStage changes:
# Stage specific files
git add path/to/file.js
# Stage all changes
git add .
# Stage all modified files (not new files)
git add -uCommit changes:
# With message
git commit -m "Description of changes"
# With detailed message (opens editor)
git commitPush to GitLab:
# Push to main branch
git push
# Push and set upstream
git push -u origin mainPull latest changes:
git pullView commit history:
git log --oneline
git log --graph --oneline --allIf you have the GitLab extension installed:
- View changes in the Source Control panel (Ctrl+Shift+G)
- Stage files by clicking the + icon
- Write commit message in the text box
- Click ✓ to commit
- Click "..." → "Push" to push to GitLab
- Create merge requests directly from VS Code
Create feature branch:
git checkout -b feature/new-featureSwitch branches:
git checkout main
git checkout feature/new-featureMerge feature into main:
git checkout main
git merge feature/new-featurePush branch to GitLab:
git push -u origin feature/new-featureUndo uncommitted changes:
# Discard changes to specific file
git checkout -- path/to/file.js
# Discard all changes
git reset --hard HEADAmend last commit:
git commit --amend -m "Updated commit message"View differences:
# Changes not yet staged
git diff
# Changes staged for commit
git diff --cached
# Changes in specific file
git diff path/to/file.js- Commit often: Small, focused commits are easier to review and revert
- Write clear commit messages: Describe what and why, not how
- Pull before push: Always get latest changes before pushing
- Use branches: Keep main stable, develop features in branches
- Review changes: Use
git diffbefore committing - Never commit secrets:
.envfiles are gitignored for this reason