diff --git a/apps/api/.env.example b/apps/api/.env.example index 8789db7..5e4cc4a 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -6,5 +6,4 @@ LOG_LEVEL=info # CORS — origini consentite (separate da virgola) CORS_ORIGINS=http://localhost:3000 -# Database (configurato in step successivo con Prisma) -# DATABASE_URL=postgresql://user:password@localhost:5432/ketopath +DATABASE_URL=postgresql://luciano:luciano%40@localhost:5432/ketopath?schema=public diff --git a/apps/api/package.json b/apps/api/package.json index fb34133..acb69d1 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "dev": "tsx watch src/server.ts", + "dev": "tsx watch --env-file=.env src/server.ts", "build": "tsc", "start": "node dist/server.js", "typecheck": "tsc --noEmit", @@ -15,8 +15,10 @@ "@fastify/helmet": "^11.1.1", "@fastify/rate-limit": "^9.1.0", "@fastify/sensible": "^5.6.0", + "@ketopath/db": "workspace:*", "@ketopath/shared": "workspace:*", "fastify": "^4.28.1", + "fastify-plugin": "^4.5.1", "zod": "^3.23.8" }, "devDependencies": { diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 61dd6f5..adbea94 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -5,7 +5,9 @@ import sensible from '@fastify/sensible'; import Fastify, { type FastifyInstance } from 'fastify'; import { env } from './config/env.js'; +import { dbRoutes } from './modules/db/db.routes.js'; import { healthRoutes } from './modules/health/health.routes.js'; +import { prismaPlugin } from './plugins/prisma.js'; export async function buildApp(): Promise { const app = Fastify({ @@ -33,8 +35,10 @@ export async function buildApp(): Promise { timeWindow: '1 minute', }); await app.register(sensible); + await app.register(prismaPlugin); await app.register(healthRoutes); + await app.register(dbRoutes); return app; } diff --git a/apps/api/src/config/env.ts b/apps/api/src/config/env.ts index f496234..7abd932 100644 --- a/apps/api/src/config/env.ts +++ b/apps/api/src/config/env.ts @@ -14,6 +14,7 @@ const envSchema = z.object({ .map((origin) => origin.trim()) .filter(Boolean), ), + DATABASE_URL: z.string().url(), }); export type Env = z.infer; diff --git a/apps/api/src/modules/db/db.routes.ts b/apps/api/src/modules/db/db.routes.ts new file mode 100644 index 0000000..12b33ca --- /dev/null +++ b/apps/api/src/modules/db/db.routes.ts @@ -0,0 +1,13 @@ +import type { FastifyPluginAsync } from 'fastify'; + +export const dbRoutes: FastifyPluginAsync = async (fastify) => { + fastify.get('/db/health', async (_request, reply) => { + try { + await fastify.prisma.$queryRaw`SELECT 1`; + return { status: 'ok', database: 'reachable' }; + } catch (err) { + fastify.log.error({ err }, 'database health check failed'); + return reply.code(503).send({ status: 'error', database: 'unreachable' }); + } + }); +}; diff --git a/apps/api/src/plugins/prisma.ts b/apps/api/src/plugins/prisma.ts new file mode 100644 index 0000000..fb0c190 --- /dev/null +++ b/apps/api/src/plugins/prisma.ts @@ -0,0 +1,11 @@ +import { prisma } from '@ketopath/db'; +import fp from 'fastify-plugin'; + +export const prismaPlugin = fp(async (app) => { + await prisma.$connect(); + app.decorate('prisma', prisma); + + app.addHook('onClose', async () => { + await prisma.$disconnect(); + }); +}); diff --git a/apps/api/src/types/fastify.d.ts b/apps/api/src/types/fastify.d.ts new file mode 100644 index 0000000..85efb5a --- /dev/null +++ b/apps/api/src/types/fastify.d.ts @@ -0,0 +1,7 @@ +import type { prisma } from '@ketopath/db'; + +declare module 'fastify' { + interface FastifyInstance { + prisma: typeof prisma; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6046057..116cec0 100644 Binary files a/pnpm-lock.yaml and b/pnpm-lock.yaml differ