Implements comprehensive CRUD APIs following TDD principles with 92.44% test coverage (exceeds 85% requirement). Features: - Tasks API: Full CRUD with filtering, pagination, and subtask support - Events API: Full CRUD with recurrence support and date filtering - Projects API: Full CRUD with task/event association - Authentication guards on all endpoints - Workspace-scoped queries for multi-tenant isolation - Activity logging for all operations (CREATED, UPDATED, DELETED, etc.) - DTOs with class-validator validation - Comprehensive test suite (221 tests, 44 for new APIs) Implementation: - Services: Business logic with Prisma ORM integration - Controllers: RESTful endpoints with AuthGuard - Modules: Properly registered in AppModule - Documentation: Complete API reference in docs/4-api/4-crud-endpoints/ Test Coverage: - Tasks: 96.1% - Events: 89.83% - Projects: 84.21% - Overall: 92.44% TDD Workflow: 1. RED: Wrote failing tests first 2. GREEN: Implemented minimal code to pass tests 3. REFACTOR: Improved code quality while maintaining coverage Refs #5 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
CRUD API Endpoints
Complete reference for Tasks, Events, and Projects API endpoints.
Overview
All CRUD endpoints follow standard REST conventions and require authentication. They support:
- Full CRUD operations (Create, Read, Update, Delete)
- Workspace-scoped isolation
- Pagination and filtering
- Activity logging for audit trails
Authentication
All endpoints require Bearer token authentication:
Authorization: Bearer {session_token}
The workspace context is extracted from the authenticated user's session.
Tasks API
Endpoints
GET /api/tasks # List tasks
GET /api/tasks/:id # Get single task
POST /api/tasks # Create task
PATCH /api/tasks/:id # Update task
DELETE /api/tasks/:id # Delete task
List Tasks
GET /api/tasks?status=IN_PROGRESS&page=1&limit=20
Query Parameters:
workspaceId(UUID, required) — Workspace IDstatus(enum, optional) —NOT_STARTED,IN_PROGRESS,PAUSED,COMPLETED,ARCHIVEDpriority(enum, optional) —LOW,MEDIUM,HIGHassigneeId(UUID, optional) — Filter by assigned userprojectId(UUID, optional) — Filter by projectparentId(UUID, optional) — Filter by parent task (for subtasks)dueDateFrom(ISO 8601, optional) — Filter tasks due after this datedueDateTo(ISO 8601, optional) — Filter tasks due before this datepage(integer, optional) — Page number (default: 1)limit(integer, optional) — Items per page (default: 50, max: 100)
Response:
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"workspaceId": "550e8400-e29b-41d4-a716-446655440001",
"title": "Complete API documentation",
"description": "Write comprehensive docs for CRUD endpoints",
"status": "IN_PROGRESS",
"priority": "HIGH",
"dueDate": "2026-02-01T00:00:00.000Z",
"assigneeId": "550e8400-e29b-41d4-a716-446655440002",
"creatorId": "550e8400-e29b-41d4-a716-446655440002",
"projectId": null,
"parentId": null,
"sortOrder": 0,
"metadata": {},
"createdAt": "2026-01-28T18:00:00.000Z",
"updatedAt": "2026-01-28T18:00:00.000Z",
"completedAt": null,
"assignee": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "John Doe",
"email": "john@example.com"
},
"creator": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "John Doe",
"email": "john@example.com"
},
"project": null
}
],
"meta": {
"total": 42,
"page": 1,
"limit": 20,
"totalPages": 3
}
}
Get Single Task
GET /api/tasks/:id
Response: Same as task object above, plus subtasks array.
Create Task
POST /api/tasks
Content-Type: application/json
{
"title": "Complete API documentation",
"description": "Write comprehensive docs for CRUD endpoints",
"status": "IN_PROGRESS",
"priority": "HIGH",
"dueDate": "2026-02-01T00:00:00.000Z",
"assigneeId": "550e8400-e29b-41d4-a716-446655440002",
"projectId": null,
"parentId": null,
"sortOrder": 0,
"metadata": {}
}
Fields:
title(string, required, 1-255 chars) — Task titledescription(string, optional, max 10000 chars) — Detailed descriptionstatus(enum, optional) — Default:NOT_STARTEDpriority(enum, optional) — Default:MEDIUMdueDate(ISO 8601, optional) — Target completion dateassigneeId(UUID, optional) — Assigned userprojectId(UUID, optional) — Associated projectparentId(UUID, optional) — Parent task (for subtasks)sortOrder(integer, optional, min: 0) — Display ordermetadata(object, optional) — Custom metadata
Response (200): Created task object
Activity Log: Automatically logs CREATED action for task entity.
Update Task
PATCH /api/tasks/:id
Content-Type: application/json
{
"status": "COMPLETED"
}
All fields are optional for partial updates. Setting status to COMPLETED automatically sets completedAt timestamp.
Response (200): Updated task object
Activity Logs:
UPDATED— Always loggedCOMPLETED— Logged when status changes toCOMPLETEDASSIGNED— Logged whenassigneeIdchanges
Delete Task
DELETE /api/tasks/:id
Response (200): Empty
Activity Log: Logs DELETED action with task title in details.
Note: Deleting a task with subtasks will cascade delete all subtasks.
Events API
Endpoints
GET /api/events # List events
GET /api/events/:id # Get single event
POST /api/events # Create event
PATCH /api/events/:id # Update event
DELETE /api/events/:id # Delete event
List Events
GET /api/events?startFrom=2026-02-01&startTo=2026-02-28
Query Parameters:
workspaceId(UUID, required) — Workspace IDprojectId(UUID, optional) — Filter by projectstartFrom(ISO 8601, optional) — Events starting after this datestartTo(ISO 8601, optional) — Events starting before this dateallDay(boolean, optional) — Filter all-day eventspage(integer, optional) — Page numberlimit(integer, optional) — Items per page
Response:
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"workspaceId": "550e8400-e29b-41d4-a716-446655440001",
"title": "Team Meeting",
"description": "Weekly sync",
"startTime": "2026-02-01T10:00:00.000Z",
"endTime": "2026-02-01T11:00:00.000Z",
"allDay": false,
"location": "Conference Room A",
"recurrence": null,
"creatorId": "550e8400-e29b-41d4-a716-446655440002",
"projectId": null,
"metadata": {},
"createdAt": "2026-01-28T18:00:00.000Z",
"updatedAt": "2026-01-28T18:00:00.000Z",
"creator": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "John Doe",
"email": "john@example.com"
},
"project": null
}
],
"meta": {
"total": 15,
"page": 1,
"limit": 50,
"totalPages": 1
}
}
Create Event
POST /api/events
Content-Type: application/json
{
"title": "Team Meeting",
"description": "Weekly sync",
"startTime": "2026-02-01T10:00:00.000Z",
"endTime": "2026-02-01T11:00:00.000Z",
"allDay": false,
"location": "Conference Room A",
"recurrence": null,
"projectId": null,
"metadata": {}
}
Fields:
title(string, required, 1-255 chars) — Event titledescription(string, optional, max 10000 chars) — DescriptionstartTime(ISO 8601, required) — Event start timeendTime(ISO 8601, optional) — Event end timeallDay(boolean, optional) — Default: falselocation(string, optional, max 500 chars) — Locationrecurrence(object, optional) — Recurrence rules (RRULE format)projectId(UUID, optional) — Associated projectmetadata(object, optional) — Custom metadata
Update Event
PATCH /api/events/:id
Content-Type: application/json
{
"location": "Conference Room B"
}
All fields optional for partial updates.
Delete Event
DELETE /api/events/:id
Projects API
Endpoints
GET /api/projects # List projects
GET /api/projects/:id # Get single project
POST /api/projects # Create project
PATCH /api/projects/:id # Update project
DELETE /api/projects/:id # Delete project
List Projects
GET /api/projects?status=ACTIVE
Query Parameters:
workspaceId(UUID, required) — Workspace IDstatus(enum, optional) —PLANNING,ACTIVE,PAUSED,COMPLETED,ARCHIVEDstartDateFrom(ISO 8601, optional) — Projects starting after this datestartDateTo(ISO 8601, optional) — Projects starting before this datepage(integer, optional) — Page numberlimit(integer, optional) — Items per page
Response:
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"workspaceId": "550e8400-e29b-41d4-a716-446655440001",
"name": "API Development",
"description": "Build CRUD APIs",
"status": "ACTIVE",
"startDate": "2026-01-15",
"endDate": "2026-02-15",
"creatorId": "550e8400-e29b-41d4-a716-446655440002",
"color": "#FF5733",
"metadata": {},
"createdAt": "2026-01-28T18:00:00.000Z",
"updatedAt": "2026-01-28T18:00:00.000Z",
"creator": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "John Doe",
"email": "john@example.com"
},
"_count": {
"tasks": 12,
"events": 3
}
}
],
"meta": {
"total": 5,
"page": 1,
"limit": 50,
"totalPages": 1
}
}
Get Single Project
GET /api/projects/:id
Returns project with embedded tasks and events arrays.
Create Project
POST /api/projects
Content-Type: application/json
{
"name": "API Development",
"description": "Build CRUD APIs",
"status": "ACTIVE",
"startDate": "2026-01-15",
"endDate": "2026-02-15",
"color": "#FF5733",
"metadata": {}
}
Fields:
name(string, required, 1-255 chars) — Project namedescription(string, optional, max 10000 chars) — Descriptionstatus(enum, optional) — Default:PLANNINGstartDate(ISO 8601 date, optional) — Project start dateendDate(ISO 8601 date, optional) — Project end datecolor(string, optional) — Hex color code (e.g.,#FF5733)metadata(object, optional) — Custom metadata
Update Project
PATCH /api/projects/:id
Content-Type: application/json
{
"status": "COMPLETED"
}
All fields optional for partial updates.
Delete Project
DELETE /api/projects/:id
Note: Deleting a project sets projectId to null for all associated tasks and events (does NOT cascade delete).
Error Responses
400 Bad Request
Invalid request format or parameters.
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request"
}
401 Unauthorized
Missing or invalid authentication token.
{
"statusCode": 401,
"message": "No authentication token provided",
"error": "Unauthorized"
}
404 Not Found
Resource not found or not accessible in workspace.
{
"statusCode": 404,
"message": "Task with ID 550e8400-e29b-41d4-a716-446655440003 not found",
"error": "Not Found"
}
422 Unprocessable Entity
Validation errors in request body.
{
"statusCode": 422,
"message": [
"title must not be empty",
"priority must be a valid TaskPriority"
],
"error": "Unprocessable Entity"
}
Activity Logging
All CRUD operations automatically create activity log entries for audit trails:
- Tasks:
CREATED,UPDATED,DELETED,COMPLETED,ASSIGNED - Events:
CREATED,UPDATED,DELETED - Projects:
CREATED,UPDATED,DELETED
See Activity Logging API for querying audit trails.
Examples
Create Task with Project Association
curl -X POST http://localhost:3001/api/tasks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"title": "Design database schema",
"description": "Create ERD for new features",
"priority": "HIGH",
"projectId": "550e8400-e29b-41d4-a716-446655440005",
"dueDate": "2026-02-05T00:00:00.000Z"
}'
Filter Tasks by Multiple Criteria
curl "http://localhost:3001/api/tasks?\
workspaceId=550e8400-e29b-41d4-a716-446655440001&\
status=IN_PROGRESS&\
priority=HIGH&\
dueDateFrom=2026-02-01&\
dueDateTo=2026-02-28&\
page=1&\
limit=20" \
-H "Authorization: Bearer ${TOKEN}"
Update Task Status to Completed
curl -X PATCH http://localhost:3001/api/tasks/550e8400-e29b-41d4-a716-446655440003 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"status": "COMPLETED"
}'
Create Recurring Event
curl -X POST http://localhost:3001/api/events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"title": "Weekly Team Sync",
"startTime": "2026-02-03T10:00:00.000Z",
"endTime": "2026-02-03T11:00:00.000Z",
"recurrence": {
"freq": "WEEKLY",
"interval": 1,
"byweekday": ["MO"]
},
"location": "Zoom"
}'