NTI Backend is a NestJS API for the NTI platform. The service uses PostgreSQL for persistence, Redis and BullMQ for background jobs, Prisma for data access, Fastify for HTTP, Swagger for API docs, and Puppeteer-based rendering for PDF/export workflows.
The codebase currently covers these major areas:
auth: registration, login, refresh/logout, email confirmation, password reset, forced password change, and invite-based onboardingaccount: authenticated account operations such as password and email change flowsadmin: admin APIs for users, organizations, system invites, and academic structureorganization: organization profile, membership, invitations, access control, and organization documentsteam: team lifecycle, membership, leadership transfer, and team invitationsstudent-profile: student profile data plus academic structure lookup APIsapplications: application lifecycle, calls, sections, evaluations, needs-info threads, eligibility signals, and document completenessprograms/program-a: mentorship and milestone support for mentored applicationsprograms/program-b: backlog, team application, company overview, and project execution flowsreports: dashboards, exports, audit data, and async PDF/export jobscontact: contact form submissionsfiles: presigned uploads/downloads and upload trackinginvites: invite validation endpointsinfrastructure: config, database, logging, mail, queueing, storage, hashing, and PDF supportworker: BullMQ worker entrypoint for async processors
If you are new to the codebase, start with:
src/main.tsfor bootstrap, global validation, CORS, cookies, and Swaggersrc/app.module.tsfor the active module graphprisma/schema.prismafor the domain modelsrc/app.controller.tsfor health endpoints
flowchart TD
FE[Frontend / Admin UI] --> API[NestJS API<br/>src/main.ts + src/app.module.ts]
API --> AUTH[Auth + Account]
API --> CORE[Organization + Team + Invites]
API --> STUDENT[Student Profile]
API --> APPS[Applications]
API --> PROGA[Program A]
API --> PROGB[Program B]
API --> REPORTS[Reports + Export API]
API --> CONTACT[Contact]
API --> FILES[Files + Document Flows]
AUTH --> DB[(PostgreSQL + Prisma)]
CORE --> DB
STUDENT --> DB
APPS --> DB
PROGA --> DB
PROGB --> DB
REPORTS --> DB
CONTACT --> DB
FILES --> DB
FILES --> R2[Cloudflare R2 / Object Storage]
REPORTS --> PDF[PDF Renderer]
PDF --> QUEUE[BullMQ Queues]
API --> QUEUE
QUEUE --> REDIS[(Redis)]
WORKER[Worker<br/>src/worker.ts] --> QUEUE
WORKER --> REDIS
WORKER --> MAIL[Email Processor]
WORKER --> PDFJOBS[PDF / Export Processors]
MAIL --> BREVO[Brevo Email API]
PDFJOBS --> R2
APPS --> FLOW[Application Lifecycle]
FLOW --> PROGA
FLOW --> PROGB
- Node.js 22+
- npm
- Docker and Docker Compose
- PostgreSQL and Redis if you run the app outside Compose
Create a local environment file from the example:
cp .env.example .envImportant variables:
| Variable | Purpose |
|---|---|
PORT |
HTTP port for the API |
NODE_ENV |
Nest/Node runtime mode |
APP_ENV |
Cookie/CORS behavior profile (local, development, staging, production, test) |
RUN_QUEUE_PROCESSORS |
Enables in-process queue processors inside the API app |
DATABASE_URL |
PostgreSQL connection string used by Prisma and seeds |
CORS_ORIGINS |
Comma-separated list of allowed frontend origins; wildcard patterns are supported |
REDIS_HOST / REDIS_PORT |
Redis connection used by BullMQ |
BREVO_API_KEY / EMAIL_FROM |
Outbound email delivery configuration |
EMAIL_LOGO_URL |
Optional absolute public logo URL rendered in emails |
FRONTEND_URL |
Frontend base URL used in generated links |
R2_* |
Cloudflare R2 object storage configuration |
FILE_UPLOAD_* |
Presigned upload expiry, size limits, MIME allowlist, and upload verification |
FILE_DOWNLOAD_PRESIGN_EXPIRES_SECONDS |
Presigned download lifetime for private file access |
ORGANIZATION_DOCUMENT_* |
Upload policy for organization documents |
PUPPETEER_* |
Browser executable, headless mode, and timeout for PDF/export rendering |
PDF_JOB_WAIT_TIMEOUT_MS / PDF_WORKER_CONCURRENCY |
Queue-backed PDF/export job behavior |
JWT_* |
Access, refresh, and forced-password-change token secrets and expirations |
ARGON2_TIME_COST |
Password hashing cost used by auth and seed scripts |
TOKEN_BYTE_LENGTH |
Random token entropy for auth/invite flows |
EMAIL_VERIFICATION_EXPIRATION_HOURS |
Email confirmation token lifetime |
DEV_EMAIL_VERIFICATION_BYPASS_* |
Local-only bypass for email verification flows |
FORCE_PASSWORD_CHANGE_TOKEN_EXPIRATION_MINUTES |
Forced password change token lifetime |
PASSWORD_RESET_EXPIRATION_MINUTES |
Password reset token lifetime |
SYSTEM_INVITATION_EXPIRATION_HOURS |
System invitation token lifetime |
ORGANIZATION_INVITATION_EXPIRATION_DAYS |
Organization invitation token lifetime |
SUPERADMIN_TEMP_PASSWORD |
Temporary password for seeded superadmin account |
The authoritative schema for supported variables is src/infrastructure/config/env.schema.ts. .env.example covers the common local setup but is not the canonical source for every optional key.
Compose builds the dev target from the single multi-stage Dockerfile and starts:
app: Nest API onhttp://localhost:3001postgres: PostgreSQL 16 onlocalhost:5432redis: Redis 7 onlocalhost:6379
Run it with:
docker compose upImportant behavior:
- the
appservice runsprisma migrate deploybeforenpm run start:dev - the
appservice setsRUN_QUEUE_PROCESSORS=true, so email/PDF/export processors run in-process by default - the separate
workerCompose service exists, but it is optional and only starts when you enable theworkerprofile
To start the dedicated worker locally:
docker compose --profile worker upIf you want to run the app without Compose, start PostgreSQL and Redis first, then:
npm install
npm run prisma:generate
npm run prisma:migrate
npm run start:devRun the worker in a second terminal only when you want dedicated queue processing outside the API process:
npm run start:worker:devIf you keep RUN_QUEUE_PROCESSORS=false, async jobs require the worker process.
- health endpoints:
GET /andGET /health - REST prefix:
/api/v1 - Swagger UI:
http://localhost:3001/api/docs
Swagger is configured for:
- bearer-token fallback auth
accessTokencookie authrefreshTokencookie authrequiresPasswordChangeTokencookie auth
The auth/account stack supports:
- standard registration and login
- company-owner registration
- invite-based registration
- email confirmation and resend
- access token refresh through HttpOnly cookies
- logout and refresh-token revocation
- forgotten password and password reset
- forced password change for privileged accounts
- authenticated password and email change flows
Operational details:
- access tokens are primarily transported in the HttpOnly
accessTokencookie - refresh tokens are stored in the HttpOnly
refreshTokencookie - forced password changes use
requiresPasswordChangeToken APP_ENV=localuses local-friendly cookie policy- deployed environments use cross-site secure cookie policy
- frontend requests must use credentials and come from allowed
CORS_ORIGINS
The current domain goes beyond auth/org/team management:
- application calls can be managed by admins and queried publicly
- applications support sections, history, evaluations, decisions, mentorship assignments, needs-info threads, and document tracking
- Program A includes mentored application support plus milestones and mentorship notes
- Program B includes backlog intake, team applications, company overview, project execution, mentoring, milestones, rewards, and project documents
Supporting docs in docs/ include PROGRAM_A_STATUS_MAP.md and DEV_FIXTURES.md.
File handling is built around presigned URLs rather than streaming file bodies through the API.
POST /api/v1/files/upload-urlcreates an upload record and returns a presigned upload URLPOST /api/v1/files/completemarks the upload as finishedGET /api/v1/files/:id/download-urlreturns a public URL or a presigned private download URL
There are also dedicated organization/program document flows built on top of the same storage infrastructure.
BullMQ and Redis handle asynchronous work such as:
- email delivery
- PDF rendering
- report export jobs
Relevant code lives in:
src/infrastructure/queuesrc/infrastructure/queue/processorssrc/infrastructure/pdfsrc/reports/report-exportsrc/worker.ts
Prisma is used for schema management and database access.
- Generate the client:
npm run prisma:generate - Create/apply local migrations:
npm run prisma:migrate - Apply migrations in deployment:
npm run prisma:migrate:deploy - Open Prisma Studio:
npm run prisma:studio - Reset the local database:
npm run prisma:reset - Run baseline seed data:
npm run prisma:seed - Run extended dev fixtures:
npm run seed:dev
The Prisma schema, migrations, and seed scripts live in prisma/.
npm run prisma:seed currently runs:
001-superadmin.seed.ts002-calls.seed.ts
npm run seed:dev additionally runs:
003-program-b-backlog.seed.ts004-dev-fixtures.seed.ts
The seed pipeline requires:
DATABASE_URLSUPERADMIN_TEMP_PASSWORD- a valid
ARGON2_TIME_COST
npm run build
npm run format
npm run start
npm run start:dev
npm run start:debug
npm run start:prod
npm run start:worker
npm run start:worker:dev
npm run lint
npm run lint:staged
npm run typescript
npm run test
npm run test:watch
npm run test:cov
npm run test:debug
npm run test:e2e
npm run prisma:generate
npm run prisma:migrate
npm run prisma:migrate:deploy
npm run prisma:studio
npm run prisma:reset
npm run prisma:seed
npm run seed:dev- ESLint handles linting
- Prettier handles formatting
- TypeScript checks run through
npm run typescript - Husky, lint-staged, and Commitlint are configured for contributor workflow support
src/
account/ authenticated account operations
admin/ admin APIs
applications/ application lifecycle and calls
auth/ authentication and onboarding
contact/ contact submissions
files/ upload/download record management
infrastructure/ shared technical modules
organization/ organization and org documents
programs/ program A and program B flows
reports/ dashboards, audits, exports
student-profile/ student profile and academic structure
team/ team membership and invites
prisma/ schema, migrations, and seed tasks
docs/ supporting project documentation
test/ end-to-end tests
src/main.tsis the canonical place for app-level middleware, validation, CORS, cookies, and API docs setupsrc/app.module.tsshows which modules are active in the API processRUN_QUEUE_PROCESSORSdetermines whether processors run inside the API process- when adding or changing a public endpoint, update the matching Swagger decorators and tests together
- when changing auth, storage, or reporting flows, also inspect the related repositories and queue processors
The repository ships a single multi-stage Dockerfile with these targets:
devworkerproduction
Default docker build . uses the final production stage for the API. The worker is built from the same Dockerfile with --target worker; there is no separate Dockerfile.worker in the repository.
If you deploy queue-backed email/PDF/export jobs separately, provision:
- one web service for the API
- one background worker built from
Dockerfiletargetworker - one Redis instance
Notes:
- the API production container runs Prisma migrations on startup
- API and worker must point to the same PostgreSQL and Redis instances
- both runtimes need the required app config from
src/infrastructure/config/env.schema.ts - Puppeteer/Chromium settings must be present anywhere PDF/export rendering is expected