Common Solutions & Patterns
Proven patterns and code examples for handling common API challenges.
Retry Logic Patterns
Exponential Backoff
async function sendWithRetry(messageData, maxRetries = 3) {
let attempt = 0;
while (attempt < maxRetries) {
try {
const result = await sendMessage(messageData);
return result;
} catch (error) {
attempt++;
// Don't retry authentication errors
if (error.message.includes('Data is not valid') ||
error.message.includes('not authorized')) {
throw error;
}
// Exponential backoff for server errors
if (error.status >= 500 || error.message.includes('Service unavailable')) {
if (attempt < maxRetries) {
const backoffTime = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
await new Promise(resolve => setTimeout(resolve, backoffTime));
continue;
}
}
// Don't retry client errors (4xx)
throw error;
}
}
throw new Error(`Failed after ${maxRetries} attempts`);
}
Rate Limiting Handler
class RateLimitedClient {
constructor(requestsPerSecond = 10) {
this.requestsPerSecond = requestsPerSecond;
this.lastRequestTime = 0;
this.requestQueue = [];
this.processing = false;
}
async sendMessage(messageData) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ messageData, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.requestQueue.length === 0) {
return;
}
this.processing = true;
while (this.requestQueue.length > 0) {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
const minInterval = 1000 / this.requestsPerSecond;
if (timeSinceLastRequest < minInterval) {
await new Promise(resolve =>
setTimeout(resolve, minInterval - timeSinceLastRequest)
);
}
const { messageData, resolve, reject } = this.requestQueue.shift();
this.lastRequestTime = Date.now();
try {
const result = await this.sendMessageDirect(messageData);
resolve(result);
} catch (error) {
reject(error);
}
}
this.processing = false;
}
async sendMessageDirect(messageData) {
// Direct API call implementation
const response = await fetch('https://m5api.groupcall.com/api/SendMessage_V3/SMS', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messageData: [messageData] })
});
return await response.json();
}
}
// Usage
const client = new RateLimitedClient(5); // 5 requests per second
const result = await client.sendMessage(messageData);
Response Validation
Comprehensive Response Handler
class ResponseValidator {
static validate(response, httpStatus) {
// Handle HTTP-level errors
if (httpStatus >= 500) {
throw new ApiError('Server error occurred', 'SERVER_ERROR', httpStatus);
}
if (httpStatus >= 400 && httpStatus < 500) {
throw new ApiError('Client error occurred', 'CLIENT_ERROR', httpStatus);
}
// Handle API response format
if (!Array.isArray(response)) {
throw new ApiError('Invalid response format', 'INVALID_FORMAT');
}
if (response.length === 0) {
throw new ApiError('Empty response received', 'EMPTY_RESPONSE');
}
const result = response[0];
// Handle null response
if (result === null) {
throw new ApiError('Null response received', 'NULL_RESPONSE');
}
// Check for success
if (result.errorMsg &&
result.errorMsg.startsWith('OK') &&
result.MessageId) {
return {
success: true,
messageId: result.MessageId,
status: result.statusMsg,
transmitDateTime: result.transmitDateTime,
warnings: result.WarningMessages || []
};
}
// Handle error conditions
const errorMsg = result.errorMsg ||
(result.WarningMessages && result.WarningMessages[0]) ||
'Unknown error';
// Categorize errors
if (errorMsg.includes('Data is not valid')) {
throw new ApiError(errorMsg, 'AUTH_ERROR', result);
}
if (errorMsg.includes('No recipients')) {
throw new ApiError(errorMsg, 'INVALID_RECIPIENTS', result);
}
if (errorMsg.includes('Rate limit') || errorMsg.includes('Too many')) {
throw new ApiError(errorMsg, 'RATE_LIMITED', result);
}
throw new ApiError(errorMsg, 'API_ERROR', result);
}
}
class ApiError extends Error {
constructor(message, code, data) {
super(message);
this.name = 'ApiError';
this.code = code;
this.data = data;
}
}
// Usage
try {
const response = await fetch(apiUrl, options);
const data = await response.json();
const result = ResponseValidator.validate(data, response.status);
console.log('Message sent:', result);
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error (${error.code}):`, error.message);
// Handle specific error types
switch (error.code) {
case 'AUTH_ERROR':
// Handle authentication issues
break;
case 'RATE_LIMITED':
// Implement backoff
break;
// ... other cases
}
} else {
console.error('Unexpected error:', error);
}
}