A robust Node.js REST API built with Express.js and PostgreSQL for managing school operations with comprehensive authentication, authorization, and CRUD operations.
- Node.js (v16 or higher)
- PostgreSQL (v12 or higher)
- npm or yarn
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
# Edit .env with your configuration# Set up database
createdb school_mgmt
psql -d school_mgmt -f ../seed_db/tables.sql
psql -d school_mgmt -f ../seed_db/seed-db.sql
# Start the server
npm startCreate a .env file with the following variables:
PORT=5007DATABASE_URL=postgresql://postgres:postgres@localhost:5432/school_mgmtJWT_ACCESS_TOKEN_SECRET=your_access_token_secretJWT_REFRESH_TOKEN_SECRET=your_refresh_token_secretCSRF_TOKEN_SECRET=your_csrf_secretJWT_ACCESS_TOKEN_TIME_IN_MS=900000JWT_REFRESH_TOKEN_TIME_IN_MS=28800000CSRF_TOKEN_TIME_IN_MS=950000MAIL_FROM_USER=your-email@domain.comEMAIL_VERIFICATION_TOKEN_SECRET=your_email_verification_secretEMAIL_VERIFICATION_TOKEN_TIME_IN_MS=18000000PASSWORD_SETUP_TOKEN_TIME_IN_MS=300000PASSWORD_SETUP_TOKEN_SECRET=your_password_setup_secretUI_URL=http://localhost:5173API_URL=http://localhost:5007COOKIE_DOMAIN=localhostRESEND_API_KEY=your_resend_api_key- Node.js - JavaScript runtime
- Express.js - Web application framework
- PostgreSQL - Primary database
- JWT - Authentication tokens
- Argon2 - Password hashing
- express-async-handler - Async error handling
- cors - Cross-origin resource sharing
- cookie-parser - Cookie parsing middleware
- dotenv - Environment variable management
- pg - PostgreSQL client
- uuid - UUID generation
- zod - Runtime type validation
- resend - Email service
src/
├── config/ # Configuration files
│ ├── database.js # Database connection setup
│ └── env.js # Environment variables
├── middlewares/ # Express middlewares
│ ├── auth.js # Authentication middleware
│ ├── csrf.js # CSRF protection
│ ├── error-handler.js # Global error handling
│ └── validation.js # Request validation
├── modules/ # Feature-based API modules
│ ├── auth/ # Authentication endpoints
│ │ ├── auth-controller.js
│ │ ├── auth-service.js
│ │ ├── auth-router.js
│ │ └── auth-repository.js
│ ├── students/ # Student management
│ │ ├── students-controller.js
│ │ ├── students-service.js
│ │ ├── students-router.js
│ │ └── students-repository.js
│ ├── notices/ # Notice management
│ ├── leave/ # Leave management
│ ├── staff/ # Staff management
│ └── departments/ # Department management
├── routes/ # API route definitions
│ ├── v1.js # Version 1 API routes
│ └── index.js # Route aggregation
├── shared/ # Shared utilities and repositories
│ ├── repository/ # Common database operations
│ ├── errors/ # Custom error classes
│ └── validators/ # Shared validation schemas
├── templates/ # Email templates
│ ├── password-setup.html
│ └── email-verification.html
├── utils/ # Utility functions
│ ├── jwt-handle.js # JWT operations
│ ├── csrf-handle.js # CSRF token handling
│ ├── email-service.js # Email sending utilities
│ └── helpers.js # General helper functions
├── app.js # Express app configuration
└── server.js # Server entry point
- Access Tokens: Short-lived tokens (15 minutes) for API access
- Refresh Tokens: Long-lived tokens (8 hours) for token renewal
- Token Rotation: Automatic token refresh mechanism
- CSRF Protection: HMAC-based CSRF tokens
- Password Hashing: Argon2 for secure password storage
- Role-Based Access Control: Granular permissions system
- Request Validation: Zod schema validation
- Secure Cookies: HttpOnly, Secure, SameSite cookies
1. User login → Validate credentials
2. Generate access + refresh tokens
3. Set secure HTTP-only cookies
4. Client includes tokens in requests
5. Middleware validates tokens
6. Automatic token refresh when needed
http://localhost:5007/api/v1
Login user and get authentication tokens.
{
"email": "admin@school-admin.com",
"password": "3OU4zn3q6Zh9"
}Logout user and invalidate tokens.
{
"message": "Logged out successfully"
}Refresh access token using refresh token.
{
"accessToken": "new_access_token",
"user": { "id": 1, "name": "Admin", "role": "admin" }
}Get all students with pagination and filtering.
Query Parameters:
- page: Page number (default: 1)
- limit: Items per page (default: 10)
- search: Search term
- class: Filter by class
- section: Filter by section
Create a new student.
{
"name": "John Doe",
"email": "john@example.com",
"class_name": "Grade 10",
"section_name": "A",
"roll": 101,
"dob": "2005-01-15",
"father_name": "Robert Doe",
"father_phone": "+1234567890"
}Update student information.
{
"name": "John Smith",
"phone": "+1234567891"
}Delete a student record.
{
"message": "Student deleted successfully"
}Get all notices with filtering.
Query Parameters:
- status: Filter by status (draft, published, archived)
- author_id: Filter by author
- recipient_type: Filter by recipient type
Create a new notice.
{
"title": "Important Announcement",
"description": "This is an important notice for all students.",
"recipient_type": "all",
"recipient_role_id": null
}Update notice.
{
"title": "Updated Announcement",
"description": "Updated notice content"
}Delete a notice.
Get leave requests with filtering.
Submit a new leave request.
{
"from_dt": "2024-01-15",
"to_dt": "2024-01-17",
"note": "Family emergency",
"leave_policy_id": 1
}Approve a leave request.
Reject a leave request.
Get all staff members.
Add new staff member.
{
"name": "Jane Teacher",
"email": "jane@school.com",
"role_id": 2,
"department_id": 1,
"join_dt": "2024-01-01"
}- users: User accounts and basic information
- user_profiles: Extended user profile data
- roles: System roles and permissions
- classes: Academic classes
- sections: Class sections
- departments: Organizational departments
- notices: System notices and announcements
- user_leaves: Leave requests and approvals
- access_controls: Permission definitions
- permissions: Role-permission mappings
- Users belong to roles
- Users have profiles
- Students belong to classes and sections
- Staff belong to departments
- Notices have authors and recipients
- Leave requests belong to users
// Controller patternconsthandleGetStudents=asyncHandler(async(req,res)=>{const{ page =1, limit =10, search }=req.query;constresult=awaitstudentService.getStudents({ page, limit, search });res.json(result);});// Service patternconstgetStudents=async({ page, limit, search })=>{constoffset=(page-1)*limit;returnawaitstudentRepository.findStudents({ offset, limit, search });};// Repository patternconstfindStudents=async({ offset, limit, search })=>{constquery=` SELECT * FROM users u JOIN user_profiles up ON u.id = up.user_id WHERE u.role_id = $1${search ? 'AND u.name ILIKE $2' : ''} LIMIT $3 OFFSET $4 `;// Execute query and return results};// Custom error classesclassApiErrorextendsError{constructor(statusCode,message){super(message);this.statusCode=statusCode;}}// Global error handlerconsterrorHandler=(err,req,res,next)=>{conststatusCode=err.statusCode||500;res.status(statusCode).json({success: false,message: err.message,
...(process.env.NODE_ENV==='development'&&{stack: err.stack})});};// Zod schema validationconstcreateStudentSchema=z.object({name: z.string().min(1,'Name is required'),email: z.string().email('Invalid email'),class_name: z.string().optional(),section_name: z.string().optional(),roll: z.number().int().positive().optional()});// Middleware usageconstvalidateCreateStudent=(req,res,next)=>{try{createStudentSchema.parse(req.body);next();}catch(error){thrownewApiError(400,'Validation failed');}};# Run all tests
npm test# Run tests in watch mode
npm run test:watch
# Generate coverage report
npm run test:coverage// Example testdescribe('Student Controller',()=>{describe('GET /students',()=>{it('should return paginated students',async()=>{constresponse=awaitrequest(app).get('/api/v1/students').set('Authorization',`Bearer ${token}`).expect(200);expect(response.body.data).toBeInstanceOf(Array);expect(response.body.pagination).toBeDefined();});});});# Install production dependencies
npm ci --only=production
# Start production server
NODE_ENV=production npm startFROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 5007
CMD ["npm", "start"]- Set up PostgreSQL database
- Configure environment variables
- Set up SSL certificates for HTTPS
- Configure reverse proxy (Nginx)
- Set up monitoring and logging
Problem: Some CRUD operations for students are missing or incomplete.
Location: /src/modules/students/students-controller.jsSolution:
- Implement missing endpoints (CREATE, UPDATE, DELETE)
- Add proper validation and error handling
- Test all operations thoroughly
Problem: Notice description field not being saved properly.
Location: /src/modules/notices/notices-service.jsSolution:
- Check database query parameters
- Verify request body parsing
- Add proper validation for description field
- Use connection pooling
- Implement proper indexing
- Use prepared statements
- Optimize complex queries
- Implement Redis for session storage
- Cache frequently accessed data
- Use ETags for conditional requests
- Regular security audits
- Keep dependencies updated
- Implement rate limiting
- Use HTTPS in production
| Script | Description |
|---|---|
npm start | Start production server |
npm run dev | Start development server with nodemon |
npm test | Run test suite |
npm run lint | Run ESLint |
npm run format | Format code with Prettier |
For frontend documentation, see ../frontend/README.md