Skip to content

Latest commit

 

History

History
475 lines (396 loc) · 10.2 KB

File metadata and controls

475 lines (396 loc) · 10.2 KB

🔌 TIMS API Client Documentation

📦 Axios Instance Configuration

File Location:

frontend/src/services/api.js


🚀 Base Configuration:

Base URL: http://localhost:5000/api/v1
Timeout: 15 seconds
Content-Type: application/json
Credentials: included (cookies)

🔐 Authentication:

Automatic Token Handling:

// Token is automatically added to all requests
const token = localStorage.getItem('token');
config.headers.Authorization = `Bearer ${token}`;

Manual Token Management:

import { setAuthToken, removeAuthToken, getAuthToken, isAuthenticated } from '@/services/api';

// Set token after login
setAuthToken('your-jwt-token-here');

// Get current token
const token = getAuthToken();

// Check if authenticated
if (isAuthenticated()) {
  // User is logged in
}

// Remove token (logout)
removeAuthToken();

📝 Request Interceptor Features:

1. Auto Token Injection:

  • Automatically adds JWT token to headers
  • Checks for token existence before adding

2. Request Logging (Development):

🚀 [API Request] GET http://localhost:5000/api/v1/students

3. Request Metadata:

  • Adds timestamp for response time calculation
  • Useful for debugging and performance monitoring

📥 Response Interceptor Features:

1. Response Time Logging (Development):

✅ [API Response] GET /students - 245ms

2. Automatic Error Handling:

Status Code Error Type Behavior
400 VALIDATION_ERROR Returns validation errors
401 AUTH_ERROR Clears token & redirects to login
403 FORBIDDEN_ERROR Shows permission denied
404 NOT_FOUND_ERROR Resource not found
409 CONFLICT_ERROR Duplicate entry
422 VALIDATION_ERROR Returns field errors
429 RATE_LIMIT_ERROR Too many requests
500 SERVER_ERROR Internal server error
502 SERVER_ERROR Bad gateway
503 SERVER_ERROR Service unavailable

3. Network Error Handling:

{
  message: 'Network error. Please check your connection.',
  type: 'NETWORK_ERROR'
}

🎯 Usage Examples:

Basic GET Request:

import api from '@/services/api';

const fetchStudents = async () => {
  try {
    const response = await api.get('/students');
    return response.data;
  } catch (error) {
    console.error('Failed to fetch students:', error.message);
  }
};

POST Request:

const createStudent = async (studentData) => {
  try {
    const response = await api.post('/students', studentData);
    return response.data;
  } catch (error) {
    if (error.type === 'VALIDATION_ERROR') {
      console.error('Validation errors:', error.errors);
    }
    throw error;
  }
};

PUT Request:

const updateStudent = async (id, data) => {
  try {
    const response = await api.put(`/students/${id}`, data);
    return response.data;
  } catch (error) {
    console.error('Update failed:', error.message);
    throw error;
  }
};

DELETE Request:

const deleteStudent = async (id) => {
  try {
    const response = await api.delete(`/students/${id}`);
    return response.data;
  } catch (error) {
    console.error('Delete failed:', error.message);
    throw error;
  }
};

Request with Query Parameters:

const fetchStudentsWithFilters = async (page = 1, limit = 10, search = '') => {
  try {
    const response = await api.get('/students', {
      params: {
        page,
        limit,
        search,
      }
    });
    return response.data;
  } catch (error) {
    console.error('Fetch failed:', error.message);
    throw error;
  }
};

File Upload:

const uploadFile = async (formData) => {
  try {
    const response = await api.post('/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      timeout: 60000, // 60 seconds for file uploads
    });
    return response.data;
  } catch (error) {
    console.error('Upload failed:', error.message);
    throw error;
  }
};

⚠️ Error Handling Patterns:

Pattern 1: Try-Catch:

try {
  const response = await api.get('/students');
  // Success handling
} catch (error) {
  if (error.type === 'AUTH_ERROR') {
    // Redirect to login
  } else if (error.type === 'VALIDATION_ERROR') {
    // Show validation errors
  } else {
    // Show generic error
  }
}

Pattern 2: .then().catch():

api.get('/students')
  .then(response => {
    // Success handling
  })
  .catch(error => {
    console.error('Error:', error.message);
    console.error('Type:', error.type);
  });

Pattern 3: Global Error Handler:

const handleApiError = (error) => {
  switch (error.type) {
    case 'AUTH_ERROR':
      showToast('Session expired. Please login again.', 'error');
      break;
    case 'VALIDATION_ERROR':
      showValidationErrors(error.errors);
      break;
    case 'NETWORK_ERROR':
      showToast('No internet connection.', 'error');
      break;
    case 'SERVER_ERROR':
      showToast('Server error. Please try again later.', 'error');
      break;
    default:
      showToast(error.message, 'error');
  }
};

🔧 Advanced Usage:

Custom Headers for Single Request:

const response = await api.get('/students', {
  headers: {
    'X-Custom-Header': 'custom-value',
  }
});

Override Timeout:

const response = await api.post('/export', data, {
  timeout: 60000, // 60 seconds
});

Download File:

const downloadFile = async (url, filename) => {
  try {
    const response = await api.get(url, {
      responseType: 'blob',
    });
    
    const blob = new Blob([response.data]);
    const downloadUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = downloadUrl;
    link.download = filename;
    link.click();
    window.URL.revokeObjectURL(downloadUrl);
  } catch (error) {
    console.error('Download failed:', error.message);
  }
};

Cancel Request:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

api.get('/students', {
  cancelToken: source.token
}).catch(error => {
  if (axios.isCancel(error)) {
    console.log('Request canceled:', error.message);
  }
});

// Cancel the request
source.cancel('Operation canceled by the user.');

📊 Response Format:

Success Response:

{
  data: {
    success: true,
    message: 'Students fetched successfully',
    data: [...], // Array of students
    pagination: {
      page: 1,
      limit: 10,
      total: 100,
      totalPages: 10
    }
  },
  status: 200,
  statusText: 'OK',
  headers: {...},
  config: {...}
}

Error Response:

{
  message: 'Validation failed',
  type: 'VALIDATION_ERROR',
  errors: {
    email: 'Email already exists',
    phone: 'Invalid phone number'
  }
}

🎨 Development Features:

Console Logging:

In development mode, you'll see:

🚀 [API Request] POST http://localhost:5000/api/v1/students
✅ [API Response] POST /students - 342ms

Error Logging:

❌ [400] Bad Request: Email already exists
❌ [401] Unauthorized: Session expired
❌ [500] Server Error: Internal server error

🔒 Security Features:

1. Automatic Token Refresh:

  • Token is checked on every request
  • Expired tokens trigger automatic logout

2. CSRF Protection:

  • withCredentials: true enables cookie sending
  • Backend can set CSRF tokens

3. XSS Protection:

  • Content-Type headers prevent MIME sniffing
  • JSON-only responses expected

4. Rate Limiting:

  • 429 errors handled gracefully
  • User-friendly error messages

🚀 Best Practices:

1. Use Service Files:

// ✅ Good - Create service file
import api from '@/services/api';

export const studentService = {
  getAll: () => api.get('/students'),
  getById: (id) => api.get(`/students/${id}`),
  create: (data) => api.post('/students', data),
  update: (id, data) => api.put(`/students/${id}`, data),
  delete: (id) => api.delete(`/students/${id}`),
};

2. Handle Errors in Components:

// ✅ Good - Proper error handling
const fetchStudents = async () => {
  try {
    setLoading(true);
    const response = await studentService.getAll();
    setStudents(response.data.data);
  } catch (error) {
    setError(error.message);
    showToast(error.message, 'error');
  } finally {
    setLoading(false);
  }
};

3. Use Async/Await:

// ✅ Good - Clean async/await
const handleSubmit = async (data) => {
  try {
    await studentService.create(data);
    showToast('Student created successfully', 'success');
    refreshList();
  } catch (error) {
    showToast(error.message, 'error');
  }
};

📁 File Structure:

frontend/src/services/
├── api.js                 ✅ Axios instance
├── student.service.js     ✅ Student API calls
├── auth.service.js        ✅ Authentication calls
├── trainer.service.js     ✅ Trainer API calls
└── attendance.service.js  ✅ Attendance API calls

🎯 Quick Reference:

Method Description Example
api.get() GET request api.get('/students')
api.post() POST request api.post('/students', data)
api.put() PUT request api.put('/students/:id', data)
api.patch() PATCH request api.patch('/students/:id/status', data)
api.delete() DELETE request api.delete('/students/:id')

Checklist:

  • Axios instance created
  • Base URL configured
  • Timeout set (15s)
  • Request interceptor (token injection)
  • Response interceptor (error handling)
  • Authentication handling (401 redirect)
  • Network error handling
  • Development logging
  • Utility functions exported
  • All HTTP status codes handled
  • TypeScript ready (JSDoc comments)

🎉 Status: READY

The API client is now production-ready with comprehensive error handling, automatic token management, and development logging!