Assignment Project: Building a real-time attendance management system with Node.js, Express, MongoDB, and WebSockets.
A backend system that enables teachers to manage classes and track student attendance in real-time through WebSocket connections. This project is built as part of a backend development assignment.
This project implements a complete backend system with:
- JWT-based authentication (signup, login, me)
- Role-based access control (teacher & student)
- Class management CRUD operations
- WebSocket-based live attendance tracking
- Attendance persistence to MongoDB
Key Design: Only ONE class session can be active at a time on WebSocket. No room management needed - all broadcasts go to all connected clients.
-
Authentication System
- User registration with role selection (teacher/student)
- Login with JWT token generation
- Get current user info (me endpoint)
- Password hashing with bcrypt
- JWT middleware for protected routes
-
Class Management
- Create classes (Teacher only)
- Add students to classes
- View class details with enrolled students
- Get list of all students (Teacher only)
-
Attendance System
- Attendance model created
- Get student's attendance status
- Start attendance session endpoint
- In-memory session structure
- Real-time Attendance (WebSocket)
- Live attendance marking via WebSocket
- Real-time updates broadcast to all clients
- Full WebSocket event implementation
- Persistence of attendance to MongoDB on session end
- Runtime: Node.js with TypeScript
- Framework: Express.js
- Database: MongoDB with Mongoose
- Authentication: JWT (jsonwebtoken) - Token expires in 1 day
- Validation: Zod schemas
- WebSocket: ws library v8.19.0
- Security: bcrypt (10 salt rounds)
- Dev Tools: nodemon, tsx, prettier
-
Clone the repository
git clone https://github.com/Akhand0ps/Live-Attendance-System.git cd Live-Attendance-System -
Install dependencies
npm install
-
Setup environment variables
Create a
.envfile in the root directory with the following:PORT=3000 uri=mongodb://localhost:27017/attendance-system JWT_SECRET=your_super_secret_jwt_key_here
Important:
- The MongoDB URI variable is named
uri(lowercase) - Make sure MongoDB is running before starting the server
- The MongoDB URI variable is named
-
Start MongoDB
# Windows net start MongoDB # macOS/Linux sudo systemctl start mongod
-
Run the development server
npm run dev
Server will start on
http://localhost:3000(or your specified PORT)
# Start development server with hot reload
npm run dev
# Format code with Prettier
npm run format{
_id: ObjectId,
name: String, // lowercase, trimmed
email: String, // unique, trimmed
password: String, // hashed with bcrypt (10 rounds)
role: "teacher" | "student",
createdAt: Date,
updatedAt: Date
}{
_id: ObjectId,
className: String, // trimmed, min 3 characters
teacherId: ObjectId, // ref: User
studentsIds: [ObjectId], // ref: User (array)
createdAt: Date,
updatedAt: Date
}{
_id: ObjectId,
classId: ObjectId, // ref: ClassModel
studentId: ObjectId, // ref: User
status: "present" | "absent",
createdAt: Date,
updatedAt: Date
}All API routes are prefixed with /api/v1
Register a new user (teacher or student).
Request Body:
{
"name": "john doe",
"email": "john@example.com",
"password": "password123",
"role": "student"
}Success Response (201):
{
"success": true,
"data": {
"_id": "675e8f9a1234567890abcdef",
"name": "john doe",
"email": "john@example.com",
"role": "student",
"createdAt": "2026-01-08T10:30:00.000Z",
"updatedAt": "2026-01-08T10:30:00.000Z"
}
}Error Responses:
// Validation Error (422)
{
"success": false,
"error": "Invalid request Schema"
}
// Email exists (403)
{
"success": false,
"error": "Email already exists"
}Login and receive JWT token.
Request Body:
{
"email": "john@example.com",
"password": "password123"
}Success Response (200):
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}JWT Payload Structure:
{
"id": "675e8f9a1234567890abcdef",
"role": "student",
"exp": 1704802200
}Error Responses:
// Validation Error (422)
{
"success": false,
"error": "Invalid request Schema"
}
// User not found (404)
{
"success": false,
"error": "User not found"
}
// Wrong password (400)
{
"success": false,
"error": "Invalid email or password"
}Get current user details (requires authentication).
Headers:
Authorization: Bearer <JWT_TOKEN>
Success Response (200):
{
"success": true,
"data": {
"_id": "675e8f9a1234567890abcdef",
"name": "john doe",
"email": "john@example.com",
"role": "student",
"createdAt": "2026-01-08T10:30:00.000Z",
"updatedAt": "2026-01-08T10:30:00.000Z"
}
}Error Responses:
// Missing/Invalid token (404 or 401)
{
"success": false,
"error": "please send jwt token"
}
// User not found (404)
{
"success": false,
"error": "User not found"
}Note: Password field is excluded from the response.
Create a new class (Teacher only).
Headers:
Authorization: Bearer <JWT_TOKEN>
Request Body:
{
"className": "Math 101"
}Success Response (201):
{
"success": true,
"data": {
"_id": "675e9a1b1234567890fedcba",
"className": "Math 101",
"teacherId": "675e8f9a1234567890abcdef",
"studentsIds": [],
"createdAt": "2026-01-08T11:00:00.000Z",
"updatedAt": "2026-01-08T11:00:00.000Z"
}
}Error Responses:
// Validation Error (400)
{
"success": false,
"error": "Invalid request schema"
}
// Not a teacher (403)
{
"success": false,
"error": "Forbidden, teacher access required"
}
// Class already exists (409)
{
"success": false,
"error": "class already exist"
}Add a student to a class (Teacher only).
Request Body:
{
"studentId": "675e8f9a1234567890abcdef"
}Success Response (201):
{
"success": true,
"ExistingClass": {
"_id": "675e9a1b1234567890fedcba",
"className": "Math 101",
"teacherId": "675e8f9a1234567890teacher",
"studentsIds": ["675e8f9a1234567890abcdef"],
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
}Error Responses:
// Student not found (404)
{
"success": false,
"error": "Student not found"
}
// Class not found (404)
{
"success": false,
"error": "Class not found"
}
// Student already in class (409)
{
"success": false,
"error": "student already exist in class"
}Get class details with enrolled students (Teacher who owns class OR student enrolled in class).
Success Response (200):
{
"success": true,
"data": [
{
"_id": "675e9a1b1234567890fedcba",
"className": "Math 101",
"teacherId": {
"name": "teacher name",
"email": "teacher@test.com"
},
"studentsIds": [
{
"name": "john doe",
"email": "john@example.com"
}
],
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
]
}Error Responses:
// Class not found (404)
{
"success": false,
"error": "Class not found"
}
// Not enrolled (404)
{
"success": false,
"message": "you are not enrolled in the class"
}Note: Response includes populated teacher and students data with name and email fields only.
Get all students (Teacher only).
Success Response (200):
{
"success": true,
"data": [
{
"_id": "675e8f9a1234567890abcdef",
"name": "john doe",
"email": "john@example.com",
"role": "student"
}
]
}Error Response:
// No students found (404)
{
"success": false,
"error": "No students found"
}Get student's attendance for a class (Student only, must be enrolled).
Success Response (200):
// When attendance is marked
{
"success": true,
"data": {
"classId": "675e9a1b1234567890fedcba",
"status": "present"
}
}
// When not marked yet
{
"success": true,
"data": {
"classId": "675e9a1b1234567890fedcba",
"status": null
}
}Error Responses:
// Class not found (404)
{
"success": false,
"error": "Class not found"
}
// Not enrolled (403)
{
"success": false,
"error": "You are not in the class, please contact your professor/admin"
}Start a new attendance session (Teacher only).
Request Body:
{
"className": "Math 101"
}Success Response (200):
{
"success": true,
"data": {
"_id": "675e9a1b1234567890fedcba",
"className": "Math 101",
"teacherId": "675e8f9a1234567890teacher",
"studentsIds": ["675e8f9a1234567890abcdef"],
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
}Error Responses:
// Class not found (404)
{
"success": false,
"error": "class not found"
}
// Validation Error (400)
{
"success": false,
"error": "Invalid request schema"
}Note: This endpoint initializes an in-memory active session structure for WebSocket attendance tracking.
ws://localhost:3000/ws?token=<JWT_TOKEN>
NOTE: WebSocket implementation is pending.
All WebSocket messages follow this format:
{
"event": "EVENT_NAME",
"data": { ... }
}Teacher marks attendance for a student.
Teacher Sends:
{
"event": "ATTENDANCE_MARKED",
"data": {
"studentId": "675e8f9a1234567890abcdef",
"status": "present"
}
}Server Broadcasts:
{
"event": "ATTENDANCE_MARKED",
"data": {
"studentId": "675e8f9a1234567890abcdef",
"status": "present"
}
}Request attendance summary.
Teacher Sends:
{
"event": "TODAY_SUMMARY"
}Server Broadcasts:
{
"event": "TODAY_SUMMARY",
"data": {
"present": 18,
"absent": 4,
"total": 22
}
}Student requests their attendance status.
Student Sends:
{
"event": "MY_ATTENDANCE"
}Server Responds (Unicast):
{
"event": "MY_ATTENDANCE",
"data": {
"status": "present"
}
}End attendance session and persist to database.
Teacher Sends:
{
"event": "DONE"
}Server Broadcasts:
{
"event": "DONE",
"data": {
"message": "Attendance persisted",
"present": 18,
"absent": 4,
"total": 22
}
}{
"event": "ERROR",
"data": {
"message": "Error description"
}
}Common errors:
"Unauthorized or invalid token"- Invalid JWT"Forbidden, teacher event only"- Student tried teacher-only event"Forbidden, student event only"- Teacher tried student-only event"No active attendance session"- No session started
- Token expiry: 1 day (24 hours)
- Secret: Defined in
.envasJWT_SECRET - Algorithm: HS256 (default)
{
"id": "675e8f9a1234567890abcdef", // user's MongoDB _id
"role": "teacher" | "student",
"iat": 1704715800, // issued at
"exp": 1704802200 // expires at
}1. HTTP Requests Include JWT token in Authorization header with Bearer prefix:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example with curl:
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
http://localhost:3000/api/v1/auth/meExample with JavaScript fetch:
fetch('http://localhost:3000/api/v1/auth/me', {
headers: {
Authorization: `Bearer ${token}`,
},
})2. WebSocket Connection (when implemented) Pass token as query parameter:
ws://localhost:3000/ws?token=<JWT_TOKEN>
Routes that require authentication middleware (authenticate):
GET /api/v1/auth/mePOST /api/v1/class/create-class(also requires teacher role)POST /api/v1/class/:id/add-student(also requires teacher role)GET /api/v1/class/:idGET /api/v1/class/students(also requires teacher role)GET /api/v1/attendance/:id/my-attendance(also requires student role)POST /api/v1/attendance/start(also requires teacher role)
Routes that require teacher role (verifyRole middleware):
POST /api/v1/class/create-classPOST /api/v1/class/:id/add-studentGET /api/v1/class/studentsPOST /api/v1/attendance/start
Routes that require student role (OnlyUser middleware):
GET /api/v1/attendance/:id/my-attendance
The server will maintain one active attendance session at a time:
const activeSession = {
classId: '675e9a1b1234567890fedcba',
startedAt: '2026-01-08T10:00:00.000Z',
attendance: {
'675e8f9a1234567890abcdef': 'present',
'675e8f9a1234567890student2': 'absent',
},
}Design Decisions:
- Only ONE session can be active at a time
startedAtmust be ISO string:new Date().toISOString()- Session is cleared when teacher sends
DONEevent - No room management - all broadcasts go to all connected clients
Success:
{
"success": true,
"data": {
/* response data */
}
}Error:
{
"success": false,
"error": "Error message"
}| Code | Description | Example Error Messages |
|---|---|---|
| 200 | Success | - |
| 201 | Created | - |
| 400 | Bad Request | "Invalid request schema", "Invalid email or password" |
| 401 | Unauthorized | "Unauthorized, token missing or invalid" |
| 403 | Forbidden (Role) | "Forbidden, teacher access required", "Email already exists" |
| 404 | Not Found | "User not found", "please send jwt token" |
| 422 | Validation Error | "Invalid request Schema" |
| 500 | Server Error | "Internal Server Error", err.message |
- Start Session: Teacher calls
POST /api/v1/attendance/startwith className (Implemented) - Mark Attendance: Teacher sends
ATTENDANCE_MARKEDevents via WebSocket - Real-time Updates: All connected clients receive attendance updates
- Check Status: Students can query their status via
MY_ATTENDANCEevent - View Summary: Teacher can request
TODAY_SUMMARYanytime - End Session: Teacher sends
DONEevent, which:- Marks all unmarked students as absent
- Persists attendance to MongoDB
- Broadcasts final summary
- Clears in-memory session
Teacher Permissions:
- Create classes ✓
- Add students to classes ✓
- Start attendance sessions ✓
- View all students ✓
- Mark attendance (WebSocket - in progress)
- Access teacher-only routes ✓
Student Permissions:
- View enrolled classes ✓
- Check own attendance ✓
- Receive real-time attendance updates (WebSocket - in progress)
Live-Attendance-System/
├── src/
│ ├── index.ts # Entry point - server initialization
│ ├── app.ts # Express app configuration
│ ├── config/
│ │ └── db.config.ts # MongoDB connection setup
│ ├── controller/
│ │ ├── user.controller.ts # Auth: register, login, me
│ │ ├── class.controller.ts # Class CRUD operations
│ │ ├── attendance.controller.ts # Attendance management
│ │ ├── activesession.controller.ts # In-memory session state
│ │ └── ws.controller.ts # WebSocket handler (in progress)
│ ├── middleware/
│ │ ├── auth.middleware.ts # JWT verification
│ │ └── auth.role.ts # Role checks (teacher/student)
│ ├── models/
│ │ ├── user.model.ts # User schema with timestamps
│ │ ├── class.model.ts # Class schema with timestamps
│ │ └── attendance.model.ts # Attendance schema
│ ├── routes/
│ │ ├── user.route.ts # /api/v1/auth routes
│ │ ├── class.route.ts # /api/v1/class routes
│ │ └── attendance.route.ts # /api/v1/attendance routes
│ ├── schemas/
│ │ ├── auth.schema.ts # Zod validation for auth
│ │ └── class.schema.ts # Zod validation for class
│ ├── types/
│ │ └── express.d.ts # Custom Express types
│ └── utils/
│ └── hash.pass.ts # Bcrypt password hashing
├── .env # Environment variables
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript configuration
└── README.md # This file
- index.ts: Starts server after DB connection
- app.ts: Express middleware and route mounting (includes attendance routes)
- db.config.ts: Async MongoDB connection with error handling
- auth.middleware.ts: Extracts and verifies JWT, attaches
req.user - auth.role.ts: Role verification (
verifyRolefor teachers,OnlyUserfor students) - class.controller.ts: Create class, add students, get class details, get all students
- attendance.controller.ts: Get student attendance, start attendance session
- activesession.controller.ts: Manages in-memory active session state
- attendance.model.ts: Mongoose schema for attendance records
- express.d.ts: TypeScript declarations for
req.user
You can test the API using any HTTP client like Postman, Thunder Client, or curl.
Example workflow:
# 1. Register a teacher
POST http://localhost:3000/api/v1/auth/register
Content-Type: application/json
{
"name": "Teacher Name",
"email": "teacher@test.com",
"password": "123456",
"role": "teacher"
}
# 2. Login to get token
POST http://localhost:3000/api/v1/auth/login
Content-Type: application/json
{
"email": "teacher@test.com",
"password": "123456"/create-class
}
# 3. Create a class (use token from step 2)
POST http://localhost:3000/api/v1/class/create-class
Authorization: Bearer YOUR_TOKEN_HERE
Content-Type: application/json
{
"className": "Mathematics 101"
}
# 4. Get your profile
GET http://localhost:3000/api/v1/auth/me
Authorization: Bearer YOUR_TOKEN_HEREThe assignment provides a test application: https://github.com/rahul-MyGit/mid-test
# Windows
net start MongoDB
# macOS/Linux
sudo systemctl start mongod
# Or using MongoDB Compass
# Just open MongoDB Compass - it will start MongoDB automaticallynpm run devYou should see:
Mongodb connected localhost
server running on PORT 3000
Step 1: Register a Teacher
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "John Teacher",
"email": "teacher@test.com",
"password": "123456",
"role": "teacher"
}'Step 2: Login
curl -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "teacher@test.com",
"password": "123456"
}'Save the token from response!
Step 3: Create a Class
curl -X POST http://localhost:3000/api/v1/class \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-d '{
"className": "Mathematics 101"
}'Step 4: Get Your Profile
curl http://localhost:3000/api/v1/auth/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"Completed:
- Authentication (register, login, me) - All 3 endpoints
- JWT middleware with role-based access (verifyRole for teacher, OnlyUser for student)
- Create class endpoint with duplicate check
- Add students to class with ObjectId validation
- Get class details with populated teacher/students
- Get all students endpoint (teacher only)
- My attendance endpoint (student only)
- Start attendance session endpoint (teacher only)
- Attendance model with timestamps
- Password hashing with bcrypt (10 rounds)
- Zod validation schemas including ObjectId validation
- MongoDB models with timestamps
- In-memory active session structure
Pending:
- WebSocket server implementation
- Real-time event handlers (ATTENDANCE_MARKED, TODAY_SUMMARY, MY_ATTENDANCE, DONE)
- Attendance persistence on session end
- Auto-marking absent students
- Complete WebSocket server setup with ws package
- Implement real-time event handlers
- Add attendance persistence logic
- Add auto-marking for absent students
- Test with provided test application
This project is built according to specifications from: Backend WebSocket Live Attendance System assignment
Assignment details: Notion Link
ISC
GitHub: @Akhand0ps
Repository: Live-Attendance-System
NOTE: This is a work-in-progress assignment project. The system is designed for a single active attendance session at a time, with all WebSocket broadcasts going to all connected clients without room management.