Backend API for iDecide — an educational decision support system. Built with NestJS + TypeORM + PostgreSQL, with JWT authentication, Swagger docs (dev-only), file uploads, real-time chat (Socket.IO), and a security-focused middleware stack.
- Features
- Tech Stack
- Project Structure
- Requirements
- Quick Start
- Environment Variables
- Running
- API Docs (Swagger)
- Authentication
- Database
- Uploads
- Testing
- Security Notes
- Scripts
- License
- Auth & Users
- JWT-based auth (
Bearer <token>) - User roles/entities:
User,Student,Advisor,Admin
- JWT-based auth (
- Domain Modules
- Universities
- Colleges
- Majors
- Scholarships
- Favorites (universities & scholarships)
- Real-time Chat
- Socket.IO-based chat module (see
src/chat)
- Socket.IO-based chat module (see
- Email
- Email module for notifications and flows (see
src/email)
- Email module for notifications and flows (see
- Security & Validation
- Global DTO validation via
class-validator/class-transformer - Strict request shaping: whitelist + forbid non-whitelisted fields
- Helmet security headers + compression + cookie parsing
- Global exception filter for sanitized errors
- Request logging interceptor
- Simple IP-based rate limiting interceptor
- Global DTO validation via
More details on the security/validation work are documented in SECURITY_IMPLEMENTATION.md.
- Runtime: Node.js + TypeScript
- Framework: NestJS
- DB: PostgreSQL
- ORM: TypeORM
- Auth:
@nestjs/jwt,passport-jwt - Docs:
@nestjs/swagger - Logging:
nestjs-pino - HTTP Client: Axios
- Uploads: Multer
- Realtime: Socket.IO
- Testing: Jest + Supertest
High-level layout:
src/auth— authentication + user entitiessrc/universities— universities domainsrc/colleges— colleges domainsrc/majors— majors domainsrc/scholarships— scholarships domainsrc/favorites— favorites domainsrc/chat— websocket chatsrc/common— filters/interceptors/validatorsuploads/— uploaded files (runtime directory)
The app uses a global API prefix of api (configured in src/main.ts), so routes look like:
http://localhost:3000/api/...
- Node.js (recommended: latest LTS)
- npm
- PostgreSQL database instance
- Install dependencies:
npm install- Create your
.envfile.
This project loads environment variables from .env (see ConfigModule.forRoot({ envFilePath: '.env' }) in src/app.module.ts).
There is an .env.example in the repo; copy it to .env and fill in values.
- Start the API in dev mode:
npm run start:devThe server will start on:
http://localhost:3000- API base path:
http://localhost:3000/api
This project expects DB configuration via environment variables (with development-friendly defaults):
PORT(default:3000)NODE_ENV(productiondisables Swagger and TypeORMsynchronize)
Database variables used in src/app.module.ts:
DB_HOST(default:localhost)DB_PORT(default:32768)DB_USERNAME(default:postgres)DB_PASSWORD(default:postgres)DB_DATABASE(default:idecide)
Other variables may be required depending on enabled features (JWT secrets, email SMTP, etc.). Use the repo’s .env.example as the source of truth.
npm run start:devnpm run start:debugnpm run build
npm run start:prodSwagger is enabled only when NODE_ENV !== 'production'.
Once running in dev, open:
http://localhost:3000/api/docs
The Swagger setup includes JWT bearer auth under the name JWT-auth. Authorize using:
Authorization: Bearer <your_token>
This API uses JWT bearer tokens. Typical flow:
- Sign up / sign in via the auth endpoints (see Swagger in dev).
- Receive an access token.
- Call protected endpoints with:
Authorization: Bearer <token>
User-related entities live under src/auth/users.
TypeORM is configured in src/app.module.ts.
Important behavior:
synchronizeis enabled only in non-production (NODE_ENV !== 'production')- In production, you should use migrations (not included here by default) or another controlled schema strategy.
To connect locally:
- Ensure PostgreSQL is running
- Ensure the database exists (e.g.
idecide) - Set
DB_*values in.env
This repo includes an uploads/ directory at the project root. If you’re using file upload endpoints, ensure the process has permissions to write there (especially in containerized deployments).
Run unit tests:
npm run testRun e2e/integration tests:
npm run test:e2eRun all test projects:
npm run test:allCoverage:
npm run test:covKey security choices implemented at the app level (src/main.ts):
- Helmet security headers + basic CSP
- CORS configured with:
- Dev origins:
http://localhost:5173,http://localhost:5174 - Production placeholder:
https://yourdomain.com(change this)
- Dev origins:
- Global
ValidationPipe:whitelist: trueforbidNonWhitelisted: truetransform: truewith implicit conversiondisableErrorMessagesin production
- Global exception filter to sanitize error output
- Global logging + rate-limiting interceptors
See SECURITY_IMPLEMENTATION.md for a detailed breakdown.
Common scripts (from package.json):
npm run start— startnpm run start:dev— start (watch)npm run start:prod— run compiled outputnpm run build— buildnpm run lint— eslint (with--fix)npm run format— prettiernpm run test/test:e2e/test:cov— Jest test suites