Chapter 10: Performance Optimization and Best Practices
9/1/25About 3 min
Chapter 10: Performance Optimization and Best Practices
Learning Objectives
- Master request caching mechanisms
- Learn data compression and optimization
- Implement connection pooling and reuse
- Master performance monitoring methods
- Understand best practices and standards
10.1 Request Caching
In-Memory Cache Implementation
class RequestCache {
constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl;
}
generateKey(config) {
const { method, url, params, data } = config;
return `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`;
}
get(config) {
const key = this.generateKey(config);
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return cached.data;
}
set(config, data) {
const key = this.generateKey(config);
// If the cache is full, delete the oldest entry
if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
clear() {
this.cache.clear();
}
}
// Using a cached axios instance
const cache = new RequestCache();
const cachedAxios = axios.create();
cachedAxios.interceptors.request.use(config => {
// Only cache GET requests
if (config.method === 'get') {
const cached = cache.get(config);
if (cached) {
console.log('Using cached data');
return Promise.resolve({ data: cached, status: 200, cached: true });
}
}
return config;
});
cachedAxios.interceptors.response.use(response => {
if (response.config.method === 'get' && !response.cached) {
cache.set(response.config, response.data);
}
return response;
});
10.2 Data Compression
Request Data Compression
// Using a compression library for data compression
const pako = require('pako'); // or other compression libraries
class CompressionHandler {
static compress(data) {
const jsonString = JSON.stringify(data);
const compressed = pako.gzip(jsonString);
return compressed;
}
static decompress(compressed) {
const decompressed = pako.ungzip(compressed, { to: 'string' });
return JSON.parse(decompressed);
}
static shouldCompress(data, threshold = 1024) {
const size = JSON.stringify(data).length;
return size > threshold;
}
}
// Compression interceptor
axios.interceptors.request.use(config => {
if (config.data && CompressionHandler.shouldCompress(config.data)) {
config.data = CompressionHandler.compress(config.data);
config.headers['Content-Encoding'] = 'gzip';
config.headers['Content-Type'] = 'application/octet-stream';
}
return config;
});
10.3 Connection Pool Optimization
HTTP Agent Configuration (Node.js)
const http = require('http');
const https = require('https');
// Configure connection pool
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000
});
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000
});
const optimizedAxios = axios.create({
httpAgent,
httpsAgent,
timeout: 30000
});
10.4 Performance Monitoring
Request Performance Monitoring
class PerformanceMonitor {
constructor() {
this.metrics = {
requests: 0,
responses: 0,
errors: 0,
totalTime: 0,
avgResponseTime: 0
};
}
startTimer(config) {
config.metadata = {
startTime: performance.now()
};
}
endTimer(response) {
const endTime = performance.now();
const startTime = response.config.metadata?.startTime;
if (startTime) {
const duration = endTime - startTime;
this.recordMetric(duration);
}
}
recordMetric(duration) {
this.metrics.requests++;
this.metrics.totalTime += duration;
this.metrics.avgResponseTime = this.metrics.totalTime / this.metrics.requests;
}
recordError() {
this.metrics.errors++;
}
getStats() {
return {
...this.metrics,
errorRate: this.metrics.errors / this.metrics.requests,
successRate: (this.metrics.requests - this.metrics.errors) / this.metrics.requests
};
}
}
const monitor = new PerformanceMonitor();
// Performance monitoring interceptor
axios.interceptors.request.use(config => {
monitor.startTimer(config);
return config;
});
axios.interceptors.response.use(
response => {
monitor.endTimer(response);
return response;
},
error => {
monitor.recordError();
if (error.config) {
monitor.endTimer({ config: error.config });
}
return Promise.reject(error);
}
);
10.5 Best Practices
API Client Design Principles
class BestPracticesClient {
constructor(config = {}) {
this.client = axios.create({
// Basic configuration
baseURL: config.baseURL,
timeout: config.timeout || 10000,
// Security headers
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
// Status validation
validateStatus: (status) => status >= 200 && status < 300,
// Automatic retry configuration
retry: 3,
retryDelay: 1000
});
this.setupInterceptors();
this.setupCache();
this.setupMonitoring();
}
setupInterceptors() {
// Request interceptor
this.client.interceptors.request.use(
this.handleRequest.bind(this),
this.handleRequestError.bind(this)
);
// Response interceptor
this.client.interceptors.response.use(
this.handleResponse.bind(this),
this.handleResponseError.bind(this)
);
}
handleRequest(config) {
// Add request ID
config.headers['X-Request-ID'] = this.generateRequestId();
// Add timestamp
config.metadata = { startTime: Date.now() };
return config;
}
handleResponse(response) {
// Record response time
const duration = Date.now() - response.config.metadata.startTime;
console.log(`Request complete: ${duration}ms`);
return response;
}
handleResponseError(error) {
// Unified error handling
const errorInfo = {
url: error.config?.url,
method: error.config?.method,
status: error.response?.status,
message: error.message
};
console.error('Request failed:', errorInfo);
return Promise.reject(error);
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
10.6 Code Standards
API Interface Encapsulation Standards
// Good API encapsulation example
class UserService {
constructor(apiClient) {
this.api = apiClient;
this.basePath = '/api/v1/users';
}
// Get user list
async getUsers(params = {}) {
const response = await this.api.get(this.basePath, { params });
return response.data;
}
// Get single user
async getUser(id) {
if (!id) throw new Error('User ID is required');
const response = await this.api.get(`${this.basePath}/${id}`);
return response.data;
}
// Create user
async createUser(userData) {
this.validateUserData(userData);
const response = await this.api.post(this.basePath, userData);
return response.data;
}
// Update user
async updateUser(id, userData) {
if (!id) throw new Error('User ID is required');
this.validateUserData(userData, false);
const response = await this.api.put(`${this.basePath}/${id}`, userData);
return response.data;
}
// Delete user
async deleteUser(id) {
if (!id) throw new Error('User ID is required');
await this.api.delete(`${this.basePath}/${id}`);
return true;
}
// Data validation
validateUserData(data, isCreate = true) {
if (isCreate && (!data.name || !data.email)) {
throw new Error('Username and email are required');
}
if (data.email && !this.isValidEmail(data.email)) {
throw new Error('Invalid email format');
}
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
}
Chapter Summary
- Caching mechanisms significantly improve the performance of repeated requests
- Data compression reduces network transmission overhead
- Connection pool optimization improves concurrent processing capabilities
- Performance monitoring helps identify and resolve bottlenecks
- Following best practices improves code quality and maintainability
Key Takeaways
- Use caching reasonably to balance performance and data freshness
- Compressing large data reduces transmission time
- Connection reuse reduces connection establishment overhead
- Continuous monitoring identifies performance issues
- Standardized API encapsulation improves development efficiency