This guide covers production environment configuration, security considerations, and monitoring for the Inmobiliaria API.
Environment Variables
Configure these environment variables in production:
Required Variables
# Node Environment
NODE_ENV=production
# Server Configuration
PORT=3000
# Database
DATABASE_URL="postgres://user:password@host:port/database?sslmode=require"
# Authentication (Better Auth)
BETTER_AUTH_SECRET="your-secure-random-secret-here"
BETTER_AUTH_URL="https://api.yourdomain.com"
FRONTEND_URL="https://yourdomain.com"
# Email Service (Resend)
RESEND_API_KEY="re_xxxxxxxxxx"
EMAIL_FROM="[email protected]"
BUSINESS_EMAIL="[email protected]"
Storage Configuration
Local Storage (Default)
STORAGE_DRIVER=local
UPLOAD_DIR="/var/www/uploads"
PUBLIC_UPLOAD_URL_BASE="https://yourdomain.com/uploads"
S3-Compatible Storage
For AWS S3, Cloudflare R2, or Backblaze B2:
STORAGE_DRIVER=s3
S3_REGION="us-east-1"
S3_BUCKET="your-bucket-name"
S3_ACCESS_KEY_ID="your-access-key"
S3_SECRET_ACCESS_KEY="your-secret-key"
S3_ENDPOINT="https://endpoint.example.com" # Optional, for R2/B2
PUBLIC_UPLOAD_URL_BASE="https://cdn.yourdomain.com"
Optional Variables
# File Upload Limits
MAX_UPLOAD_SIZE_MB=10
MAX_UPLOAD_FILES=10
# OAuth (Optional)
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
Never commit .env files to version control. Use environment variable management provided by your hosting platform.
Security Configuration
HTTPS Required
The API must be served over HTTPS in production:
- Authentication cookies use
Secure flag (HTTPS only)
- Session cookies use
SameSite=none for cross-origin requests
- Database connections require SSL
CORS Configuration
The API is configured to accept requests from FRONTEND_URL:
app.use(
cors({
origin: [process.env.FRONTEND_URL],
credentials: true, // Required for cookies
})
);
The credentials: true setting allows cookies to be sent cross-origin, required for Better Auth session management.
Proxy Configuration
If behind a reverse proxy (nginx, Apache), enable trust proxy:
app.set("trust proxy", 1);
This ensures the API correctly handles forwarded headers from your proxy.
Authentication Secret
Generate a strong random secret for BETTER_AUTH_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Never use the default development secret in production. The application will log warnings if critical environment variables are missing.
Database Configuration
Connection Pooling
The API uses pg (node-postgres) which handles connection pooling automatically:
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
Default pool settings:
- Maximum connections: 10
- Idle timeout: 30 seconds
Adjust if needed based on your traffic.
SSL Connections
Always use SSL for production database connections:
DATABASE_URL="postgres://user:pass@host:port/db?sslmode=require"
The API automatically detects SSL requirement from the connection string.
Health Monitoring
The API provides two health check endpoints:
Root Health Check
Returns API status and database health:
{
"status": "ok",
"message": "Inmobiliaria API Server",
"timestamp": "2026-03-03T12:00:00.000Z",
"database": {
"status": "healthy",
"message": "Connected successfully"
}
}
Database Health Check
Returns HTTP 200 if healthy, 503 if database unavailable:
{
"status": "ok",
"database": {
"status": "healthy",
"message": "Connected successfully"
},
"timestamp": "2026-03-03T12:00:00.000Z"
}
Use this endpoint for load balancer health checks and monitoring systems.
Logging Recommendations
Production Logging
The API logs to stdout/stderr. Configure log aggregation:
- Structured logging: Consider adding Winston or Pino
- Log levels: Info for normal operations, error for failures
- Sensitive data: Never log passwords, tokens, or secrets
Error Handling
In production, detailed error messages are hidden from API responses:
src/middleware/errorHandler.ts:42
if (process.env.NODE_ENV === "production" && statusCode === 500) {
// Hide detailed error in production
message = "An internal server error occurred";
}
Full errors are still logged server-side for debugging.
Process Management
Using PM2 (Recommended)
PM2 provides process management, automatic restarts, and monitoring:
Start Application
pm2 start dist/index.js --name inmobiliaria-api
Monitor Application
pm2 monit
pm2 logs inmobiliaria-api
Using systemd
Create a systemd service file /etc/systemd/system/inmobiliaria-api.service:
[Unit]
Description=Inmobiliaria API
After=network.target postgresql.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/inmobiliaria-api
EnvironmentFile=/var/www/inmobiliaria-api/.env
ExecStart=/usr/bin/node dist/index.js
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable inmobiliaria-api
sudo systemctl start inmobiliaria-api
sudo systemctl status inmobiliaria-api
Using Docker
Create a Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
Build and run:
npm run build
docker build -t inmobiliaria-api .
docker run -d \
-p 3000:3000 \
--env-file .env.production \
--name inmobiliaria-api \
inmobiliaria-api
Deployment Checklist
Before deploying to production:
Reverse Proxy Configuration
nginx Example
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Serve uploaded files directly (if using local storage)
location /uploads {
alias /var/www/uploads;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Connection Pooling
Adjust PostgreSQL pool size based on traffic:
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // Maximum pool connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
File Upload Location
For high-traffic sites, use S3-compatible storage instead of local filesystem:
- Better scalability
- CDN integration
- No server disk space concerns
Caching
Consider adding Redis for:
- Session storage
- Rate limiting
- Frequently accessed data
Troubleshooting
Application Won’t Start
- Check environment variables:
node -e "console.log(process.env.DATABASE_URL)"
- Verify database connectivity:
psql $DATABASE_URL
- Check logs for specific errors
- Ensure
dist/ directory exists (run npm run build)
Authentication Issues
- Verify
BETTER_AUTH_SECRET is set and consistent
- Check
FRONTEND_URL matches your actual frontend URL
- Ensure HTTPS is working (cookies require secure context)
- Verify CORS configuration allows your frontend origin
Database Connection Errors
- Check
DATABASE_URL format and credentials
- Verify database accepts connections from server IP
- Ensure SSL is configured if required
- Check connection pool isn’t exhausted
File Upload Failures
- Verify storage driver configuration (
STORAGE_DRIVER)
- Check upload directory permissions (local storage)
- Verify S3 credentials and bucket permissions (S3 storage)
- Check
MAX_UPLOAD_SIZE_MB setting