Opinionated backend development standards for Node.js + Express + TypeScript microservices. Covers layered architecture, BaseController pattern, dependency injection, Prisma repositories, Zod validation, unifiedConfig, Sentry error tracking, async safety, and testing discipline.
71
71%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Complete guide to managing configuration in backend microservices.
Problems with process.env:
Benefits of unifiedConfig:
// ❌ NEVER DO THIS
const timeout = parseInt(process.env.TIMEOUT_MS || '5000');
const dbHost = process.env.DB_HOST || 'localhost';
// ✅ ALWAYS DO THIS
import { config } from './config/unifiedConfig';
const timeout = config.timeouts.default;
const dbHost = config.database.host;Example of problems:
// Typo in environment variable name
const host = process.env.DB_HSOT; // undefined! No error!
// Type safety
const port = process.env.PORT; // string! Need parseInt
const timeout = parseInt(process.env.TIMEOUT); // NaN if not set!With unifiedConfig:
const port = config.server.port; // number, guaranteed
const timeout = config.timeouts.default; // number, with fallbackexport interface UnifiedConfig {
database: {
host: string;
port: number;
username: string;
password: string;
database: string;
};
server: {
port: number;
sessionSecret: string;
};
tokens: {
jwt: string;
inactivity: string;
internal: string;
};
keycloak: {
realm: string;
client: string;
baseUrl: string;
secret: string;
};
aws: {
region: string;
emailQueueUrl: string;
accessKeyId: string;
secretAccessKey: string;
};
sentry: {
dsn: string;
environment: string;
tracesSampleRate: number;
};
// ... more sections
}File: /blog-api/src/config/unifiedConfig.ts
import * as fs from 'fs';
import * as path from 'path';
import * as ini from 'ini';
const configPath = path.join(__dirname, '../../config.ini');
const iniConfig = ini.parse(fs.readFileSync(configPath, 'utf-8'));
export const config: UnifiedConfig = {
database: {
host: iniConfig.database?.host || process.env.DB_HOST || 'localhost',
port: parseInt(iniConfig.database?.port || process.env.DB_PORT || '3306'),
username: iniConfig.database?.username || process.env.DB_USER || 'root',
password: iniConfig.database?.password || process.env.DB_PASSWORD || '',
database: iniConfig.database?.database || process.env.DB_NAME || 'blog_dev',
},
server: {
port: parseInt(iniConfig.server?.port || process.env.PORT || '3002'),
sessionSecret: iniConfig.server?.sessionSecret || process.env.SESSION_SECRET || 'dev-secret',
},
// ... more configuration
};
// Validate critical config
if (!config.tokens.jwt) {
throw new Error('JWT secret not configured!');
}Key Points:
[database]
host = localhost
port = 3306
username = root
password = password1
database = blog_dev
[server]
port = 3002
sessionSecret = your-secret-here
[tokens]
jwt = your-jwt-secret
inactivity = 30m
internal = internal-api-token
[keycloak]
realm = myapp
client = myapp-client
baseUrl = http://localhost:8080
secret = keycloak-client-secret
[sentry]
dsn = https://your-sentry-dsn
environment = development
tracesSampleRate = 0.1# .env file (optional overrides)
DB_HOST=production-db.example.com
DB_PASSWORD=secure-password
PORT=80Precedence:
# .gitignore
config.ini
.env
sentry.ini
*.pem
*.key// Development: config.ini
// Production: Environment variables
export const config: UnifiedConfig = {
database: {
password: process.env.DB_PASSWORD || iniConfig.database?.password || '',
},
tokens: {
jwt: process.env.JWT_SECRET || iniConfig.tokens?.jwt || '',
},
};grep -r "process.env" blog-api/src/ --include="*.ts" | wc -lBefore:
// Scattered throughout code
const timeout = parseInt(process.env.OPENID_HTTP_TIMEOUT_MS || '15000');
const keycloakUrl = process.env.KEYCLOAK_BASE_URL;
const jwtSecret = process.env.JWT_SECRET;After:
import { config } from './config/unifiedConfig';
const timeout = config.keycloak.timeout;
const keycloakUrl = config.keycloak.baseUrl;
const jwtSecret = config.tokens.jwt;Benefits:
Related Files: