Files
brazil-swift-ops/apps/api/src/index.ts
defiQUG f213aac927 Implement Phase 2: Monitoring, Error Handling, and UI Components
Phase 2 - Monitoring & Observability:
- Create metrics collection system with counters, gauges, and histograms
- Add Prometheus-compatible /metrics endpoint
- Implement request/response metrics tracking
- Database and process metrics monitoring

Phase 2 - Enhanced Error Handling:
- Circuit breaker pattern for service resilience
- Retry mechanism with exponential backoff
- Comprehensive error handler middleware
- Async error wrapper for route handlers
- Request timeout middleware

Phase 3 - UI Components:
- SkeletonLoader components for better loading states
- EmptyState component with helpful messages
- ErrorState component with retry functionality
- Enhanced DataTable with sorting, filtering, pagination

All components are production-ready and integrated.
2026-01-23 18:58:06 -08:00

151 lines
4.4 KiB
TypeScript

/**
* REST API Server
* Provides RESTful API for Brazil SWIFT Operations Platform
*/
import 'dotenv/config';
import express, { Express, Request, Response, NextFunction } from 'express';
import cors from 'cors';
import { getLogger } from '@brazil-swift-ops/utils';
import { evaluateTransaction } from '@brazil-swift-ops/rules-engine';
import type { Transaction } from '@brazil-swift-ops/types';
import { initializeDatabase, closeDatabase } from './db/connection';
import { runMigrations } from './db/migrate';
import { seedDatabase } from './db/seed';
import authRoutes from './routes/auth';
const app: Express = express();
const logger = getLogger();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
app.use((req: Request, res: Response, next: NextFunction) => {
const correlationId = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
logger.setCorrelationId(correlationId);
req.headers['x-correlation-id'] = correlationId;
next();
});
// Health checks
import { healthCheckHandler, readinessCheckHandler, livenessCheckHandler } from './health';
app.get('/health', healthCheckHandler);
app.get('/health/ready', readinessCheckHandler);
app.get('/health/live', livenessCheckHandler);
// Import all route handlers
import transactionRoutes from './routes/transactions';
import accountRoutes from './routes/accounts';
import userRoutes from './routes/users';
import complianceRoutes from './routes/compliance';
import reportsRoutes from './routes/reports';
import fxContractRoutes from './routes/fx-contracts';
import metricsRoutes from './routes/metrics';
import { errorHandler } from './middleware/errorHandler';
import { incrementCounter, recordHistogram } from './monitoring/metrics';
// Request metrics middleware
app.use((req: Request, res: Response, next: NextFunction) => {
const startTime = Date.now();
incrementCounter('http_requests_total', { method: req.method, path: req.path });
res.on('finish', () => {
const duration = Date.now() - startTime;
recordHistogram('http_request_duration_ms', duration, {
method: req.method,
status: res.statusCode.toString(),
});
incrementCounter('http_responses_total', {
method: req.method,
status: res.statusCode.toString(),
});
});
next();
});
// Register routes
app.use('/api/v1/auth', authRoutes);
app.use('/api/v1/transactions', transactionRoutes);
app.use('/api/v1/accounts', accountRoutes);
app.use('/api/v1/users', userRoutes);
app.use('/api/v1/compliance', complianceRoutes);
app.use('/api/v1/reports', reportsRoutes);
app.use('/api/v1/fx-contracts', fxContractRoutes);
app.use('/metrics', metricsRoutes);
// Legacy evaluate transaction endpoint
app.post('/api/v1/transactions/evaluate', async (req: Request, res: Response) => {
try {
const transaction = req.body as Transaction;
const result = evaluateTransaction(transaction);
logger.info('Transaction evaluated', { transactionId: transaction.id });
res.json({
success: true,
data: result,
});
} catch (error) {
logger.error('Error evaluating transaction', error as Error);
res.status(500).json({
success: false,
error: 'Failed to evaluate transaction',
});
}
});
// Error handler (must be last)
app.use(errorHandler);
// Initialize database and start server
let server: any;
async function startServer() {
try {
// Initialize database
await initializeDatabase();
console.log('✓ Database initialized');
// Run migrations
await runMigrations();
console.log('✓ Migrations completed');
// Seed database with initial data
await seedDatabase();
console.log('✓ Database seeded');
// Start server
server = app.listen(PORT, () => {
logger.info(`API server started on port ${PORT}`);
});
// Handle graceful shutdown
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
} catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
}
async function shutdown() {
console.log('\nShutting down gracefully...');
if (server) {
server.close(async () => {
await closeDatabase();
console.log('Server closed');
process.exit(0);
});
}
}
// Start server (only if running directly, not when imported)
if (typeof require !== 'undefined' && require.main === module) {
startServer();
}
export default app;