--- layout: default title: API Integration nav_order: 8 --- # API Integration Guide {: .no_toc } Learn how to integrate Gentelella Admin Template with backend APIs and external services {: .fs-6 .fw-300 } ## Table of contents {: .no_toc .text-delta } 1. TOC {:toc} --- ## REST API Integration ### HTTP Client Setup #### Axios Configuration ```javascript // src/js/api/http-client.js import axios from 'axios'; class HttpClient { constructor() { this.client = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8080/api', timeout: 10000, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } }); this.setupInterceptors(); } setupInterceptors() { // Request interceptor - add auth token this.client.interceptors.request.use( (config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Response interceptor - handle errors this.client.interceptors.response.use( (response) => response.data, (error) => { if (error.response?.status === 401) { this.handleUnauthorized(); } return Promise.reject(this.formatError(error)); } ); } handleUnauthorized() { localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); window.location.href = '/login.html'; } formatError(error) { if (error.response) { return { message: error.response.data?.message || 'Server error', status: error.response.status, data: error.response.data }; } else if (error.request) { return { message: 'Network error - please check your connection', status: 0 }; } else { return { message: error.message || 'Unknown error occurred', status: -1 }; } } // HTTP methods get(url, config = {}) { return this.client.get(url, config); } post(url, data = {}, config = {}) { return this.client.post(url, data, config); } put(url, data = {}, config = {}) { return this.client.put(url, data, config); } patch(url, data = {}, config = {}) { return this.client.patch(url, data, config); } delete(url, config = {}) { return this.client.delete(url, config); } // File upload upload(url, file, onProgress = null) { const formData = new FormData(); formData.append('file', file); return this.client.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent) => { if (onProgress) { const progress = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); onProgress(progress); } } }); } } // Create singleton instance export const httpClient = new HttpClient(); ``` ### API Service Layer #### Base Service Class ```javascript // src/js/api/base-service.js import { httpClient } from './http-client.js'; export class BaseService { constructor(endpoint) { this.endpoint = endpoint; this.http = httpClient; } async getAll(params = {}) { try { const response = await this.http.get(this.endpoint, { params }); return { success: true, data: response.data, meta: response.meta }; } catch (error) { return { success: false, error: error.message, details: error }; } } async getById(id) { try { const response = await this.http.get(`${this.endpoint}/${id}`); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async create(data) { try { const response = await this.http.post(this.endpoint, data); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async update(id, data) { try { const response = await this.http.put(`${this.endpoint}/${id}`, data); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async delete(id) { try { await this.http.delete(`${this.endpoint}/${id}`); return { success: true }; } catch (error) { return { success: false, error: error.message, details: error }; } } async search(query, params = {}) { try { const response = await this.http.get(`${this.endpoint}/search`, { params: { q: query, ...params } }); return { success: true, data: response.data, meta: response.meta }; } catch (error) { return { success: false, error: error.message, details: error }; } } } ``` #### Specific Service Classes ```javascript // src/js/api/user-service.js import { BaseService } from './base-service.js'; class UserService extends BaseService { constructor() { super('/users'); } async authenticate(credentials) { try { const response = await this.http.post('/auth/login', credentials); // Store auth token if (response.token) { localStorage.setItem('auth_token', response.token); localStorage.setItem('user_data', JSON.stringify(response.user)); } return { success: true, data: response }; } catch (error) { return { success: false, error: error.message }; } } async logout() { try { await this.http.post('/auth/logout'); } catch (error) { console.warn('Logout API call failed:', error.message); } finally { localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); window.location.href = '/login.html'; } } async getCurrentUser() { try { const response = await this.http.get('/auth/me'); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async updateProfile(data) { try { const response = await this.http.put('/auth/profile', data); // Update stored user data localStorage.setItem('user_data', JSON.stringify(response.data)); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async changePassword(passwordData) { try { const response = await this.http.post('/auth/change-password', passwordData); return { success: true, data: response }; } catch (error) { return { success: false, error: error.message }; } } async uploadAvatar(file, onProgress) { try { const response = await this.http.upload('/auth/avatar', file, onProgress); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } } export const userService = new UserService(); // src/js/api/dashboard-service.js import { BaseService } from './base-service.js'; class DashboardService extends BaseService { constructor() { super('/dashboard'); } async getStats(dateRange = '30d') { try { const response = await this.http.get('/dashboard/stats', { params: { range: dateRange } }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async getChartData(chartType, params = {}) { try { const response = await this.http.get(`/dashboard/charts/${chartType}`, { params }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async getRecentActivity(limit = 10) { try { const response = await this.http.get('/dashboard/activity', { params: { limit } }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } } export const dashboardService = new DashboardService(); ``` --- ## Real-time Integration ### WebSocket Connection ```javascript // src/js/api/websocket-client.js class WebSocketClient { constructor() { this.ws = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 1000; this.listeners = new Map(); this.isConnected = false; } connect() { const wsUrl = import.meta.env.VITE_WS_URL || 'ws://localhost:8080/ws'; const token = localStorage.getItem('auth_token'); this.ws = new WebSocket(`${wsUrl}?token=${token}`); this.ws.onopen = () => { console.log('WebSocket connected'); this.isConnected = true; this.reconnectAttempts = 0; this.emit('connected'); }; this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data); this.handleMessage(message); } catch (error) { console.error('Failed to parse WebSocket message:', error); } }; this.ws.onclose = () => { console.log('WebSocket disconnected'); this.isConnected = false; this.emit('disconnected'); this.reconnect(); }; this.ws.onerror = (error) => { console.error('WebSocket error:', error); this.emit('error', error); }; } reconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('Max reconnection attempts reached'); return; } this.reconnectAttempts++; const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`); setTimeout(() => { this.connect(); }, delay); } handleMessage(message) { const { type, data } = message; this.emit(type, data); } send(type, data = {}) { if (!this.isConnected) { console.warn('WebSocket not connected'); return false; } const message = JSON.stringify({ type, data }); this.ws.send(message); return true; } on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event).push(callback); } off(event, callback) { if (!this.listeners.has(event)) return; const callbacks = this.listeners.get(event); const index = callbacks.indexOf(callback); if (index > -1) { callbacks.splice(index, 1); } } emit(event, data) { if (!this.listeners.has(event)) return; this.listeners.get(event).forEach(callback => { try { callback(data); } catch (error) { console.error(`Error in WebSocket event handler for ${event}:`, error); } }); } disconnect() { if (this.ws) { this.ws.close(); this.ws = null; } this.isConnected = false; } } // Create singleton instance export const wsClient = new WebSocketClient(); // Auto-connect if user is authenticated if (localStorage.getItem('auth_token')) { wsClient.connect(); } ``` ### Real-time Dashboard Updates ```javascript // src/js/dashboard/real-time-dashboard.js import { wsClient } from '../api/websocket-client.js'; import { dashboardService } from '../api/dashboard-service.js'; class RealTimeDashboard { constructor() { this.charts = new Map(); this.stats = new Map(); this.init(); } init() { this.setupWebSocketListeners(); this.loadInitialData(); } setupWebSocketListeners() { // Listen for real-time stats updates wsClient.on('stats.update', (data) => { this.updateStats(data); }); // Listen for new chart data wsClient.on('chart.data', (data) => { this.updateChart(data.chartId, data.data); }); // Listen for new notifications wsClient.on('notification', (data) => { this.showNotification(data); }); // Listen for user activity wsClient.on('user.activity', (data) => { this.updateActivityFeed(data); }); } async loadInitialData() { try { // Load dashboard stats const statsResult = await dashboardService.getStats(); if (statsResult.success) { this.renderStats(statsResult.data); } // Load chart data const chartTypes = ['sales', 'users', 'revenue']; for (const chartType of chartTypes) { const chartResult = await dashboardService.getChartData(chartType); if (chartResult.success) { this.renderChart(chartType, chartResult.data); } } // Load recent activity const activityResult = await dashboardService.getRecentActivity(); if (activityResult.success) { this.renderActivity(activityResult.data); } } catch (error) { console.error('Failed to load dashboard data:', error); } } updateStats(data) { Object.entries(data).forEach(([key, value]) => { const element = document.querySelector(`[data-stat="${key}"]`); if (element) { // Animate value change this.animateValue(element, value); } }); } animateValue(element, newValue) { const currentValue = parseFloat(element.textContent.replace(/[^0-9.-]/g, '')) || 0; const difference = newValue - currentValue; const steps = 30; const stepValue = difference / steps; let current = currentValue; const timer = setInterval(() => { current += stepValue; element.textContent = this.formatValue(current, element.dataset.format); if (--steps <= 0) { clearInterval(timer); element.textContent = this.formatValue(newValue, element.dataset.format); } }, 16); } formatValue(value, format) { switch (format) { case 'currency': return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value); case 'percentage': return `${value.toFixed(1)}%`; case 'number': return new Intl.NumberFormat('en-US').format(Math.round(value)); default: return value.toString(); } } updateChart(chartId, newData) { const chart = this.charts.get(chartId); if (!chart) return; // Update chart data chart.data = newData; chart.update('active'); } showNotification(data) { // Use notification plugin or create custom notification if (window.GentelellaPlugins && window.GentelellaPlugins.getPlugin('notifications')) { const notifications = window.GentelellaPlugins.getPlugin('notifications'); notifications.show(data.message, data.type); } } updateActivityFeed(activity) { const feedContainer = document.querySelector('#activity-feed'); if (!feedContainer) return; const activityItem = document.createElement('div'); activityItem.className = 'activity-item'; activityItem.innerHTML = `