Skip to content

Commit 0cbcfd6

Browse files
committed
add error handling and graceful shutdown
1 parent 3fc7228 commit 0cbcfd6

File tree

1 file changed

+60
-2
lines changed

1 file changed

+60
-2
lines changed

apps/server/src/index.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { parse } from 'node:url';
21
import { Connect, Identity, Inboxes, Messages, SpaceEvents, Utils } from '@graphprotocol/hypergraph';
32
import { bytesToHex, randomBytes } from '@noble/hashes/utils.js';
43
import cors from 'cors';
54
import { Effect, Exit, Schema } from 'effect';
6-
import express, { type Request, type Response } from 'express';
5+
import express, { type NextFunction, type Request, type Response } from 'express';
6+
import { parse } from 'node:url';
77
import WebSocket, { WebSocketServer } from 'ws';
88
import { addAppIdentityToSpaces } from './handlers/add-app-identity-to-spaces.js';
99
import { applySpaceEvent } from './handlers/applySpaceEvent.js';
@@ -68,6 +68,28 @@ app.use(express.json({ limit: '2mb' }));
6868

6969
app.use(cors());
7070

71+
// Request timeout middleware
72+
app.use((req: Request, res: Response, next: NextFunction) => {
73+
req.setTimeout(30000, () => {
74+
res.status(408).json({ error: 'Request timeout' });
75+
});
76+
next();
77+
});
78+
79+
// Global error handling middleware
80+
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
81+
console.error('Unhandled error:', error);
82+
res.status(500).json({
83+
error: 'Internal server error',
84+
message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong',
85+
});
86+
});
87+
88+
// 404 handler
89+
app.use('*', (req: Request, res: Response) => {
90+
res.status(404).json({ error: 'Route not found' });
91+
});
92+
7193
app.get('/', (_req, res) => {
7294
res.send('Server is running (v0.0.10)');
7395
});
@@ -557,6 +579,42 @@ const server = app.listen(PORT, () => {
557579
console.log(`Listening on port ${PORT}`);
558580
});
559581

582+
// Global process error handlers
583+
process.on('unhandledRejection', (reason, promise) => {
584+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
585+
});
586+
587+
process.on('uncaughtException', (error) => {
588+
console.error('Uncaught Exception:', error);
589+
// Graceful shutdown
590+
server.close(() => {
591+
console.log('Server closed due to uncaught exception');
592+
process.exit(1);
593+
});
594+
});
595+
596+
// Graceful shutdown handlers
597+
const gracefulShutdown = (signal: string) => {
598+
console.log(`Received ${signal}. Starting graceful shutdown...`);
599+
600+
server.close(() => {
601+
console.log('HTTP server closed');
602+
webSocketServer.close(() => {
603+
console.log('WebSocket server closed');
604+
process.exit(0);
605+
});
606+
});
607+
608+
// Force close after 30 seconds
609+
setTimeout(() => {
610+
console.error('Could not close connections in time, forcefully shutting down');
611+
process.exit(1);
612+
}, 30000);
613+
};
614+
615+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
616+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
617+
560618
function broadcastSpaceEvents({
561619
spaceId,
562620
event,

0 commit comments

Comments
 (0)