# TypeScript Patterns for Multiverse Campus ## Express Route Pattern (with auth) ```typescript import { Router } from 'express'; import { requireAuth, requireAdmin } from '../middleware/auth'; const router = Router(); // All routes require auth unless explicitly public router.use(requireAuth); router.get('/resource', async (req, res) => { try { const result = await service.getResource(req.user.studentId); res.json({ success: true, data: result }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // Admin routes router.put('/resource/:id', requireAdmin, async (req, res) => { ... }); ``` ## Zustand Store Pattern ```typescript import { create } from 'zustand'; interface ResourceStore { items: Resource[]; loading: boolean; error: string | null; fetch: () => Promise; update: (id: string, data: Partial) => void; } export const useResourceStore = create((set, get) => ({ items: [], loading: false, error: null, fetch: async () => { set({ loading: true, error: null }); try { const res = await api.get('/resource'); set({ items: res.data, loading: false }); } catch (err) { set({ error: err.message, loading: false }); } }, update: (id, data) => { set(state => ({ items: state.items.map(item => item.id === id ? { ...item, ...data } : item ), })); }, })); ``` ## Socket.IO Handler Pattern (server, PM2 cluster-safe) ```typescript export function registerHandlers(io: Server, socket: AuthenticatedSocket) { socket.on('resource:action', async (payload, callback) => { try { // Validate payload const { resourceId } = validatePayload(payload); // Perform action const result = await service.performAction(socket.userId, resourceId); // Broadcast to room (Redis adapter handles cross-process) io.to(`resource:${resourceId}`).emit('resource:updated', result); // Acknowledge to sender callback({ success: true, data: result }); } catch (err) { callback({ success: false, error: err.message }); } }); } ``` ## Database Transaction Pattern (pg-boss safe) ```typescript import { getPool } from '../db'; async function transferGems(fromId: string, toId: string, amount: number) { const pool = getPool(); const client = await pool.connect(); try { await client.query('BEGIN'); // Debit const debit = await client.query( 'UPDATE students SET gems = gems - $1 WHERE id = $2 AND gems >= $1 RETURNING gems', [amount, fromId] ); if (debit.rowCount === 0) throw new Error('Insufficient gems'); // Credit await client.query( 'UPDATE students SET gems = gems + $1 WHERE id = $2', [amount, toId] ); await client.query('COMMIT'); } catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); } } ``` ## Migration Pattern (additive only) ```typescript // migrations/YYYYMMDDHHMMSS_add_feature_column.ts import { Knex } from 'knex'; export async function up(knex: Knex): Promise { await knex.schema.alterTable('students', table => { table.timestamp('last_login_at').nullable().defaultTo(null); table.index('last_login_at'); // Only if frequently queried }); } export async function down(knex: Knex): Promise { await knex.schema.alterTable('students', table => { table.dropColumn('last_login_at'); }); } // NOTE: down() exists for dev rollback only. // In production, prefer a NEW migration that deprecates rather than drops. ``` ## Error Handling Pattern ```typescript // Custom error classes for typed error handling class AppError extends Error { constructor( message: string, public statusCode: number = 500, public code: string = 'INTERNAL_ERROR' ) { super(message); } } class NotFoundError extends AppError { constructor(resource: string, id: string) { super(`${resource} ${id} not found`, 404, 'NOT_FOUND'); } } class AuthorizationError extends AppError { constructor(action: string) { super(`Not authorized to ${action}`, 403, 'FORBIDDEN'); } } // Express error middleware function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) { if (err instanceof AppError) { return res.status(err.statusCode).json({ success: false, error: { code: err.code, message: err.message }, }); } // Unknown errors — log and return generic console.error('Unhandled error:', err); res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' } }); } ```