Skip to main content

API Integration

Overview

The frontend communicates with the backend API using Axios with interceptors for authentication and error handling.

API Service Structure

services/
├── api.ts # Base Axios instance
├── auth.service.ts # Authentication endpoints
├── academy.service.ts
├── student.service.ts
├── course.service.ts
└── enrollment.service.ts

Base API Configuration

// services/api.ts
import axios from 'axios';

const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: {
'Content-Type': 'application/json',
},
});

// Request interceptor
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);

// Response interceptor
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Handle unauthorized
useAuthStore().logout();
}
return Promise.reject(error);
}
);

export default api;

Service Classes

// services/student.service.ts
import api from './api';
import type { Student, PaginatedResponse } from '@/types';

export const studentService = {
async getAll(params?: {
page?: number;
search?: string;
}): Promise<PaginatedResponse<Student>> {
const response = await api.get('/api/v1/students', { params });
return response.data;
},

async getById(id: number): Promise<Student> {
const response = await api.get(`/api/v1/students/${id}`);
return response.data.data;
},

async create(data: Partial<Student>): Promise<Student> {
const response = await api.post('/api/v1/students', data);
return response.data.data;
},

async update(id: number, data: Partial<Student>): Promise<Student> {
const response = await api.put(`/api/v1/students/${id}`, data);
return response.data.data;
},

async delete(id: number): Promise<void> {
await api.delete(`/api/v1/students/${id}`);
},
};

Error Handling

try {
await studentService.create(studentData);
} catch (error) {
if (axios.isAxiosError(error)) {
const message = error.response?.data?.message || 'An error occurred';
// Handle error (show notification, etc.)
}
}

Type Safety

Define TypeScript interfaces for API responses:

// types/api.ts
export interface ApiResponse<T> {
data: T;
message?: string;
}

export interface PaginatedResponse<T> {
data: T[];
meta: {
current_page: number;
last_page: number;
per_page: number;
total: number;
};
}

Loading States

Handle loading states in stores:

const fetchStudents = async () => {
loading.value = true;
error.value = null;

try {
const response = await studentService.getAll();
students.value = response.data;
} catch (err) {
error.value = 'Failed to fetch students';
} finally {
loading.value = false;
}
};