This document describes how the frontend is integrated with the backend API according to the official contract.
All API calls are centralized in the /services directory:
auth.service.ts- Authentication operationsprojects.service.ts- Project CRUD operationstasks.service.ts- Task managementnotifications.service.ts- User notificationswebsocket.service.ts- Real-time WebSocket communication
The lib/api-client.ts provides:
- Automatic token refresh on 401 errors
- Centralized error handling
- Request/response interceptors
- Token management
API configuration is centralized in lib/api-config.ts:
export const API_CONFIG = {
BASE_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api/v1',
WEBSOCKET_URL: process.env.NEXT_PUBLIC_WS_URL || 'http://localhost:5000',
}Create a .env.local file (use .env.local.example as template):
NEXT_PUBLIC_API_URL=http://localhost:5000/api/v1
NEXT_PUBLIC_WS_URL=http://localhost:5000- User submits credentials via
/sign-inpage authService.login()callsPOST /auth/login- Backend returns
accessTokenand user data - Token stored in localStorage
- User redirected to dashboard
- WebSocket connection established
- Access Token: Stored in localStorage, expires in 15 minutes
- Refresh Token: Stored in httpOnly cookie (managed by backend), expires in 7 days
- Auto Refresh: API client automatically refreshes tokens on 401 errors
Use the ProtectedRoute component to guard authenticated pages:
import { ProtectedRoute } from '@/components/protected-route'
export default function DashboardPage() {
return (
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
)
}Manages authentication state:
const { user, isAuthenticated, login, logout, register, isLoading, error } = useAuth()Manages project data with API integration:
const {
projects,
isLoading,
error,
addProject,
updateProject,
deleteProject,
refreshProjects
} = useProjects()Manages tasks with real-time updates:
const {
tasksByProject,
isLoading,
error,
getTasks,
loadProjectTasks,
addTask,
updateTask,
deleteTask,
moveTask,
completeTask
} = useTasks()WebSocket automatically connects when user authenticates:
websocketService.connect(accessToken)Join a project room to receive real-time updates:
websocketService.joinProject(projectId)The TasksContext automatically subscribes to:
task:created- New task addedtask:updated- Task modifiedtask:deleted- Task removedtask:moved- Task moved between columns
websocketService.onTaskCreated((data) => {
console.log('New task:', data.task)
})All API errors are instances of ApiError:
try {
await projectsService.createProject(data)
} catch (error) {
if (error instanceof ApiError) {
console.log(error.code) // Error code from backend
console.log(error.message) // Human-readable message
console.log(error.details) // Additional error details
console.log(error.statusCode) // HTTP status code
}
}UNAUTHORIZED(401) - Invalid or expired tokenFORBIDDEN(403) - Insufficient permissionsNOT_FOUND(404) - Resource doesn't existVALIDATION_ERROR(422) - Input validation failedCONFLICT(409) - Duplicate resourceRATE_LIMIT_EXCEEDED(429) - Too many requests
All contexts expose error state:
const { error, clearError } = useProjects()
// Display error
{error && <Alert variant="destructive">{error}</Alert>}
// Clear error
useEffect(() => {
clearError()
}, [])All type definitions match the backend contract (see lib/types.ts):
ProjectStatus:'IN_PROGRESS' | 'COMPLETED' | 'ON_HOLD' | 'PLANNING'TaskPriority:'HIGH' | 'MEDIUM' | 'LOW'ProjectRole:'OWNER' | 'ADMIN' | 'MEMBER' | 'VIEWER'UserStatus:'ONLINE' | 'AWAY' | 'OFFLINE'
User- User profile dataProject- Project detailsTask- Task informationNotification- User notificationsProjectMember- Project membership info
const project = await projectsService.createProject({
name: 'New Project',
description: 'Project description',
color: '#3B82F6',
status: 'IN_PROGRESS'
})const task = await tasksService.createTask(projectId, {
title: 'Task title',
description: 'Task description',
columnId: columnId,
priority: 'HIGH',
tags: ['frontend', 'urgent'],
assigneeId: userId,
dueDate: '2024-01-31T23:59:59.000Z'
})await tasksService.moveTask(taskId, {
columnId: newColumnId,
position: 0
})const { notifications, total, unreadCount } = await notificationsService.getNotifications({
page: 1,
limit: 20,
unreadOnly: true
})All contexts provide isLoading state:
const { projects, isLoading } = useProjects()
if (isLoading) {
return <LoadingSpinner />
}
return <ProjectsList projects={projects} />The TasksContext implements optimistic updates:
- Update local state immediately
- Make API call
- If API call fails, revert local state
- WebSocket confirms change from server
This provides instant feedback while maintaining consistency.
The backend implements rate limits:
- General endpoints: 100 requests / 15 minutes
- Auth endpoints: 5 requests / 15 minutes
- File uploads: 50 uploads / hour
Monitor rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000- Never log tokens - Tokens are sensitive and should not be logged
- HTTPS in production - Always use HTTPS for API calls
- Token storage - Access token in localStorage, refresh token in httpOnly cookie
- Auto logout - Clear auth data when token refresh fails
- CORS - Backend CORS is configured for frontend origin
// Check API health
const response = await fetch(`${API_CONFIG.BASE_URL}/health`)- Verify token is valid
- Check WebSocket URL configuration
- Ensure backend WebSocket server is running
- Check browser console for connection errors
- Clear localStorage:
localStorage.clear() - Clear cookies
- Restart browser
- Check token expiration
// Force refresh from API
await refreshProjects()
await loadProjectTasks(projectId)The integration is complete - all mock data and localStorage usage has been replaced with real API calls:
- ✅ Authentication (login, register, logout)
- ✅ Projects (CRUD operations)
- ✅ Tasks (CRUD + real-time updates)
- ✅ WebSocket (real-time collaboration)
- ✅ Error handling
- ✅ Loading states
- ✅ Token management
When testing against the backend:
- Start backend server on
http://localhost:5000 - Start frontend:
npm run dev - Navigate to
http://localhost:3000 - Register a new account or login
- Test CRUD operations
- Open multiple browser windows to test real-time updates
Update environment variables for production:
NEXT_PUBLIC_API_URL=https://api.yourdomain.com/api/v1
NEXT_PUBLIC_WS_URL=https://api.yourdomain.comEnsure backend CORS is configured for production frontend URL.