# API & Service Layer Guide Complete guide for making API calls in the Yaltopia Ticket Admin application. ## Table of Contents - [Architecture Overview](#architecture-overview) - [Available Services](#available-services) - [Basic Usage](#basic-usage) - [Common Patterns](#common-patterns) - [Service Methods Reference](#service-methods-reference) - [Error Handling](#error-handling) - [Best Practices](#best-practices) - [Examples](#examples) --- ## Architecture Overview ### The Service Layer Pattern All API calls flow through a centralized service layer: ``` ┌─────────────────┐ │ Component │ "I need user data" └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Service Layer │ userService.getUsers() │ (Business │ • Type-safe methods │ Logic) │ • Error handling └────────┬────────┘ • Data transformation │ ▼ ┌─────────────────┐ │ API Client │ axios.get('/admin/users') │ (HTTP Layer) │ • Auth token injection └────────┬────────┘ • Token refresh │ • Request/response interceptors ▼ ┌─────────────────┐ │ Backend API │ Returns JSON data └─────────────────┘ ``` ### Why Service Layer? **Before (Bad):** ```typescript // Direct API calls - scattered everywhere const response = await axios.get('/api/users') const users = response.data // Different patterns in different files fetch('/api/users').then(r => r.json()) ``` **After (Good):** ```typescript // Centralized, typed, consistent import { userService } from '@/services' const users = await userService.getUsers() ``` **Benefits:** - ✅ Single source of truth - ✅ Type safety (TypeScript) - ✅ Automatic authentication - ✅ Consistent error handling - ✅ Easy to test - ✅ Easy to maintain --- ## Available Services ### Import All Services ```typescript import { authService, // Authentication & authorization userService, // User management analyticsService, // Dashboard analytics & metrics securityService, // Security monitoring & logs systemService, // System health & maintenance announcementService,// Announcements management auditService, // Audit logs & history settingsService // System settings } from '@/services' ``` ### Service Locations ``` src/services/ ├── index.ts # Central export (import from here) ├── api/ │ └── client.ts # Shared axios instance ├── auth.service.ts # authService ├── user.service.ts # userService ├── analytics.service.ts # analyticsService ├── security.service.ts # securityService ├── system.service.ts # systemService ├── announcement.service.ts # announcementService ├── audit.service.ts # auditService └── settings.service.ts # settingsService ``` --- ## Basic Usage ### 1. Simple API Call ```typescript import { userService } from '@/services' // Async/await async function loadUsers() { const users = await userService.getUsers() console.log(users) // Typed response } // Promise userService.getUsers() .then(users => console.log(users)) .catch(error => console.error(error)) ``` ### 2. With React Query (Recommended) ```typescript import { useQuery } from '@tanstack/react-query' import { userService } from '@/services' function UsersPage() { const { data, isLoading, error } = useQuery({ queryKey: ['users'], queryFn: () => userService.getUsers() }) if (isLoading) return
Loading...
if (error) return
Error: {error.message}
return (
{data?.data.map(user => (
{user.email}
))}
) } ``` ### 3. With Parameters ```typescript import { userService } from '@/services' // Fetch with filters const users = await userService.getUsers({ page: 1, limit: 20, search: 'john', role: 'ADMIN', isActive: true }) // Single user const user = await userService.getUser('user-id-123') ``` ### 4. Mutations (Create/Update/Delete) ```typescript import { useMutation, useQueryClient } from '@tanstack/react-query' import { userService } from '@/services' import { toast } from 'sonner' function DeleteUserButton({ userId }) { const queryClient = useQueryClient() const mutation = useMutation({ mutationFn: () => userService.deleteUser(userId), onSuccess: () => { // Refresh the users list queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User deleted successfully') }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to delete user') } }) return ( ) } ``` --- ## Common Patterns ### Pattern 1: Fetching List Data ```typescript import { useQuery } from '@tanstack/react-query' import { userService } from '@/services' import { useState } from 'react' function UsersPage() { const [page, setPage] = useState(1) const [search, setSearch] = useState('') const { data, isLoading } = useQuery({ queryKey: ['users', page, search], queryFn: () => userService.getUsers({ page, limit: 20, search }) }) return (
setSearch(e.target.value)} placeholder="Search users..." /> {isLoading ? (
Loading...
) : (
{data?.data.map(user => ( ))}
)}
) } ``` ### Pattern 2: Creating New Records ```typescript import { useMutation, useQueryClient } from '@tanstack/react-query' import { userService } from '@/services' import { useState } from 'react' function CreateUserForm() { const queryClient = useQueryClient() const [formData, setFormData] = useState({ email: '', firstName: '', lastName: '', role: 'USER' }) const createMutation = useMutation({ mutationFn: (data) => userService.createUser(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User created successfully') setFormData({ email: '', firstName: '', lastName: '', role: 'USER' }) }, onError: (error: any) => { toast.error(error.response?.data?.message || 'Failed to create user') } }) const handleSubmit = (e) => { e.preventDefault() createMutation.mutate(formData) } return (
setFormData({ ...formData, email: e.target.value })} placeholder="Email" required /> {/* More fields... */}
) } ``` ### Pattern 3: Updating Records ```typescript import { useMutation, useQueryClient } from '@tanstack/react-query' import { userService } from '@/services' function UpdateUserButton({ userId, updates }) { const queryClient = useQueryClient() const updateMutation = useMutation({ mutationFn: () => userService.updateUser(userId, updates), onSuccess: () => { // Refresh both the list and the single user queryClient.invalidateQueries({ queryKey: ['users'] }) queryClient.invalidateQueries({ queryKey: ['users', userId] }) toast.success('User updated successfully') } }) return ( ) } ``` ### Pattern 4: File Upload ```typescript import { userService } from '@/services' function ImportUsersButton() { const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return try { const result = await userService.importUsers(file) toast.success(`Imported ${result.imported} users. ${result.failed} failed.`) } catch (error: any) { toast.error(error.response?.data?.message || 'Import failed') } } return ( ) } ``` ### Pattern 5: File Download ```typescript import { userService } from '@/services' function ExportUsersButton() { const handleExport = async () => { try { const blob = await userService.exportUsers('csv') // Create download link const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `users-${new Date().toISOString()}.csv` a.click() window.URL.revokeObjectURL(url) toast.success('Users exported successfully') } catch (error: any) { toast.error('Export failed') } } return ( ) } ``` --- ## Service Methods Reference ### AuthService ```typescript import { authService } from '@/services' // Login const response = await authService.login({ email: 'admin@example.com', password: 'password' }) // Returns: { accessToken, refreshToken, user } // Logout await authService.logout() // Refresh token await authService.refreshToken() // Get current user (from localStorage) const user = authService.getCurrentUser() // Check if authenticated const isAuth = authService.isAuthenticated() // Check if admin const isAdmin = authService.isAdmin() ``` ### UserService ```typescript import { userService } from '@/services' // Get paginated users const users = await userService.getUsers({ page: 1, limit: 20, search: 'john', role: 'ADMIN', isActive: true }) // Get single user const user = await userService.getUser('user-id') // Create user const newUser = await userService.createUser({ email: 'user@example.com', firstName: 'John', lastName: 'Doe', role: 'USER' }) // Update user const updated = await userService.updateUser('user-id', { isActive: false, role: 'ADMIN' }) // Delete user await userService.deleteUser('user-id', hard = false) // Reset password const result = await userService.resetPassword('user-id') // Returns: { temporaryPassword: 'abc123' } // Get user activity const activity = await userService.getUserActivity('user-id', days = 30) // Import users from CSV const result = await userService.importUsers(file) // Returns: { imported: 10, failed: 2 } // Export users to CSV const blob = await userService.exportUsers('csv') ``` ### AnalyticsService ```typescript import { analyticsService } from '@/services' // Get overview statistics const stats = await analyticsService.getOverview() // Returns: { totalUsers, activeUsers, totalRevenue, ... } // Get user growth data const growth = await analyticsService.getUserGrowth(days = 30) // Returns: [{ date: '2024-01-01', users: 100, ... }] // Get revenue data const revenue = await analyticsService.getRevenue('30days') // Options: '7days', '30days', '90days' // Get API usage const apiUsage = await analyticsService.getApiUsage(days = 7) // Get error rate const errorRate = await analyticsService.getErrorRate(days = 7) // Get storage analytics const storage = await analyticsService.getStorageAnalytics() ``` ### SecurityService ```typescript import { securityService } from '@/services' // Get suspicious activity const suspicious = await securityService.getSuspiciousActivity() // Returns: { suspiciousIPs: [...], suspiciousEmails: [...] } // Get active sessions const sessions = await securityService.getActiveSessions() // Terminate session await securityService.terminateSession('session-id') // Get failed login attempts const failedLogins = await securityService.getFailedLogins({ page: 1, limit: 50, email: 'user@example.com' }) // Get rate limit violations const violations = await securityService.getRateLimitViolations(days = 7) // Get all API keys const apiKeys = await securityService.getAllApiKeys() // Revoke API key await securityService.revokeApiKey('key-id') // Ban IP address await securityService.banIpAddress('192.168.1.1', 'Suspicious activity') ``` ### SystemService ```typescript import { systemService } from '@/services' // Get system health const health = await systemService.getHealth() // Returns: { status: 'healthy', database: 'connected', ... } // Get system info const info = await systemService.getSystemInfo() // Returns: { version, environment, memory, cpu, ... } // Get maintenance status const maintenance = await systemService.getMaintenanceStatus() // Enable maintenance mode await systemService.enableMaintenance('System upgrade in progress') // Disable maintenance mode await systemService.disableMaintenance() // Clear cache await systemService.clearCache() // Run migrations const result = await systemService.runMigrations() ``` ### AnnouncementService ```typescript import { announcementService } from '@/services' // Get all announcements const announcements = await announcementService.getAnnouncements(activeOnly = false) // Get single announcement const announcement = await announcementService.getAnnouncement('announcement-id') // Create announcement const newAnnouncement = await announcementService.createAnnouncement({ title: 'Maintenance Notice', message: 'System will be down for maintenance', type: 'warning', priority: 1, targetAudience: 'all' }) // Update announcement const updated = await announcementService.updateAnnouncement('announcement-id', { title: 'Updated Title' }) // Toggle announcement active status await announcementService.toggleAnnouncement('announcement-id') // Delete announcement await announcementService.deleteAnnouncement('announcement-id') ``` ### AuditService ```typescript import { auditService } from '@/services' // Get audit logs const logs = await auditService.getAuditLogs({ page: 1, limit: 50, userId: 'user-id', action: 'DELETE', resourceType: 'user', startDate: '2024-01-01', endDate: '2024-12-31' }) // Get audit log by ID const log = await auditService.getAuditLog('log-id') // Get user audit activity const activity = await auditService.getUserAuditActivity('user-id', days = 30) // Get resource history const history = await auditService.getResourceHistory('user', 'resource-id') // Get audit statistics const stats = await auditService.getAuditStats(startDate, endDate) // Export audit logs const blob = await auditService.exportAuditLogs({ format: 'csv', startDate: '2024-01-01', endDate: '2024-12-31' }) ``` ### SettingsService ```typescript import { settingsService } from '@/services' // Get all settings const settings = await settingsService.getSettings(category = 'GENERAL') // Get single setting const setting = await settingsService.getSetting('feature_flag') // Create setting const newSetting = await settingsService.createSetting({ key: 'feature_flag', value: 'true', category: 'FEATURES', description: 'Enable new feature', isPublic: false }) // Update setting const updated = await settingsService.updateSetting('feature_flag', { value: 'false' }) // Delete setting await settingsService.deleteSetting('feature_flag') // Get public settings (for frontend use) const publicSettings = await settingsService.getPublicSettings() ``` --- ## Error Handling ### Standard Error Pattern All services throw errors with consistent structure: ```typescript try { await userService.deleteUser(userId) toast.success('User deleted') } catch (error: any) { // Error structure: // error.response.status - HTTP status code // error.response.data.message - Error message from backend const message = error.response?.data?.message || 'Operation failed' toast.error(message) } ``` ### Common HTTP Status Codes ```typescript // 400 - Bad Request (validation error) catch (error: any) { if (error.response?.status === 400) { toast.error('Invalid input: ' + error.response.data.message) } } // 401 - Unauthorized (handled automatically by interceptor) // Token refresh attempted automatically // If refresh fails, user redirected to login // 403 - Forbidden (no permission) catch (error: any) { if (error.response?.status === 403) { toast.error('You do not have permission to perform this action') } } // 404 - Not Found catch (error: any) { if (error.response?.status === 404) { toast.error('Resource not found') } } // 500 - Server Error catch (error: any) { if (error.response?.status === 500) { toast.error('Server error. Please try again later.') } } ``` ### React Query Error Handling ```typescript const { data, error } = useQuery({ queryKey: ['users'], queryFn: () => userService.getUsers(), retry: 3, // Retry failed requests retryDelay: 1000, // Wait 1s between retries }) // Display error if (error) { return
Error: {error.message}
} ``` --- ## Best Practices ### ✅ DO: Use Services ```typescript // Good import { userService } from '@/services' const users = await userService.getUsers() ``` ### ❌ DON'T: Direct API Calls ```typescript // Bad - don't do this import axios from 'axios' const response = await axios.get('/api/users') ``` ### ✅ DO: Use React Query ```typescript // Good - caching, loading states, error handling const { data, isLoading, error } = useQuery({ queryKey: ['users'], queryFn: () => userService.getUsers() }) ``` ### ❌ DON'T: Manual State Management ```typescript // Bad - manual state management const [users, setUsers] = useState([]) const [loading, setLoading] = useState(false) useEffect(() => { setLoading(true) userService.getUsers() .then(setUsers) .finally(() => setLoading(false)) }, []) ``` ### ✅ DO: Specific Query Keys ```typescript // Good - specific, cacheable queryKey: ['users', page, limit, search] ``` ### ❌ DON'T: Generic Query Keys ```typescript // Bad - too generic queryKey: ['data'] ``` ### ✅ DO: Invalidate After Mutations ```typescript // Good - refresh data after changes const mutation = useMutation({ mutationFn: userService.deleteUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) } }) ``` ### ✅ DO: Handle Errors ```typescript // Good - user feedback try { await userService.deleteUser(id) toast.success('User deleted') } catch (error: any) { toast.error(error.response?.data?.message) } ``` ### ❌ DON'T: Ignore Errors ```typescript // Bad - no error handling await userService.deleteUser(id) ``` --- ## Examples ### Complete CRUD Example ```typescript import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { userService } from '@/services' import { toast } from 'sonner' import { useState } from 'react' function UsersManagement() { const queryClient = useQueryClient() const [page, setPage] = useState(1) const [editingUser, setEditingUser] = useState(null) // READ - Fetch users const { data: users, isLoading } = useQuery({ queryKey: ['users', page], queryFn: () => userService.getUsers({ page, limit: 20 }) }) // CREATE - Add new user const createMutation = useMutation({ mutationFn: (data) => userService.createUser(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User created') } }) // UPDATE - Edit user const updateMutation = useMutation({ mutationFn: ({ id, data }) => userService.updateUser(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) setEditingUser(null) toast.success('User updated') } }) // DELETE - Remove user const deleteMutation = useMutation({ mutationFn: (id) => userService.deleteUser(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User deleted') } }) if (isLoading) return
Loading...
return (

Users Management

{/* User List */} {users?.data.map(user => ( ))}
Email Name Role Actions
{user.email} {user.firstName} {user.lastName} {user.role}
{/* Pagination */}
Page {page} of {users?.totalPages}
) } ``` --- ## Summary ### Key Takeaways 1. **Always use services** - Never make direct API calls 2. **Use React Query** - For data fetching and caching 3. **Handle errors** - Provide user feedback 4. **Invalidate queries** - After mutations 5. **Use specific query keys** - For better caching ### Quick Reference ```typescript // Import import { userService } from '@/services' // Fetch const { data } = useQuery({ queryKey: ['users'], queryFn: () => userService.getUsers() }) // Mutate const mutation = useMutation({ mutationFn: (id) => userService.deleteUser(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('Success') } }) // Error handling try { await userService.deleteUser(id) } catch (error: any) { toast.error(error.response?.data?.message) } ``` --- **For more information:** - [Development Guide](./DEVELOPMENT.md) - General development practices - [Testing Guide](./TESTING_GUIDE.md) - How to test services - [Security Guide](./SECURITY.md) - Security best practices