feat(backend): bootstrap express app with error handling
This commit is contained in:
33
packages/backend/src/app.ts
Normal file
33
packages/backend/src/app.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import express, { type Express, type NextFunction, type Request, type Response } from 'express';
|
||||
import { ZodError } from 'zod';
|
||||
import { ApiError } from './lib/errors.js';
|
||||
|
||||
export function createApp(): Express {
|
||||
const app = express();
|
||||
app.use(express.json({ limit: '5mb' }));
|
||||
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// Routes mounted in later tasks.
|
||||
|
||||
app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => {
|
||||
if (err instanceof ZodError) {
|
||||
res.status(400).json({
|
||||
error: { code: 'VALIDATION_ERROR', message: 'Invalid input', details: err.flatten() },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (err instanceof ApiError) {
|
||||
res.status(err.status).json({
|
||||
error: { code: err.code, message: err.message, details: err.details },
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.error(err);
|
||||
res.status(500).json({ error: { code: 'INTERNAL', message: 'Internal server error' } });
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
7
packages/backend/src/index.ts
Normal file
7
packages/backend/src/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createApp } from './app.js';
|
||||
|
||||
const PORT = Number(process.env.PORT ?? 3000);
|
||||
const app = createApp();
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Backend listening on http://localhost:${PORT}`);
|
||||
});
|
||||
20
packages/backend/src/lib/errors.ts
Normal file
20
packages/backend/src/lib/errors.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
public status: number,
|
||||
public code: string,
|
||||
message: string,
|
||||
public details?: unknown
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
static notFound(what = 'Resource') {
|
||||
return new ApiError(404, 'NOT_FOUND', `${what} not found`);
|
||||
}
|
||||
static validation(message: string, details?: unknown) {
|
||||
return new ApiError(400, 'VALIDATION_ERROR', message, details);
|
||||
}
|
||||
static conflict(message: string) {
|
||||
return new ApiError(409, 'CONFLICT', message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user