Environment variables keep configuration separate from code. Never hard-code secrets.
# .env file (never commit this!)
PORT=3000
NODE_ENV=production
DATABASE_URL=mongodb://...
JWT_SECRET=your-secret-key
API_KEY=abc123
// Install: npm install dotenv
// Load in code
require('dotenv').config();
console.log(process.env.PORT);
console.log(process.env.NODE_ENV);
PORT=3000
NODE_ENV=development
DATABASE_URL=your-database-url
JWT_SECRET=your-secret
API_KEY=your-api-key
| Aspect | Development | Production |
|---|---|---|
| Error Detail | Full stack traces | Limited, user-friendly |
| Logging Level | Verbose (debug, info) | Minimal (warn, error) |
| Auto-reload | Enabled (nodemon) | Disabled |
| Caching | Minimal | Aggressive |
| Minification | No | Yes |
A typical CI/CD pipeline for Node.js applications includes linting, testing, building, and deploying.
# .github/workflows/deploy.yml
name: Node.js CI/CD
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build --if-present
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
# Deploy to your hosting provider
echo "Deploying..."
express-rate-limit to prevent abuse.helmet middleware for security headers.npm audit.// Security setup example
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(helmet());
app.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
message: 'Too many requests, please try again later.'
}));
# Example Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "src/index.js"]