Common Solutions
Guideline Metadata
Version: 1.0.0 Last Modified: 2025-01-19T00:00:00Z Category: Implementation Guidelines Priority: High
Reusable solutions and patterns for frequently encountered problems across projects.
Error Handling Patterns
Result Pattern Implementation
// Base Result type
export interface Result<T> {
success: boolean;
data?: T;
error?: string;
}
// Helper functions
export class ResultHelper {
static success<T>(data: T): Result<T> {
return { success: true, data };
}
static failure<T>(error: string): Result<T> {
return { success: false, error };
}
static async fromPromise<T>(
promise: Promise<T>,
errorMessage?: string
): Promise<Result<T>> {
try {
const data = await promise;
return ResultHelper.success(data);
} catch (error) {
return ResultHelper.failure(
errorMessage || (error as Error).message
);
}
}
}
// Usage example
async function createUser(userData: CreateUserData): Promise<Result<User>> {
return ResultHelper.fromPromise(
this.userProvider.createUser(userData),
'Failed to create user'
);
}
Custom Error Classes
// Base error class
export class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500
) {
super(message);
this.name = this.constructor.name;
}
}
// Specific error types
export class ValidationError extends AppError {
constructor(message: string) {
super(message, 'VALIDATION_ERROR', 400);
}
}
export class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} not found`, 'NOT_FOUND', 404);
}
}
export class DatabaseError extends AppError {
constructor(message: string, originalError?: Error) {
super(message, 'DATABASE_ERROR', 500);
if (originalError) {
this.stack = originalError.stack;
}
}
}
Validation Patterns
Input Validation with Zod
import { z } from 'zod';
// Schema definitions
export const CreateUserSchema = z.object({
email: z.string().email('Invalid email format'),
name: z.string().min(1, 'Name is required').max(100, 'Name too long'),
age: z.number().int().min(18, 'Must be at least 18')
});
export const UpdateUserSchema = CreateUserSchema.partial();
// Validation helper
export class ValidationHelper {
static validate<T>(schema: z.ZodSchema<T>, data: unknown): Result<T> {
try {
const validData = schema.parse(data);
return ResultHelper.success(validData);
} catch (error) {
if (error instanceof z.ZodError) {
const messages = error.errors.map(e => e.message).join(', ');
return ResultHelper.failure(`Validation failed: ${messages}`);
}
return ResultHelper.failure('Validation failed');
}
}
}
// Usage in manager
export class UserManager {
async createUser(userData: unknown): Promise<Result<User>> {
const validation = ValidationHelper.validate(CreateUserSchema, userData);
if (!validation.success) {
return validation;
}
return this.userProvider.createUser(validation.data);
}
}
Business Rule Validation
// Business rule validators
export class UserBusinessRules {
static async validateUserCreation(
userData: CreateUserData,
userProvider: UserProvider
): Promise<Result<void>> {
// Check if email already exists
const existingUser = await userProvider.getUserByEmail(userData.email);
if (existingUser) {
return ResultHelper.failure('Email already registered');
}
// Check age requirement
if (userData.age < 18) {
return ResultHelper.failure('User must be at least 18 years old');
}
return ResultHelper.success(undefined);
}
}
// Usage in manager
export class UserManager {
async createUser(userData: CreateUserData): Promise<Result<User>> {
const businessValidation = await UserBusinessRules.validateUserCreation(
userData,
this.userProvider
);
if (!businessValidation.success) {
return businessValidation;
}
return this.userProvider.createUser(userData);
}
}
Database Patterns
Connection Management
// Database connection interface
export interface Database {
query<T>(sql: string, params?: unknown[]): Promise<T[]>;
queryOne<T>(sql: string, params?: unknown[]): Promise<T | null>;
transaction<T>(callback: (tx: Database) => Promise<T>): Promise<T>;
}
// PostgreSQL implementation
export class PostgresDatabase implements Database {
constructor(private pool: Pool) {}
async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {
try {
const result = await this.pool.query(sql, params);
return result.rows;
} catch (error) {
throw new DatabaseError('Query failed', error as Error);
}
}
async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {
const results = await this.query<T>(sql, params);
return results[0] || null;
}
async transaction<T>(callback: (tx: Database) => Promise<T>): Promise<T> {
const client = await this.pool.connect();
try {
await client.query('BEGIN');
const tx = new PostgresTransaction(client);
const result = await callback(tx);
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
}
Query Builder Pattern
export class QueryBuilder {
private selectClause = '';
private fromClause = '';
private whereConditions: string[] = [];
private params: unknown[] = [];
select(columns: string): QueryBuilder {
this.selectClause = `SELECT ${columns}`;
return this;
}
from(table: string): QueryBuilder {
this.fromClause = `FROM ${table}`;
return this;
}
where(condition: string, value: unknown): QueryBuilder {
this.whereConditions.push(`${condition} = $${this.params.length + 1}`);
this.params.push(value);
return this;
}
build(): { sql: string; params: unknown[] } {
const whereClause = this.whereConditions.length > 0
? `WHERE ${this.whereConditions.join(' AND ')}`
: '';
const sql = [this.selectClause, this.fromClause, whereClause]
.filter(clause => clause)
.join(' ');
return { sql, params: this.params };
}
}
// Usage
const query = new QueryBuilder()
.select('id, email, name')
.from('users')
.where('status', 'active')
.where('age >', 18)
.build();
const users = await database.query<User>(query.sql, query.params);
Caching Patterns
In-Memory Cache
export interface CacheProvider {
get<T>(key: string): Promise<T | null>;
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
delete(key: string): Promise<void>;
clear(): Promise<void>;
}
export class MemoryCacheProvider implements CacheProvider {
private cache = new Map<string, { value: unknown; expires: number }>();
async get<T>(key: string): Promise<T | null> {
const item = this.cache.get(key);
if (!item) {
return null;
}
if (Date.now() > item.expires) {
this.cache.delete(key);
return null;
}
return item.value as T;
}
async set<T>(key: string, value: T, ttlSeconds = 300): Promise<void> {
const expires = Date.now() + (ttlSeconds * 1000);
this.cache.set(key, { value, expires });
}
async delete(key: string): Promise<void> {
this.cache.delete(key);
}
async clear(): Promise<void> {
this.cache.clear();
}
}
// Cached provider wrapper
export class CachedUserProvider implements UserProvider {
constructor(
private userProvider: UserProvider,
private cache: CacheProvider
) {}
async getUserById(id: string): Promise<User | null> {
const cacheKey = `user:${id}`;
const cached = await this.cache.get<User>(cacheKey);
if (cached) {
return cached;
}
const user = await this.userProvider.getUserById(id);
if (user) {
await this.cache.set(cacheKey, user, 300); // 5 minutes
}
return user;
}
}
Authentication Patterns
JWT Token Handling
import jwt from 'jsonwebtoken';
export interface TokenPayload {
userId: string;
email: string;
role: string;
}
export class TokenService {
constructor(private secretKey: string) {}
generateToken(payload: TokenPayload): string {
return jwt.sign(payload, this.secretKey, { expiresIn: '24h' });
}
verifyToken(token: string): Result<TokenPayload> {
try {
const payload = jwt.verify(token, this.secretKey) as TokenPayload;
return ResultHelper.success(payload);
} catch (error) {
return ResultHelper.failure('Invalid token');
}
}
}
// Authentication middleware
export function authMiddleware(tokenService: TokenService) {
return async (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing or invalid token' });
}
const token = authHeader.substring(7);
const verification = tokenService.verifyToken(token);
if (!verification.success) {
return res.status(401).json({ error: verification.error });
}
req.user = verification.data;
next();
};
}
Role-Based Access Control
export enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest'
}
export class AuthorizationService {
private static roleHierarchy = {
[UserRole.Admin]: [UserRole.Admin, UserRole.User, UserRole.Guest],
[UserRole.User]: [UserRole.User, UserRole.Guest],
[UserRole.Guest]: [UserRole.Guest]
};
static hasRole(userRole: UserRole, requiredRole: UserRole): boolean {
return this.roleHierarchy[userRole].includes(requiredRole);
}
static requireRole(requiredRole: UserRole) {
return (req: Request, res: Response, next: NextFunction) => {
const user = req.user as TokenPayload;
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!this.hasRole(user.role as UserRole, requiredRole)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
}
API Response Patterns
Standardized API Responses
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
meta?: {
timestamp: string;
requestId: string;
};
}
export interface PaginatedResponse<T> extends ApiResponse<T[]> {
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
// Response builder
export class ResponseBuilder {
static success<T>(data: T, requestId: string): ApiResponse<T> {
return {
success: true,
data,
meta: {
timestamp: new Date().toISOString(),
requestId
}
};
}
static error(error: string, requestId: string): ApiResponse<never> {
return {
success: false,
error,
meta: {
timestamp: new Date().toISOString(),
requestId
}
};
}
static paginated<T>(
data: T[],
pagination: { page: number; limit: number; total: number },
requestId: string
): PaginatedResponse<T> {
return {
success: true,
data,
pagination: {
...pagination,
totalPages: Math.ceil(pagination.total / pagination.limit)
},
meta: {
timestamp: new Date().toISOString(),
requestId
}
};
}
}
Logging Patterns
Structured Logging
export enum LogLevel {
Debug = 'debug',
Info = 'info',
Warn = 'warn',
Error = 'error'
}
export interface LogEntry {
level: LogLevel;
message: string;
timestamp: string;
context?: Record<string, unknown>;
error?: Error;
}
export interface Logger {
debug(message: string, context?: Record<string, unknown>): void;
info(message: string, context?: Record<string, unknown>): void;
warn(message: string, context?: Record<string, unknown>): void;
error(message: string, error?: Error, context?: Record<string, unknown>): void;
}
export class ConsoleLogger implements Logger {
constructor(private minLevel: LogLevel = LogLevel.Info) {}
debug(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.Debug, message, context);
}
info(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.Info, message, context);
}
warn(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.Warn, message, context);
}
error(message: string, error?: Error, context?: Record<string, unknown>): void {
this.log(LogLevel.Error, message, context, error);
}
private log(
level: LogLevel,
message: string,
context?: Record<string, unknown>,
error?: Error
): void {
if (!this.shouldLog(level)) {
return;
}
const entry: LogEntry = {
level,
message,
timestamp: new Date().toISOString(),
context,
error
};
console.log(JSON.stringify(entry));
}
private shouldLog(level: LogLevel): boolean {
const levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
return levels.indexOf(level) >= levels.indexOf(this.minLevel);
}
}
Configuration Patterns
Environment Configuration
export interface AppConfig {
port: number;
database: {
url: string;
maxConnections: number;
};
redis: {
url: string;
};
jwt: {
secret: string;
expiresIn: string;
};
email: {
host: string;
port: number;
username: string;
password: string;
};
}
export class ConfigService {
private static instance: AppConfig;
static load(): AppConfig {
if (this.instance) {
return this.instance;
}
this.instance = {
port: parseInt(process.env.PORT || '3000'),
database: {
url: this.requireEnv('DATABASE_URL'),
maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS || '10')
},
redis: {
url: this.requireEnv('REDIS_URL')
},
jwt: {
secret: this.requireEnv('JWT_SECRET'),
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
},
email: {
host: this.requireEnv('EMAIL_HOST'),
port: parseInt(process.env.EMAIL_PORT || '587'),
username: this.requireEnv('EMAIL_USERNAME'),
password: this.requireEnv('EMAIL_PASSWORD')
}
};
return this.instance;
}
private static requireEnv(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(`Required environment variable ${key} is not set`);
}
return value;
}
}
Testing Patterns
Mock Factories
// Mock data factories
export class UserFactory {
static create(overrides: Partial<User> = {}): User {
return {
id: '1',
email: 'test@example.com',
name: 'Test User',
createdAt: new Date(),
...overrides
};
}
static createMany(count: number, overrides: Partial<User> = {}): User[] {
return Array.from({ length: count }, (_, i) =>
this.create({ id: (i + 1).toString(), ...overrides })
);
}
}
// Provider mocks
export class MockUserProvider implements UserProvider {
private users: User[] = [];
async getUserById(id: string): Promise<User | null> {
return this.users.find(user => user.id === id) || null;
}
async createUser(userData: CreateUserData): Promise<User> {
const user = UserFactory.create({
id: (this.users.length + 1).toString(),
...userData
});
this.users.push(user);
return user;
}
// Test helpers
addUser(user: User): void {
this.users.push(user);
}
clear(): void {
this.users = [];
}
}
Integration Test Helpers
export class TestHelper {
static async createTestDatabase(): Promise<Database> {
// Create test database connection
// Run migrations
// Return database instance
}
static async cleanupDatabase(db: Database): Promise<void> {
// Clean up test data
// Reset sequences
}
static async createTestUser(db: Database): Promise<User> {
// Create test user in database
// Return user instance
}
}
// Test setup
describe('UserManager Integration', () => {
let database: Database;
let userManager: UserManager;
beforeEach(async () => {
database = await TestHelper.createTestDatabase();
const userProvider = new DatabaseUserProvider(database);
userManager = new UserManager(userProvider);
});
afterEach(async () => {
await TestHelper.cleanupDatabase(database);
});
it('should create user in database', async () => {
const userData = { email: 'test@example.com', name: 'Test User' };
const result = await userManager.createUser(userData);
expect(result.success).toBe(true);
expect(result.data.email).toBe(userData.email);
});
});