# Code Patterns Common code patterns extracted from Architecture Decision Records (ADRs). Use these as templates when writing new code. ## Table of Contents - [Error Handling](#error-handling) - [Repository Patterns](#repository-patterns) - [API Response Patterns](#api-response-patterns) - [Transaction Management](#transaction-management) - [Input Validation](#input-validation) - [Authentication](#authentication) - [Caching](#caching) - [Background Jobs](#background-jobs) --- ## Error Handling **ADR**: [ADR-001](../adr/0001-standardized-error-handling-for-database-operations.md) ### Repository Layer Error Handling ```typescript import { handleDbError, NotFoundError } from '../services/db/errors.db'; import { PoolClient } from 'pg'; export async function getFlyerById(id: number, client?: PoolClient): Promise { const db = client || pool; try { const result = await db.query('SELECT * FROM flyers WHERE id = $1', [id]); if (result.rows.length === 0) { throw new NotFoundError('Flyer', id); } return result.rows[0]; } catch (error) { throw handleDbError(error); } } ``` ### Route Layer Error Handling ```typescript import { sendError } from '../utils/apiResponse'; app.get('/api/flyers/:id', async (req, res) => { try { const flyer = await flyerDb.getFlyerById(parseInt(req.params.id)); return sendSuccess(res, flyer); } catch (error) { return sendError(res, error); } }); ``` ### Custom Error Types ```typescript // NotFoundError - Entity not found throw new NotFoundError('Flyer', id); // ValidationError - Invalid input throw new ValidationError('Invalid email format'); // DatabaseError - Database operation failed throw new DatabaseError('Failed to insert flyer', originalError); ``` --- ## Repository Patterns **ADR**: [ADR-034](../adr/0034-repository-layer-method-naming-conventions.md) ### Method Naming Conventions | Prefix | Returns | Not Found Behavior | Use Case | | ------- | -------------- | -------------------- | ------------------------- | | `get*` | Entity | Throws NotFoundError | When entity must exist | | `find*` | Entity \| null | Returns null | When entity may not exist | | `list*` | Array | Returns [] | When returning multiple | ### Get Method (Must Exist) ```typescript /** * Get a flyer by ID. Throws NotFoundError if not found. */ export async function getFlyerById(id: number, client?: PoolClient): Promise { const db = client || pool; try { const result = await db.query('SELECT * FROM flyers WHERE id = $1', [id]); if (result.rows.length === 0) { throw new NotFoundError('Flyer', id); } return result.rows[0]; } catch (error) { throw handleDbError(error); } } ``` ### Find Method (May Not Exist) ```typescript /** * Find a flyer by ID. Returns null if not found. */ export async function findFlyerById(id: number, client?: PoolClient): Promise { const db = client || pool; try { const result = await db.query('SELECT * FROM flyers WHERE id = $1', [id]); return result.rows[0] || null; } catch (error) { throw handleDbError(error); } } ``` ### List Method (Multiple Results) ```typescript /** * List all active flyers. Returns empty array if none found. */ export async function listActiveFlyers(client?: PoolClient): Promise { const db = client || pool; try { const result = await db.query( 'SELECT * FROM flyers WHERE end_date >= CURRENT_DATE ORDER BY start_date DESC', ); return result.rows; } catch (error) { throw handleDbError(error); } } ``` --- ## API Response Patterns **ADR**: [ADR-028](../adr/0028-consistent-api-response-format.md) ### Success Response ```typescript import { sendSuccess } from '../utils/apiResponse'; app.post('/api/flyers', async (req, res) => { const flyer = await flyerService.createFlyer(req.body); return sendSuccess(res, flyer, 'Flyer created successfully', 201); }); ``` ### Paginated Response ```typescript import { sendPaginated } from '../utils/apiResponse'; app.get('/api/flyers', async (req, res) => { const { page = 1, pageSize = 20 } = req.query; const { items, total } = await flyerService.listFlyers(page, pageSize); return sendPaginated(res, { items, total, page: parseInt(page), pageSize: parseInt(pageSize), }); }); ``` ### Error Response ```typescript import { sendError } from '../utils/apiResponse'; app.get('/api/flyers/:id', async (req, res) => { try { const flyer = await flyerDb.getFlyerById(parseInt(req.params.id)); return sendSuccess(res, flyer); } catch (error) { return sendError(res, error); // Automatically maps error to correct status } }); ``` --- ## Transaction Management **ADR**: [ADR-002](../adr/0002-transaction-management-pattern.md) ### Basic Transaction ```typescript import { withTransaction } from '../services/db/transaction.db'; export async function createFlyerWithItems( flyerData: FlyerInput, items: FlyerItemInput[], ): Promise { return withTransaction(async (client) => { // Create flyer const flyer = await flyerDb.createFlyer(flyerData, client); // Create items const createdItems = await flyerItemDb.createItems( items.map((item) => ({ ...item, flyer_id: flyer.id })), client, ); // Automatically commits on success, rolls back on error return { ...flyer, items: createdItems }; }); } ``` ### Nested Transactions ```typescript export async function bulkImportFlyers(flyersData: FlyerInput[]): Promise { return withTransaction(async (client) => { const results = []; for (const flyerData of flyersData) { try { // Each flyer import is atomic const flyer = await createFlyerWithItems( flyerData, flyerData.items, client, // Pass transaction client ); results.push({ success: true, flyer }); } catch (error) { results.push({ success: false, error: error.message }); } } return results; }); } ``` --- ## Input Validation **ADR**: [ADR-003](../adr/0003-input-validation-framework.md) ### Zod Schema Definition ```typescript // src/schemas/flyer.schemas.ts import { z } from 'zod'; export const createFlyerSchema = z.object({ store_id: z.number().int().positive(), image_url: z .string() .url() .regex(/^https?:\/\/.*/), start_date: z.string().datetime(), end_date: z.string().datetime(), items: z .array( z.object({ name: z.string().min(1).max(255), price: z.number().positive(), quantity: z.string().optional(), }), ) .min(1), }); export type CreateFlyerInput = z.infer; ``` ### Route Validation Middleware ```typescript import { validateRequest } from '../middleware/validation'; import { createFlyerSchema } from '../schemas/flyer.schemas'; app.post('/api/flyers', validateRequest(createFlyerSchema), async (req, res) => { // req.body is now type-safe and validated const flyer = await flyerService.createFlyer(req.body); return sendSuccess(res, flyer, 'Flyer created successfully', 201); }); ``` ### Manual Validation ```typescript import { createFlyerSchema } from '../schemas/flyer.schemas'; export async function processFlyer(data: unknown): Promise { // Validate and parse input const validated = createFlyerSchema.parse(data); // Type-safe from here on return flyerDb.createFlyer(validated); } ``` --- ## Authentication **ADR**: [ADR-048](../adr/0048-authentication-strategy.md) ### Protected Route with JWT ```typescript import { authenticateJWT } from '../middleware/auth'; app.get( '/api/profile', authenticateJWT, // Middleware adds req.user async (req, res) => { // req.user is guaranteed to exist const user = await userDb.getUserById(req.user.id); return sendSuccess(res, user); }, ); ``` ### Optional Authentication ```typescript import { optionalAuth } from '../middleware/auth'; app.get( '/api/flyers', optionalAuth, // req.user may or may not exist async (req, res) => { const flyers = req.user ? await flyerDb.listFlyersForUser(req.user.id) : await flyerDb.listPublicFlyers(); return sendSuccess(res, flyers); }, ); ``` ### Generate JWT Token ```typescript import jwt from 'jsonwebtoken'; import { env } from '../config/env'; export function generateToken(user: User): string { return jwt.sign({ id: user.id, email: user.email }, env.JWT_SECRET, { expiresIn: '7d' }); } ``` --- ## Caching **ADR**: [ADR-029](../adr/0029-redis-caching-strategy.md) ### Cache Pattern ```typescript import { cacheService } from '../services/cache.server'; export async function getFlyer(id: number): Promise { // Try cache first const cached = await cacheService.get(`flyer:${id}`); if (cached) return cached; // Cache miss - fetch from database const flyer = await flyerDb.getFlyerById(id); // Store in cache (1 hour TTL) await cacheService.set(`flyer:${id}`, flyer, 3600); return flyer; } ``` ### Cache Invalidation ```typescript export async function updateFlyer(id: number, data: UpdateFlyerInput): Promise { const flyer = await flyerDb.updateFlyer(id, data); // Invalidate cache await cacheService.delete(`flyer:${id}`); await cacheService.invalidatePattern('flyers:list:*'); return flyer; } ``` --- ## Background Jobs **ADR**: [ADR-036](../adr/0036-background-job-processing-architecture.md) ### Queue Job ```typescript import { flyerProcessingQueue } from '../services/queues.server'; export async function enqueueFlyerProcessing(flyerId: number): Promise { await flyerProcessingQueue.add( 'process-flyer', { flyerId, timestamp: Date.now(), }, { attempts: 3, backoff: { type: 'exponential', delay: 2000, }, }, ); } ``` ### Process Job ```typescript // src/services/workers.server.ts import { Worker } from 'bullmq'; const flyerWorker = new Worker( 'flyer-processing', async (job) => { const { flyerId } = job.data; try { // Process flyer const result = await aiService.extractFlyerData(flyerId); await flyerDb.updateFlyerWithData(flyerId, result); // Update progress await job.updateProgress(100); return { success: true, itemCount: result.items.length }; } catch (error) { logger.error('Flyer processing failed', { flyerId, error }); throw error; // Will retry automatically } }, { connection: redisConnection, concurrency: 5, }, ); ``` --- ## Related Documentation - [ADR Index](../adr/index.md) - All architecture decision records - [TESTING.md](TESTING.md) - Testing patterns - [DEBUGGING.md](DEBUGGING.md) - Debugging strategies - [Database Guide](../subagents/DATABASE-GUIDE.md) - Database patterns - [Coder Reference](../SUBAGENT-CODER-REFERENCE.md) - Quick reference for AI agents