# 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 (
)
}
```
### 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 */}
| Email |
Name |
Role |
Actions |
{users?.data.map(user => (
| {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