API Gateway Integration Guide ‐ Frontend Integration - Wiz-DevTech/prettygirllz GitHub Wiki
This document provides comprehensive integration guidance for frontend applications consuming the WizDevTech API Gateway services. It covers REST API design, authentication patterns, data contracts, error handling, and performance optimization strategies.
-
Base URL:
http://localhost:8080
-
Content-Type:
application/json
- Protocol: HTTP/HTTPS
- HTTP Methods: GET, POST, PUT, DELETE (as appropriate)
GET /health
Purpose: Verify system and database connectivity
Response: 200 OK
"DB connection successful!"
Error Response: 500 Internal Server Error
"DB connection failed: <error message>"
GET /api/test
Purpose: Verify API gateway functionality
Response: 200 OK
"API is working! Current time: 2025-01-15T10:30:00Z"
GET /api/products/{id}
Parameters:
-
id
(path): Product identifier
Response: 200 OK
{
"name": "Sample Product",
"id": "product-123",
"... other product properties"
}
Cache Behavior:
- First request: Cache miss, generates response
- Subsequent requests: Cache hit (1-hour TTL)
- Headers include cache status information
Error Responses:
404 Not Found: Product not found
500 Internal Server Error: {"error": "Error message"}
GET /api/fallback/{route}
Parameters:
-
route
(path): Route path (e.g., 'homepage', 'category/electronics')
Response: 200 OK
<!DOCTYPE html>
<html>
<!-- Cached HTML content -->
</html>
Content-Type: text/html
Error Responses:
404 Not Found: <div>No cached version</div>
500 Internal Server Error: <div>Server Error</div>
{
"data": {
// Response payload
},
"metadata": {
"timestamp": "2025-01-15T10:30:00Z",
"cached": true,
"cacheExpiry": "2025-01-15T11:30:00Z"
}
}
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
// Additional error context
}
},
"timestamp": "2025-01-15T10:30:00Z",
"path": "/api/products/123"
}
- Use
camelCase
for JSON properties - Datetime fields in ISO 8601 format
- Consistent boolean representations (true/false)
- Null values explicitly represented
The current system does not implement authentication. For production deployment, consider:
// Frontend implementation example
const apiCall = async (endpoint, options = {}) => {
const token = localStorage.getItem('authToken');
return fetch(`${API_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...options.headers
}
});
};
// Add these headers for production
{
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': getCsrfToken(),
'Content-Security-Policy': "default-src 'self'"
}
Configure CORS for frontend origins:
// Add to WebConfig
@CrossOrigin(origins = {"http://localhost:3000", "https://yourdomain.com"})
// Frontend error handling
const handleApiCall = async (endpoint) => {
try {
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error instanceof TypeError) {
// Network error
showError('Network error. Please check your connection.');
} else {
// HTTP error
showError(`API error: ${error.message}`);
}
}
};
// Implement timeout logic
const fetchWithTimeout = (url, options = {}, timeout = 5000) => {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
};
// Exponential backoff retry
const retryApiCall = async (fn, retries = 3, delay = 1000) => {
try {
return await fn();
} catch (error) {
if (retries > 0 && isRetryableError(error)) {
await sleep(delay);
return retryApiCall(fn, retries - 1, delay * 2);
}
throw error;
}
};
Best for immediate data retrieval:
// Product data fetching
const getProduct = async (productId) => {
const response = await fetch(`/api/products/${productId}`);
return response.json();
};
Leverage cache for improved performance:
// Check cache, fallback to API
const getCachedProduct = async (productId) => {
// Check local cache first
const cached = sessionStorage.getItem(`product-${productId}`);
if (cached) {
const { data, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp < 300000) { // 5 minutes
return data;
}
}
// Fetch from API
const product = await getProduct(productId);
sessionStorage.setItem(`product-${productId}`, JSON.stringify({
data: product,
timestamp: Date.now()
}));
return product;
};
// SSR fallback implementation
const getSsrContent = async (route) => {
try {
const response = await fetch(`/api/fallback/${route}`);
if (response.ok) {
return response.text();
}
// Fallback to client-side rendering
return renderClientSide(route);
} catch (error) {
// Graceful degradation
return renderErrorFallback();
}
};
// Implement browser caching
const setupCacheHeaders = () => {
// Set HTTP cache headers
return {
'Cache-Control': 'public, max-age=300', // 5 minutes
'ETag': generateETag(content)
};
};
// Cache API responses in service worker
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/products/')) {
event.respondWith(
caches.open('api-cache').then(cache => {
return cache.match(event.request).then(response => {
if (response && !isExpired(response)) {
return response;
}
return fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});
// Batch multiple product requests
const getBatchProducts = async (productIds) => {
// If API supports batch operations
return fetch('/api/products/batch', {
method: 'POST',
body: JSON.stringify({ ids: productIds })
});
};
// Handle paginated responses
const getPaginatedData = async (endpoint, page = 1, size = 20) => {
const response = await fetch(`${endpoint}?page=${page}&size=${size}`);
return response.json();
};
Enable gzip compression on the server and handle in frontend:
// Check if response is compressed
const isCompressed = response.headers.get('content-encoding') === 'gzip';
# Test health endpoint
curl -X GET http://localhost:8080/health
# Test product endpoint
curl -X GET http://localhost:8080/api/products/123
# Test SSR fallback
curl -X GET http://localhost:8080/api/fallback/homepage
// Jest test example
describe('API Integration', () => {
test('should fetch product data', async () => {
const mockProduct = { id: '123', name: 'Test Product' };
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockProduct)
})
);
const product = await getProduct('123');
expect(product).toEqual(mockProduct);
expect(fetch).toHaveBeenCalledWith('/api/products/123');
});
});
// Development mock server
const mockServer = setupServer(
rest.get('/api/products/:id', (req, res, ctx) => {
return res(
ctx.json({ id: req.params.id, name: 'Mock Product' })
);
})
);
// React hook for API integration
const useProduct = (productId) => {
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchProduct = async () => {
try {
setLoading(true);
const data = await getProduct(productId);
setProduct(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchProduct();
}, [productId]);
return { product, loading, error };
};
// Vue composable for API calls
export function useApi() {
const loading = ref(false);
const error = ref(null);
const execute = async (apiCall) => {
loading.value = true;
error.value = null;
try {
const result = await apiCall();
return result;
} catch (err) {
error.value = err.message;
throw err;
} finally {
loading.value = false;
}
};
return { loading, error, execute };
}
// Angular service
@Injectable({
providedIn: 'root'
})
export class ApiService {
private readonly apiUrl = 'http://localhost:8080/api';
constructor(private http: HttpClient) {}
getProduct(id: string): Observable<Product> {
return this.http.get<Product>(`${this.apiUrl}/products/${id}`)
.pipe(
retry(3),
catchError(this.handleError)
);
}
private handleError(error: HttpErrorResponse) {
console.error('API error:', error);
return throwError(() => error);
}
}
// Frontend input validation
const validateProductId = (id) => {
if (!id || typeof id !== 'string' || id.length === 0) {
throw new Error('Invalid product ID');
}
return id.replace(/[^a-zA-Z0-9-]/g, '');
};
// Sanitize HTML content from SSR endpoint
const sanitizeHtml = (html) => {
const div = document.createElement('div');
div.innerHTML = html;
// Remove script tags and dangerous attributes
removeScriptTags(div);
return div.innerHTML;
};
// Environment-based API configuration
const API_CONFIG = {
development: {
baseUrl: 'http://localhost:8080',
timeout: 10000
},
staging: {
baseUrl: 'https://api-staging.yourdomain.com',
timeout: 5000
},
production: {
baseUrl: 'https://api.yourdomain.com',
timeout: 5000
}
};
Consider CDN for static asset delivery and API response caching:
// CDN cache headers for static content
const cacheHeaders = {
'Cache-Control': 'public, max-age=31536000',
'Expires': new Date(Date.now() + 31536000000).toUTCString()
};
// Track API performance
const trackApiCall = (endpoint, startTime, statusCode) => {
const duration = Date.now() - startTime;
analytics.track('api_call', {
endpoint,
duration,
statusCode,
timestamp: new Date().toISOString()
});
};
// Error reporting integration
const reportError = (error, context) => {
errorTracker.captureException(error, {
tags: { component: 'api-integration' },
extra: context
});
};
- Implement proper error handling and retry logic
- Use appropriate caching strategies
- Validate user inputs on frontend
- Monitor API performance and errors
- Implement graceful degradation
- Use TypeScript for better type safety
- Don't hard-code API URLs
- Don't ignore error responses
- Don't cache sensitive data client-side
- Don't perform blocking operations on main thread
- Don't trust backend data without validation
- Initial API implementation
- Basic caching system
- SSR fallback support
- Authentication/authorization integration
- Rate limiting implementation
- WebSocket support for real-time updates
- OpenAPI/Swagger documentation
- API versioning strategy