Error Handling Best Practices
Patterns and strategies for handling Geog API errors gracefully in your applications.
Client Implementation
Retry Wrapper with Error Handling
// Helper function for delays
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Token management (implement based on your auth system)
let token: string;
async function refreshToken() {
/* ... */
}
async function apiRequest(url: string, options?: RequestInit, retryCount = 0) {
const MAX_RETRIES = 3;
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json();
switch (error.code) {
case "TOKEN_EXPIRED":
// Refresh token and retry (with retry limit)
if (retryCount < MAX_RETRIES) {
await refreshToken();
return apiRequest(url, options, retryCount + 1);
}
throw new ApiError(error);
case "RATE_LIMIT_EXCEEDED":
// Wait and retry with backoff (with retry limit)
if (retryCount < MAX_RETRIES) {
await sleep(error.retryAfter * 1000);
return apiRequest(url, options, retryCount + 1);
}
throw new ApiError(error);
case "VALIDATION_ERROR":
// Handle validation errors
throw new ValidationError(error.details);
default:
throw new ApiError(error);
}
}
return response.json();
}
Logging Errors
Include the error code and message in logs for debugging:
try {
await apiRequest("/v1/tiles/tileset/14/4680/6125.mvt");
} catch (error) {
const { code, message } = error as ApiError;
console.error(`API Error [${code}]: ${message}`);
}
User-Facing Messages
Map error codes to user-friendly messages:
const userMessages = {
AUTH_REQUIRED: "Please log in to continue.",
TOKEN_EXPIRED: "Your session has expired. Please log in again.",
INSUFFICIENT_SCOPE: "You do not have permission to access this resource.",
RATE_LIMIT_EXCEEDED: "Too many requests. Please wait a moment.",
};
Handling Rate Limits
Free Tier Limit Handling
async function fetchTile(url: string, token: string) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
});
if (response.status === 429) {
const data = await response.json();
const resetDate = new Date(data.reset);
// Show user when limits reset
console.warn(
`Rate limit exceeded. Resets at ${resetDate.toLocaleString()}`,
);
// Consider upgrading
showUpgradePrompt();
return null;
}
return response;
}
Tips for free tier limits:
- Monitor rate limit headers: Check
X-RateLimit-<Type>-Remainingbefore making requests - Cache aggressively: Free tier limits are generous but caching reduces requests
- Query usage endpoint: Monitor your usage throughout the day to avoid surprises
Exponential Backoff
// Helper function for delays
const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
async function fetchWithRetry(url: string, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const retryAfter = parseInt(
response.headers.get("Retry-After") || "60",
10,
);
await sleep(retryAfter * 1000 * Math.pow(2, i));
continue;
}
return response;
}
throw new Error("Rate limit exceeded after retries");
}
Optimizing Usage
- Cache responses: Vector tiles are cacheable for 24 hours. Implement client-side caching.
- Use appropriate zoom levels: Request only the zoom levels needed for your use case.
- Implement request deduplication: Avoid duplicate requests for the same resource.
See Also
- Error Codes Reference - Complete error code listing
- Rate Limiting Reference - Rate limit configuration and headers
- API Best Practices - General API usage best practices