This repository contains a Fastify v5 + TypeScript API using Zod for validation and Drizzle ORM for PostgreSQL. It includes JWT & cookie based auth, middleware, and route plugins organized under src/routers/.
Badges
- Package:
apiv1.0.0 (see package.json) - Status / CI / Coverage: TODO — add your CI badge URLs
- Quick Start
- Environment
- Project Structure
- Running Locally
- API Reference (auto-inferred)
- Database
- Services / Utilities
- Diagrams (Mermaid)
Prerequisites
- Node.js (recommended: 18+)
- PostgreSQL (can use Docker)
- Recommended dev tools:
npm,npx,tsx
Install
npm installStart DB (using docker-compose provided)
docker compose up -d dbRun in development (example)
# Uses tsx watch in package.json scripts: start:server / dev
npm run devRun tests
# Uses vitest with .env.test loaded via package.json test script
npm testA generated .env.example is provided at the repository root (see .env.example). Detected environment variables (inferred from .env, .env.test, and src/services/env.ts):
| Variable | Description / Usage |
|---|---|
| NODE_ENV | environment (development |
| DATABSE_URL / NODE_ENV_DATABASE / DATABSE_URL (typos detected) | Postgres connection string used by Drizzle |
| NODE_ENV_JWT | JWT environment marker (detected) |
| SALT_ROUNDS | bcrypt salt rounds |
| SECRETET_JWT | secret used to sign JWTs |
| CURRENT_COOKIE_SECRETET | cookie signing secret |
| PREVIOUS_COOKIE_SECRETET_1 | previous cookie secret (rotation) |
| PREVIOUS_COOKIE_SECRETET_2 | previous cookie secret (rotation) |
| ALLOWED_ORIGINS | CORS allowed origins |
| ALLOWED_IP | IP allowlist (heuristic) |
See .env.example for a ready template. Fill secrets before production.
src/
├─ api.ts # exports server (bootstrap)
├─ server.ts # server listen script
├─ @types/
│ └─ fastify.d.ts # Fastify custom typings (request.user, etc.)
├─ db/
│ ├─ client.ts # drizzle client initialization
│ └─ schema.ts # Drizzle table schema (empty / TODO)
├─ routers/ # Route plugins grouped by domain
│ ├─ auth/
│ │ └─ auth.ts # authentication routes (login/register)
│ ├─ courses/
│ │ └─ courses-*.ts # GET/POST/PUT/DELETE routes for courses
│ ├─ teachers/
│ │ └─ teachers-*.ts # teachers routes
│ ├─ usesr/ # NOTE: folder name 'usesr' (typo?) contains users-* files
│ │ └─ users-*.ts
│ └─ hook/
│ ├─ check-request-jwt.ts
│ └─ check-user-role.ts
├─ services/
│ ├─ env.ts # env schema parse (zod)
│ ├─ middleware.ts # global middleware (headers/content-type)
│ ├─ errors.ts # error mapping plugin
│ ├─ utils.ts # helpers (hashPassword, etc.)
│ └─ enrollments.ts # enrollment number generator
Notes
- The
usesrdirectory name appears to be a typo forusers. Consider renaming tousersto avoid confusion. src/db/schema.tsis present but currently empty — see Database.
- Start PostgreSQL
docker compose up -d db-
Create
.envfrom.env.exampleand fill secrets. -
Install dependencies
npm ci- Run migrations (drizzle-kit is configured in package.json)
npm run db:generate # generate migration (if schema present)
npm run db:migrate- Start server
npm run dev
# or
node --env-file .env --watch ./src/server.tsNotes: the following route summary is inferred from filenames and code structure. Please open the route files under src/routers/ to confirm exact paths, parameters and schemas. If a route uses dynamic path or complex plugin registration, mark TODO and update.
Detected routes (summary table)
| Area / File | Method(s) | Path (heuristic) | Auth required? | Zod validation? | Notes |
|---|---|---|---|---|---|
| auth/auth.ts | POST | /auth | possibly public | likely Zod (auth payload) | Handles login/register — sets cookie / returns token (heuristic) |
| courses/courses-get.ts | GET | /courses (list) | public | likely query schema | List courses, supports search / pagination |
| courses/courses-post.ts | POST | /courses | protected | likely Zod body | Create course (protected) — sets 201 |
| courses/courses-put.ts | PUT | /courses/:id | protected | likely Zod params+body | Update course |
| courses/courses-delete.ts | DELETE | /courses/:id | protected | Delete course | |
| teachers/* | GET/POST/PUT/DELETE | /teachers | varies | likely Zod | CRUD for teachers |
| usesr/users-post.ts | POST | /users (register) | public | likely Zod body | NOTE: folder name 'usesr' |
| usesr/users-get.ts | GET | /users | protected? | list users | |
| usesr/users-put.ts | PUT | /users/:id | protected | update user | |
| usesr/users-delete.ts | DELETE | /users/:id | protected | delete user |
Security & auth heuristics:
- Files in
routers/hook/includecheck-request-jwt.tsandcheck-user-role.ts. These indicate JWT-based auth middleware and role enforcement. - Custom Fastify types in
src/@types/fastify.d.tsincludeFastifyRequest.userandFastifyInstance.authenticate(decorator) — the server likely decorates anauthenticatepreHandler.
Auth (login) — example (heuristic endpoints: adapt to actual route)
curl -X POST "http://localhost:3000/auth" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"secret"}'Create course (protected)
curl -X POST "http://localhost:3000/courses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <JWT>" \
-d '{"title":"Node.js Avançado","description":"..."}'Node fetch example
import fetch from 'node-fetch';
const res = await fetch('http://localhost:3000/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]', password: 'secret' })
});
console.log(await res.json());- Drizzle ORM is used. Client initialization is in
src/db/client.ts:
// src/db/client.ts
import { drizzle } from "drizzle-orm/node-postgres";
export const db = drizzle(env.DATABSE_URL);src/db/schema.tscurrently appears empty in the repository. Expected tables (based on routes and README examples):users,courses,enrollments. Please add Drizzle table definitions insrc/db/schema.ts. Example Drizzle snippet (TODO: adapt to your types):
import { pgTable, serial, text, varchar, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: varchar('id').primaryKey(),
name: text('name'),
email: varchar('email').unique(),
password: text('password'),
role: text("role").notNull().default("student"),
});Because schema.ts is empty, the generator cannot extract columns/constraints automatically. Please fill the schema to enable precise docs.
Detected services (under src/services/):
env.ts— Zod-based environment parsing (envSchema.parse). IMPORTANT: importing this file may callenvSchema.parse(process.env)and throw if env is missing. Tests and generators should mock this.middleware.ts— content-type checks and global middleware registration.errors.ts— error mapping plugin for the server (map DB errors to HTTP).utils.ts— helper functions likehashPassword.enrollments.ts—generateEnrollmentNumber()helper (example implementation exists).
Sequence (Login):
sequenceDiagram
participant Client
participant API
participant DB
Client->>API: POST /auth (email,password)
API->>DB: SELECT user by email
DB-->>API: user row
API->>API: sign JWT
API-->>Client: 200 OK + Set-Cookie / body { token }
