feat: add Drizzle enums for schema
Made-with: Cursor
This commit is contained in:
76
src/app.ts
Normal file
76
src/app.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import Fastify, { FastifyInstance } from 'fastify';
|
||||
import { AppError } from './utils/errors.js';
|
||||
import databasePlugin from './plugins/database.js';
|
||||
import redisPlugin from './plugins/redis.js';
|
||||
import securityPlugin from './plugins/security.js';
|
||||
import rateLimitPlugin from './plugins/rateLimit.js';
|
||||
import { env } from './config/env.js';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
export async function buildApp(): Promise<FastifyInstance> {
|
||||
const isDev = env.NODE_ENV === 'development';
|
||||
|
||||
const app = Fastify({
|
||||
logger: {
|
||||
level: isDev ? 'debug' : 'info',
|
||||
transport:
|
||||
isDev
|
||||
? {
|
||||
target: 'pino-pretty',
|
||||
options: {
|
||||
translateTime: 'HH:MM:ss Z',
|
||||
ignore: 'pid,hostname',
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
requestIdHeader: 'x-request-id',
|
||||
requestIdLogLabel: 'requestId',
|
||||
genReqId: () => randomUUID(),
|
||||
});
|
||||
|
||||
app.setErrorHandler((err: unknown, request, reply) => {
|
||||
const error = err as Error & { statusCode?: number; validation?: unknown };
|
||||
request.log.error({ err }, error.message);
|
||||
|
||||
if (err instanceof AppError) {
|
||||
const statusCode = err.statusCode;
|
||||
const payload = err.toJSON();
|
||||
if (err.code === 'RATE_LIMIT_EXCEEDED' && 'retryAfter' in err) {
|
||||
reply.header('Retry-After', String((err as AppError & { retryAfter?: number }).retryAfter ?? 60));
|
||||
}
|
||||
return reply.status(statusCode).send(payload);
|
||||
}
|
||||
|
||||
if (error.validation) {
|
||||
return reply.status(422).send({
|
||||
error: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
details: error.validation,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const statusCode = error.statusCode ?? 500;
|
||||
return reply.status(statusCode).send({
|
||||
error: {
|
||||
code: 'INTERNAL_ERROR',
|
||||
message: env.NODE_ENV === 'production' ? 'Internal server error' : error.message,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
app.addHook('onRequest', async (request, reply) => {
|
||||
reply.header('x-request-id', request.id);
|
||||
});
|
||||
|
||||
await app.register(redisPlugin);
|
||||
await app.register(databasePlugin);
|
||||
await app.register(securityPlugin);
|
||||
await app.register(rateLimitPlugin);
|
||||
|
||||
app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
|
||||
|
||||
return app;
|
||||
}
|
||||
Reference in New Issue
Block a user