REST API backend for the TalentFlow Learning Management System. Built for Trueminds Innovations' internship programme to manage intern learning, progress tracking, assignments, certificates, and cohort collaboration.
Live URL: https://talentflow-backend-7pp4.onrender.com
| Layer |
Technology |
| Runtime |
Node.js |
| Framework |
Express 5 |
| Language |
TypeScript 5 |
| ORM |
Prisma 6 |
| Database |
PostgreSQL (Neon) |
| Authentication |
JWT (HS256, 7-day expiry) |
| Password hashing |
bcrypt |
| File uploads |
Cloudinary + multer |
| Email |
Nodemailer |
| Validation |
express-validator + Zod |
| Rate limiting |
express-rate-limit |
- Node.js 18+
- A PostgreSQL database (Neon recommended)
- A
.env file — see Environment Variables below
# Install dependencies
npm install
# Generate Prisma client
npx prisma generate
# Run database migrations
npx prisma migrate deploy
# Development (hot reload)
npm run dev
# Production build
npm run build
npm start
The server runs on port 5000 by default (http://localhost:5000).
Note: If connecting to a Neon database, disconnect from any VPN before running prisma migrate or prisma db push.
Create a .env file in the project root:
DATABASE_URL=postgresql://user:password@host/talentflow
JWT_SECRET=your_strong_secret_here
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
EMAIL_USER=your_smtp_email
EMAIL_PASS=your_smtp_password
GOOGLE_CLIENT_ID=your_google_oauth_client_id
PORT=5000
src/
├── app.ts Express app setup — middleware, routes, CORS
├── server.ts Server entry point — listens on PORT
├── config/
│ ├── db.ts Prisma client instance
│ └── cloudinary.ts Cloudinary configuration
├── controllers/ Request handlers — one file per domain
├── services/ Business logic — one file per domain
├── routes/ Express routers — one file per domain
├── middleware/
│ ├── authMiddleware.ts JWT verification + req.user injection
│ ├── errorMiddleware.ts Global error handler
│ ├── rateLimitMiddleware.ts Rate limiters for auth routes
│ ├── uploadMiddleware.ts Multer + Cloudinary upload
│ └── validateRequest.ts express-validator error collector
├── validators/ Input validation rules per domain
├── utils/
│ ├── AppError.ts Custom error class with status code
│ ├── asyncHandler.ts Wraps async controllers — no try/catch needed
│ ├── generateToken.ts JWT signing
│ └── sendEmail.ts Nodemailer helper
└── types/
└── express.d.ts Extends Express Request with req.user
Each domain follows the same 3-layer pattern:
Route → Controller → Service → Prisma → Database
- Routes — define HTTP method and path, apply auth/validation middleware
- Controllers — extract request data, call the service, send the response
- Services — all business logic and Prisma queries live here
Errors are thrown as AppError instances from the service layer and caught by the global error middleware.
All protected endpoints require Authorization: Bearer <token> in the request headers.
| Method |
Route |
Auth |
Description |
| POST |
/api/auth/register |
None |
Register new user — returns { data: { ...user, token } } |
| POST |
/api/auth/login |
None |
Login — returns { data: { ...user, token } } |
| POST |
/api/auth/google-auth |
None |
Google OAuth — verifies Google ID token |
| POST |
/api/auth/forgot-password |
None |
Sends 6-digit OTP to email |
| POST |
/api/auth/verify-otp |
None |
Validates OTP |
| POST |
/api/auth/reset-password/:token |
None |
Sets new password |
Token shape: { id, role, iat, exp } — the token is nested inside data in the login/register response, not at the root.
| Method |
Route |
Auth |
Description |
| GET |
/api/users/me |
Any |
Get current user's full profile |
| PUT |
/api/users/me |
Any |
Update name, bio, track — sets onboardingCompleted: true |
| GET |
/api/users |
Admin/Mentor |
List all users with pagination and role filter |
| GET |
/api/users/:id |
Admin |
Get user by ID |
| PUT |
/api/users/:id/role |
Admin |
Update a user's role |
| Method |
Route |
Auth |
Description |
| POST |
/api/cohorts |
Admin |
Create a cohort |
| GET |
/api/cohorts |
Admin/Mentor |
List all cohorts |
| GET |
/api/cohorts/:id |
Admin/Mentor |
Get cohort details |
| POST |
/api/cohorts/assign-user |
Admin |
Assign an intern to a cohort |
| Method |
Route |
Auth |
Description |
| GET |
/api/enrollments |
Intern |
All enrollments with full course + module data |
| POST |
/api/enrollments |
Intern |
Enroll in a course — one active course at a time |
| Method |
Route |
Auth |
Description |
| GET |
/api/courses |
Any |
All active courses |
| POST |
/api/courses |
Admin |
Create a course |
| GET |
/api/courses/:id/overview |
Any |
Course detail — modules, resources, mentor, session IDs |
| GET |
/api/courses/:id/progress |
Intern |
Progress across all modules |
| GET |
/api/courses/:id/modules |
Any |
Ordered list of modules |
| POST |
/api/courses/:id/assign-mentor |
Admin |
Assign a mentor to a course |
| Method |
Route |
Auth |
Description |
| POST |
/api/courses/:courseId/modules |
Mentor/Admin |
Create a module |
| GET |
/api/modules/:id/progress |
Intern |
Per-lesson completion status |
| GET |
/api/modules/:id/lessons |
Any |
All lessons in a module |
| PUT |
/api/modules/:id |
Mentor/Admin |
Update a module |
| DELETE |
/api/modules/:id |
Admin |
Delete a module |
| Method |
Route |
Auth |
Description |
| POST |
/api/modules/:moduleId/lessons |
Mentor/Admin |
Create a lesson |
| PUT |
/api/lessons/:id |
Mentor/Admin |
Update a lesson |
| DELETE |
/api/lessons/:id |
Admin |
Delete a lesson |
| POST |
/api/lessons/:id/complete |
Intern |
Mark lesson complete — idempotent, triggers progress recompute |
| GET |
/api/lessons/:id/progress |
Intern |
Completion status of a single lesson |
| POST |
/api/lessons/:id/study-log |
Intern |
Log study time { duration: number } (minutes) |
| GET |
/api/lessons/:id/study-log |
Intern |
All study log entries for a lesson |
| Method |
Route |
Auth |
Description |
| POST |
/api/assignments |
Mentor/Admin |
Create an assignment |
| GET |
/api/assignments/course/:courseId |
Any |
All assignments for a course |
| GET |
/api/assignments/:id |
Any |
Single assignment detail |
| PUT |
/api/assignments/:id |
Mentor/Admin |
Update an assignment |
| DELETE |
/api/assignments/:id |
Admin |
Delete an assignment |
| Method |
Route |
Auth |
Description |
| GET |
/api/submissions |
Intern |
All of the intern's submissions |
| GET |
/api/submissions/:id |
Any |
Submission detail with grade and feedback |
| POST |
/api/submissions/:assignmentId |
Intern |
Submit — { submissionType, fileUrl, figmaUrl, portfolioUrl, noteToMentor } |
| PUT |
/api/submissions/:assignmentId/resubmit |
Intern |
Update an existing submission |
| Method |
Route |
Auth |
Description |
| POST |
/api/resources |
Mentor/Admin |
Create a resource — attach to course, module, or lesson |
| GET |
/api/resources/course/:courseId |
Any |
Course-level resources |
| GET |
/api/resources/module/:moduleId |
Any |
Module-level resources |
| GET |
/api/resources/lesson/:lessonId |
Any |
Lesson-level resources |
| PUT |
/api/resources/:id |
Mentor/Admin |
Update a resource |
| DELETE |
/api/resources/:id |
Mentor/Admin |
Delete a resource |
| Method |
Route |
Auth |
Description |
| GET |
/api/study-logs/me |
Intern |
Total study time summary |
| GET |
/api/study-logs/me/overtime |
Intern |
Grouped hours — ?period=daily|weekly|monthly |
| Method |
Route |
Auth |
Description |
| GET |
/api/analytics/me |
Intern |
Full snapshot — GPA, streaks, certificates, enrollments |
| GET |
/api/analytics/me/modules |
Intern |
Per-module performance — ?courseId= to filter |
| GET |
/api/analytics/me/study-overtime |
Intern |
Study hours over time — ?period=daily|weekly|monthly |
| Method |
Route |
Auth |
Description |
| GET |
/api/certificates/me |
Intern |
All earned certificates |
| GET |
/api/certificates/:id |
Any |
Single certificate with full detail |
| POST |
/api/certificates |
Admin |
Manually issue a certificate |
| Method |
Route |
Auth |
Description |
| POST |
/api/discussions |
Any |
Create a thread — type: COURSE | TEAM | DIRECT |
| GET |
/api/discussions/:id |
Any |
Full thread with all replies |
| POST |
/api/discussions/:id/replies |
Any |
Post a reply |
| DELETE |
/api/discussions/:id/replies/:replyId |
Any |
Delete a reply (own, or Admin) |
| Method |
Route |
Auth |
Description |
| POST |
/api/live-sessions |
Mentor/Admin |
Create a session — { title, courseId, scheduledAt, duration, platform, platformUrl } |
| GET |
/api/live-sessions/:id |
Any |
Session details |
| PUT |
/api/live-sessions/:id |
Mentor/Admin |
Update a session |
| DELETE |
/api/live-sessions/:id |
Admin |
Delete a session |
| POST |
/api/live-sessions/:id/rsvp |
Intern |
RSVP to a session |
| DELETE |
/api/live-sessions/:id/rsvp |
Intern |
Cancel RSVP |
| Method |
Route |
Auth |
Description |
| POST |
/api/teams |
Admin |
Create a team |
| GET |
/api/teams |
Any |
List all teams |
| GET |
/api/teams/:id |
Any |
Team details with members |
| POST |
/api/teams/:id/members |
Admin |
Add a member to a team |
| Method |
Route |
Auth |
Description |
| GET |
/api/dashboard |
Intern |
Aggregated dashboard data — stats, active enrollment, module progress |
| Model |
Key Purpose |
User |
All users — role: INTERN | MENTOR | ADMIN |
Cohort |
Group of interns on the same track |
Course |
Learning content container |
Module |
Ordered section within a course — can be locked |
Lesson |
Individual unit — type: VIDEO | ARTICLE |
Enrollment |
Links User to Course — tracks progress, status, averageGrade |
LessonProgress |
Records lesson completion — triggers progress recompute |
StudyLog |
Time spent on a lesson in minutes |
Assignment |
Task issued per course/module with due date |
Submission |
Intern's response — tracks grade and mentor feedback |
Resource |
Reference material — attached to course, module, or lesson |
Certificate |
Auto-issued when enrollment reaches 100% |
Discussion |
Message thread — type: COURSE | TEAM | DIRECT |
LiveSession |
Scheduled session — platform: ZOOM | GOOGLE_MEET | YOUTUBE | OTHER |
LiveSessionRSVP |
Intern's registration for a live session |
Team |
Cross-functional intern group |
- Lesson marked complete →
Enrollment.progress recomputed automatically
- Module fully complete → next module's
isLocked set to false
- Enrollment reaches 100% → status set to
COMPLETED, certificate auto-issued
- RSVP created →
LIVE_SESSION_REMINDER notification created for the intern
All errors return a consistent JSON shape:
{
"success": false,
"message": "Descriptive error message"
}
Common status codes: 400 Bad Request · 401 Unauthorized · 403 Forbidden · 404 Not Found · 429 Rate Limit Exceeded · 500 Server Error
The backend is deployed on Render as a web service using a Dockerfile for reproducible builds.
Live URL: https://talentflow-backend-7pp4.onrender.com
Database: Neon PostgreSQL
On first deploy or after schema changes, Prisma migrations run automatically via the start script:
npx prisma migrate deploy && node dist/server.js
- Branch off
main — use feature/, fix/, or chore/ prefixes
- Never commit
.env — it is gitignored
- Run
npx prisma generate after any schema change
- Keep
prisma.config.ts committed — it is required for Railway/Render build to resolve routes correctly
TalentFlow LMS — Trueminds Innovations · 2026