**refactor: add async error handler utility and standardize route handlers**

### Changes:
- Introduced `asyncHandler` utility to simplify error handling in asynchronous route handlers.
- Updated all route definitions in `cloud-wrapper/src/routes.ts` to use the new `asyncHandler` wrapper:
  - Standardized error management across CRUD operations for nouns, verbs, and search functionality.
  - Replaced redundant `try-catch` blocks with the `asyncHandler` wrapper to streamline code.
- Added `NextFunction` and adjusted TypeScript typings for improved clarity and error handling.

### Purpose:
Refactored route handlers to enhance maintainability, readability, and consistency by centralizing asynchronous error handling with a reusable `asyncHandler` utility. This reduces redundancy and simplifies debugging for API routes.
This commit is contained in:
David Snelling 2025-06-19 16:03:04 -07:00
parent b62e952655
commit 1b78e5425a

View file

@ -1,4 +1,4 @@
import { Router, Request, Response } from 'express'; import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
import { BrainyData, NounType, VerbType } from '@soulcraft/brainy'; import { BrainyData, NounType, VerbType } from '@soulcraft/brainy';
// Define a custom request type that includes the Brainy instance // Define a custom request type that includes the Brainy instance
@ -6,11 +6,17 @@ interface BrainyRequest extends Request {
brainy: BrainyData; brainy: BrainyData;
} }
// Helper function to handle async route handlers
const asyncHandler = (fn: (req: BrainyRequest, res: Response, next: NextFunction) => Promise<any>) =>
(req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req as BrainyRequest, res, next)).catch(next);
};
export function setupRoutes() { export function setupRoutes() {
const router = Router(); const router = Router();
// Get database status // Get database status
router.get('/status', async (req: BrainyRequest, res: Response) => { router.get('/status', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const status = await req.brainy.status(); const status = await req.brainy.status();
res.status(200).json(status); res.status(200).json(status);
@ -18,10 +24,10 @@ export function setupRoutes() {
console.error('Error getting status:', error); console.error('Error getting status:', error);
res.status(500).json({ error: 'Failed to get database status' }); res.status(500).json({ error: 'Failed to get database status' });
} }
}); }));
// Add a noun (entity) // Add a noun (entity)
router.post('/nouns', async (req: BrainyRequest, res: Response) => { router.post('/nouns', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { text, metadata } = req.body; const { text, metadata } = req.body;
@ -35,10 +41,10 @@ export function setupRoutes() {
console.error('Error adding noun:', error); console.error('Error adding noun:', error);
res.status(500).json({ error: 'Failed to add noun' }); res.status(500).json({ error: 'Failed to add noun' });
} }
}); }));
// Get a noun by ID // Get a noun by ID
router.get('/nouns/:id', async (req: BrainyRequest, res: Response) => { router.get('/nouns/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
const noun = await req.brainy.get(id); const noun = await req.brainy.get(id);
@ -52,10 +58,10 @@ export function setupRoutes() {
console.error('Error getting noun:', error); console.error('Error getting noun:', error);
res.status(500).json({ error: 'Failed to get noun' }); res.status(500).json({ error: 'Failed to get noun' });
} }
}); }));
// Update noun metadata // Update noun metadata
router.put('/nouns/:id', async (req: BrainyRequest, res: Response) => { router.put('/nouns/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
const { metadata } = req.body; const { metadata } = req.body;
@ -70,10 +76,10 @@ export function setupRoutes() {
console.error('Error updating noun:', error); console.error('Error updating noun:', error);
res.status(500).json({ error: 'Failed to update noun' }); res.status(500).json({ error: 'Failed to update noun' });
} }
}); }));
// Delete a noun // Delete a noun
router.delete('/nouns/:id', async (req: BrainyRequest, res: Response) => { router.delete('/nouns/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
await req.brainy.delete(id); await req.brainy.delete(id);
@ -82,10 +88,10 @@ export function setupRoutes() {
console.error('Error deleting noun:', error); console.error('Error deleting noun:', error);
res.status(500).json({ error: 'Failed to delete noun' }); res.status(500).json({ error: 'Failed to delete noun' });
} }
}); }));
// Search for similar nouns // Search for similar nouns
router.post('/search', async (req: BrainyRequest, res: Response) => { router.post('/search', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { query, limit = 10 } = req.body; const { query, limit = 10 } = req.body;
@ -99,10 +105,10 @@ export function setupRoutes() {
console.error('Error searching:', error); console.error('Error searching:', error);
res.status(500).json({ error: 'Failed to search' }); res.status(500).json({ error: 'Failed to search' });
} }
}); }));
// Add a verb (relationship) // Add a verb (relationship)
router.post('/verbs', async (req: BrainyRequest, res: Response) => { router.post('/verbs', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { sourceId, targetId, metadata } = req.body; const { sourceId, targetId, metadata } = req.body;
@ -116,10 +122,10 @@ export function setupRoutes() {
console.error('Error adding verb:', error); console.error('Error adding verb:', error);
res.status(500).json({ error: 'Failed to add verb' }); res.status(500).json({ error: 'Failed to add verb' });
} }
}); }));
// Get all verbs // Get all verbs
router.get('/verbs', async (req: BrainyRequest, res: Response) => { router.get('/verbs', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const verbs = await req.brainy.getAllVerbs(); const verbs = await req.brainy.getAllVerbs();
res.status(200).json(verbs); res.status(200).json(verbs);
@ -127,10 +133,10 @@ export function setupRoutes() {
console.error('Error getting verbs:', error); console.error('Error getting verbs:', error);
res.status(500).json({ error: 'Failed to get verbs' }); res.status(500).json({ error: 'Failed to get verbs' });
} }
}); }));
// Get verbs by source // Get verbs by source
router.get('/verbs/source/:id', async (req: BrainyRequest, res: Response) => { router.get('/verbs/source/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
const verbs = await req.brainy.getVerbsBySource(id); const verbs = await req.brainy.getVerbsBySource(id);
@ -139,10 +145,10 @@ export function setupRoutes() {
console.error('Error getting verbs by source:', error); console.error('Error getting verbs by source:', error);
res.status(500).json({ error: 'Failed to get verbs by source' }); res.status(500).json({ error: 'Failed to get verbs by source' });
} }
}); }));
// Get verbs by target // Get verbs by target
router.get('/verbs/target/:id', async (req: BrainyRequest, res: Response) => { router.get('/verbs/target/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
const verbs = await req.brainy.getVerbsByTarget(id); const verbs = await req.brainy.getVerbsByTarget(id);
@ -151,10 +157,10 @@ export function setupRoutes() {
console.error('Error getting verbs by target:', error); console.error('Error getting verbs by target:', error);
res.status(500).json({ error: 'Failed to get verbs by target' }); res.status(500).json({ error: 'Failed to get verbs by target' });
} }
}); }));
// Delete a verb // Delete a verb
router.delete('/verbs/:id', async (req: BrainyRequest, res: Response) => { router.delete('/verbs/:id', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
const { id } = req.params; const { id } = req.params;
await req.brainy.deleteVerb(id); await req.brainy.deleteVerb(id);
@ -163,10 +169,10 @@ export function setupRoutes() {
console.error('Error deleting verb:', error); console.error('Error deleting verb:', error);
res.status(500).json({ error: 'Failed to delete verb' }); res.status(500).json({ error: 'Failed to delete verb' });
} }
}); }));
// Clear all data // Clear all data
router.delete('/clear', async (req: BrainyRequest, res: Response) => { router.delete('/clear', asyncHandler(async (req: BrainyRequest, res: Response, next: NextFunction) => {
try { try {
await req.brainy.clear(); await req.brainy.clear();
res.status(200).json({ success: true }); res.status(200).json({ success: true });
@ -174,7 +180,7 @@ export function setupRoutes() {
console.error('Error clearing data:', error); console.error('Error clearing data:', error);
res.status(500).json({ error: 'Failed to clear data' }); res.status(500).json({ error: 'Failed to clear data' });
} }
}); }));
return router; return router;
} }