A robust task management REST API built with NestJS, TypeORM, and PostgreSQL. Features JWT authentication, user-specific task isolation, pagination support, comprehensive CRUD operations, interactive Swagger documentation, and Docker containerization.
- JWT-based authentication system
- User registration and login
- User-specific task management - Each user can only access and manage their own tasks
- Pagination support - Efficient data retrieval with configurable page size and sorting
- Complete task management (Create, Read, Update, Delete)
- Task filtering by title and status
- Database relationship management - Foreign key constraints and cascading deletes
- Performance optimization - Indexed queries for faster searches and sorting
- Interactive Swagger API documentation at
/docs - PostgreSQL database with migrations
- Docker support with multi-stage builds
- Input validation and error handling
- RESTful API design
- Comprehensive API testing interface
- Framework: NestJS (v11.x)
- Database: PostgreSQL 17
- ORM: TypeORM (v0.3.26)
- Authentication: JWT with bcrypt password hashing
- Validation: class-validator & class-transformer
- API Documentation: Swagger/OpenAPI 3.0
- Containerization: Docker & Docker Compose
- Language: TypeScript
- Node.js (v22 or higher)
- npm or yarn
- PostgreSQL (if running locally)
- Docker & Docker Compose (for containerized setup)
Choose your preferred setup method:
Perfect for trying out the API quickly or if you don't want to install PostgreSQL locally.
git clone https://github.com/juniorenv/nestjs-task-management.git
cd nestjs-task-managementIf you want to customize settings:
# Copy environment template
cp .env.example .env
# Edit .env with your preferred settings
vim .env # or use your preferred editorExample .env:
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key
JWT_EXPIRATION_TIME=3600
# Port Configuration
PORT=3000
# Database Configuration
DB_HOST=db
DB_PORT=5432
DB_NAME=taskmanagement
DB_USERNAME=postgres
DB_PASSWORD=your-secure-passworddocker compose up -d
# The API will be available at http://localhost:3000
# Swagger documentation at http://localhost:3000/docs# Check if services are running
docker compose ps
# View logs
docker compose logs -f app
# Stop services
docker compose downBest for active development, debugging, and when you want full control over the environment.
- Node.js (v22 or higher)
- npm or yarn
- PostgreSQL (v17 or higher)
git clone https://github.com/juniorenv/nestjs-task-management.git
cd nestjs-task-management
npm installPrerequisites:
- Install PostgreSQL (v17 or higher) on your system
- Ensure PostgreSQL service is running
- Create a database named
taskmanagement
# Copy environment template
cp .env.example .envConfigure .env for local development:
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key
JWT_EXPIRATION_TIME=3600
# Port Configuration
PORT=3000
# Database Configuration (Local PostgreSQL)
DB_HOST=localhost
DB_PORT=5432
DB_NAME=taskmanagement
DB_USERNAME=postgres
DB_PASSWORD=your-database-password# Build the application first
npm run build
# Run migrations
npm run migration:run# Development mode (with hot reload)
npm run start:dev
# Debug mode
npm run start:debug
# Production mode
npm run build
npm run start:prodThe API will be available at:
- Main API:
http://localhost:3000 - Interactive API documentation is available at
http://localhost:3000/docs
This API includes comprehensive interactive Swagger documentation that allows you to:
- Explore all available endpoints with detailed descriptions
- Test API calls directly from the browser interface
- View complete request/response schemas with examples
- Authenticate with JWT tokens for protected endpoints
- See validation rules and error responses
- Export API specification in OpenAPI format
Once the application is running, visit:
http://localhost:3000/docs
- Create Account: Use the
POST /usersendpoint to register - Login: Use the
POST /auth/loginendpoint to get your JWT token - Authorize: Click the "🔒 Authorize" button in Swagger UI
- Enter Token: Input your token
- Test Endpoints: Now you can test all protected endpoints seamlessly
- Persistent Authorization: Your JWT token persists across browser sessions
- Request Duration Display: See how long each API call takes
- Interactive Forms: Easy-to-use forms for testing endpoints
- Response Examples: See actual API responses with proper formatting
- Error Documentation: Comprehensive error response examples
- Schema Validation: Real-time validation of request payloads
POST /users
Content-Type: application/json
{
"username": "johndoe",
"password": "securepassword123"
}Response:
{
"id": "uuid",
"username": "johndoe"
}POST /auth/login
Content-Type: application/json
{
"username": "johndoe",
"password": "securepassword123"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600
}All task endpoints require authentication. Include the JWT token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Important: Each user can only access and manage their own tasks. The API automatically filters tasks based on the authenticated user.
GET /tasks
GET /tasks?page=1&limit=10&order=DESC
GET /tasks?title=example&status=TO_DO&page=2&limit=20Query Parameters:
page(optional, default: 1): Page number (1-indexed)limit(optional, default: 10, max: 50): Number of items per pageorder(optional, default: DESC): Sort order by creation date (ASC or DESC)title(optional): Filter tasks by title (partial match, case-insensitive)status(optional): Filter tasks by exact status match (TO_DO, IN_PROGRESS, DONE)
Response:
{
"data": [
{
"id": "uuid",
"userId": "user-uuid",
"title": "Complete project documentation",
"description": "Write comprehensive README",
"status": "TO_DO",
"expirationDate": "2024-12-31T23:59:59.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"itemCount": 100,
"pageCount": 10,
"hasPreviousPage": false,
"hasNextPage": true
}
}Note: Only returns tasks belonging to the authenticated user.
GET /tasks/{taskId}Note: Returns 404 if the task doesn't exist or belongs to another user.
POST /tasks
Content-Type: application/json
{
"title": "Complete project documentation",
"description": "Write comprehensive README and API documentation",
"status": "TO_DO",
"expirationDate": "2024-12-31T23:59:59.000Z"
}Response:
{
"id": "uuid",
"userId": "user-uuid",
"title": "Complete project documentation",
"description": "Write comprehensive README and API documentation",
"status": "TO_DO",
"expirationDate": "2024-12-31T23:59:59.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}Note: The userId field is automatically populated from the JWT token and cannot be specified by the client.
PUT /tasks/{taskId}
Content-Type: application/json
{
"title": "Updated task title",
"description": "Updated description",
"status": "IN_PROGRESS",
"expirationDate": "2024-12-31T23:59:59.000Z"
}Note: Only updates tasks owned by the authenticated user.
PATCH /tasks/{taskId}
Content-Type: application/json
{
"status": "DONE"
}Note: Only updates tasks owned by the authenticated user.
DELETE /tasks/{taskId}Note: Only deletes tasks owned by the authenticated user.
TO_DO- Task is pendingIN_PROGRESS- Task is being worked onDONE- Task is completed
All successful responses return JSON with the requested data. Error responses follow this format:
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request"
}The API uses offset-based pagination with the following characteristics:
- Default page size: 10 items
- Maximum page size: 50 items
- Page numbering: 1-indexed (first page is page 1, not 0)
- Sorting: Tasks are sorted by creation date (newest first by default)
- Out of bounds: Returns 400 error if requesting a page beyond available data
Pagination Metadata:
page: Current page numberlimit: Items per pageitemCount: Total number of items matching the filterpageCount: Total number of pages availablehasPreviousPage: Boolean indicating if previous page existshasNextPage: Boolean indicating if next page exists
id(UUID, Primary Key)username(VARCHAR, Unique)password_hash(VARCHAR)
id(UUID, Primary Key)title(VARCHAR, 1-256 chars)description(VARCHAR, 1-512 chars)status(VARCHAR, Default: 'TO_DO', CHECK constraint)created_at(TIMESTAMPTZ)expiration_date(TIMESTAMPTZ)user_id(UUID, Foreign Key → users.id, ON DELETE CASCADE)
- One-to-Many: User → Tasks (one user can have many tasks)
- Cascade Delete: When a user is deleted, all their tasks are automatically deleted
- Foreign Key Constraint: Ensures referential integrity between tasks and users
For optimal query performance, especially with pagination:
- idx_tasks_user_id_status: Composite index on
(user_id, status)for filtered task queries - idx_tasks_user_id_created_at: Composite index on
(user_id, created_at DESC)for efficient pagination and sorting - idx_tasks_user_id_title_trgm: GIN index on
(user_id, title)for fast title searches using trigram matching
- uuid-ossp: For UUID generation
- pg_trgm: For trigram-based text similarity searches
- btree_gin: For efficient composite GIN indexes
# Development
npm run start:dev # Start in watch mode
npm run start:debug # Start in debug mode
# Building
npm run build # Build for production
npm run start:prod # Start production build
# Database
npm run migration:create # Create new migration
npm run migration:run # Run pending migrations
npm run migration:revert # Revert last migration
# Code Quality
npm run lint # Lint and fix code
npm run format # Format code with Prettiernpm run migration:create --name=your-migration-namesrc/
├── auth/ # Authentication module
│ ├── auth.controller.ts
│ ├── auth.service.ts
│ ├── auth.guard.ts
│ ├── auth.module.ts
│ └── auth.dto.ts
├── user/ # User management module
│ ├── user.controller.ts
│ ├── user.service.ts
│ ├── user.module.ts
│ └── user.dto.ts
├── task/ # Task management module
│ ├── task.controller.ts
│ ├── task.service.ts
│ ├── task.module.ts
│ └── task.dto.ts
├── database/ # Database configuration
│ ├── entities/ # TypeORM entities
│ │ ├── user.entity.ts # User entity with tasks relation
│ │ └── task.entity.ts # Task entity with user relation
│ ├── migrations/ # Database migrations
│ │ ├── 1756494841652-user-table.ts
│ │ ├── 1756494848553-task-table.ts
│ │ └── 1759154081649-add-task-indexes.ts # Pagination indexes
│ ├── database.module.ts
│ └── data-source.ts
├── common/ # Shared utilities
│ ├── decorators/ # Custom decorators
│ └── api-common-responses.decorator.ts
│ └── interfaces/ # TypeScript interfaces
│ └── authenticated-request.interface.ts
├── app.module.ts # Root application module
└── main.ts # Application entry point
The application includes a multi-stage Dockerfile for optimized production builds:
- Base stage: Installs dependencies
- Builder stage: Compiles TypeScript
- Production stage: Creates minimal runtime image
# Build image
docker build -t nestjs-task-management .
# Run with Docker Compose
docker compose up -d
# View logs
docker compose logs -f task-management
# Stop services
docker compose downThe API implements comprehensive error handling:
- 400 Bad Request: Invalid input data, malformed requests, invalid user ID, or page out of bounds
- 401 Unauthorized: Authentication errors with specific error messages
- 404 Not Found: Resource not found or user doesn't have access
- 409 Conflict: Duplicate username during registration
- 500 Internal Server Error: Unexpected server errors
The API provides specific error messages for different authentication failures:
| Error Message | Cause | Solution |
|---|---|---|
Token not provided |
No Authorization header sent | Include Authorization: Bearer <token> header |
Token expired |
JWT token has passed expiration time | Login again to obtain a new token |
Invalid token |
Malformed JWT or invalid signature | Verify token format and validity |
Authentication failed |
Unexpected authentication error | Check server logs or contact support |
Unauthorized |
Invalid credentials during login | Verify username and password are correct |
Example Error Response:
{
"message": "Token expired",
"error": "Unauthorized",
"statusCode": 401
}- Page exceeds maximum: When requesting a page number beyond available data
- Validation errors: When pagination parameters (page, limit) fail validation rules
- Password hashing using bcrypt (salt rounds: 10)
- JWT token-based authentication with configurable expiration
- User isolation - Users can only access their own tasks
- Database-level security - Foreign key constraints prevent orphaned records
- Request validation using class-validator
- Environment variable protection for sensitive data
- CORS protection (configurable)
- Input sanitization and validation
This API implements strict data isolation:
- Task Ownership: Every task is associated with a specific user via
user_id - Automatic Filtering: All task queries are automatically filtered by the authenticated user's ID
- Access Control: Users cannot view, modify, or delete tasks belonging to other users
- Cascade Protection: When a user account is deleted, all their tasks are automatically removed
The API includes several performance enhancements:
- Paginated Results: Efficient data retrieval with configurable page sizes to prevent large result sets
- Indexed Queries: Composite indexes on frequently queried columns
- Trigram Matching: Fast full-text search on task titles using PostgreSQL's pg_trgm extension
- Efficient Joins: Optimized database relationships with proper foreign key indexes
- Query Optimization: Status filters use exact matching instead of pattern matching
- Case-Insensitive Search: Title filtering uses ILIKE for better user experience
- Navigate to
http://localhost:3000/docs - Use the interactive interface to test all endpoints
- Authenticate once and test multiple endpoints seamlessly
- Test pagination by adjusting page, limit, and order parameters
# Register a new user
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "password123"}'
# Login and get token
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "password123"}'
# Get tasks with pagination (first page, 10 items)
curl -X GET "http://localhost:3000/tasks?page=1&limit=10&order=DESC" \
-H "Authorization: Bearer <your-jwt-token>"
# Get tasks with filtering and pagination
curl -X GET "http://localhost:3000/tasks?title=project&status=TO_DO&page=1&limit=20" \
-H "Authorization: Bearer <your-jwt-token>"
# Create a task
curl -X POST http://localhost:3000/tasks \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{
"title": "Test Task",
"description": "Testing task creation",
"status": "TO_DO",
"expirationDate": "2025-12-31T23:59:59.000Z"
}'