Best Practices
Optimize your integration with these production-tested patterns.
๐ Performance Optimizationโ
Batch Requests When Possibleโ
Many endpoints support batch operations. Use them to reduce round trips:
// โ Slow: Multiple requests
const prices = await Promise.all([
fetch('https://api.web3identity.com/api/price/eth'),
fetch('https://api.web3identity.com/api/price/btc'),
fetch('https://api.web3identity.com/api/price/usdc')
]);
// โ
Fast: Single batch request
const prices = await fetch(
'https://api.web3identity.com/api/price/batch?symbols=eth,btc,usdc'
);
Use Appropriate Endpointsโ
Choose the right endpoint for your data needs:
| Need | Use | Not |
|---|---|---|
| Just ENS address | /api/ens/resolve/:name | /api/ens/profile/:name |
| Full profile | /api/ens/profile/:name | Multiple single-field calls |
| Multiple prices | /api/price/batch | Individual /api/price/:token |
| Wallet overview | /api/wallet/:address | Separate balance + NFT + tx calls |
๐พ Caching Strategiesโ
Cache Duration by Data Typeโ
Different data has different freshness requirements:
| Data Type | Recommended TTL | Example |
|---|---|---|
| ENS resolution | 5-15 minutes | Address lookups |
| ENS profiles | 1-6 hours | Avatar, bio, socials |
| Token prices | 30-60 seconds | Real-time pricing |
| TVL/DeFi stats | 5-15 minutes | Protocol analytics |
| NFT metadata | 24 hours | Collection info |
| Gas prices | 15-30 seconds | Transaction estimation |
| Historical data | 24+ hours | Past transactions |
Implementation Exampleโ
const cache = new Map();
async function cachedFetch(url, ttlSeconds) {
const cached = cache.get(url);
if (cached && Date.now() - cached.time < ttlSeconds * 1000) {
return cached.data;
}
const response = await fetch(url);
const data = await response.json();
cache.set(url, { data, time: Date.now() });
return data;
}
// Usage with appropriate TTLs
const ensProfile = await cachedFetch(
'https://api.web3identity.com/api/ens/profile/vitalik.eth',
3600 // 1 hour
);
const ethPrice = await cachedFetch(
'https://api.web3identity.com/api/price/eth',
30 // 30 seconds
);
Stale-While-Revalidate Patternโ
For better UX, serve stale data while fetching fresh:
async function swr(url, ttl, staleTTL) {
const cached = cache.get(url);
const now = Date.now();
// Fresh cache - return immediately
if (cached && now - cached.time < ttl * 1000) {
return cached.data;
}
// Stale but usable - return stale, refresh in background
if (cached && now - cached.time < staleTTL * 1000) {
fetch(url).then(r => r.json()).then(data => {
cache.set(url, { data, time: Date.now() });
});
return cached.data;
}
// Too stale - must fetch fresh
const data = await fetch(url).then(r => r.json());
cache.set(url, { data, time: now });
return data;
}
๐ Authentication Best Practicesโ
SIWE Token Managementโ
// Store token securely
const siweToken = await authenticate();
sessionStorage.setItem('siwe_token', siweToken); // โ
Session only
// localStorage.setItem('siwe_token', siweToken); // โ Persists too long
// Include in requests
const headers = {
'Authorization': `Bearer ${sessionStorage.getItem('siwe_token')}`
};
API Key Securityโ
// โ
Server-side only
// .env file (never commit!)
API_KEY=your_api_key_here
// โ Never in client-side code
const API_KEY = 'sk_live_...'; // Exposed to users!
Rotate Credentials Regularlyโ
- API Keys: Rotate every 90 days
- SIWE Tokens: Re-authenticate after 24 hours
- Monitor usage: Check for unexpected spikes
โก Rate Limit Managementโ
Implement Exponential Backoffโ
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
Spread Requests Over Timeโ
// โ Burst: All at once
const results = await Promise.all(
addresses.map(addr => fetch(`/api/wallet/${addr}`))
);
// โ
Throttled: Spread over time
async function throttledFetch(urls, requestsPerSecond = 10) {
const results = [];
const delay = 1000 / requestsPerSecond;
for (const url of urls) {
results.push(await fetch(url));
await new Promise(r => setTimeout(r, delay));
}
return results;
}
Track Your Usageโ
let requestCount = 0;
const resetTime = Date.now() + 86400000; // 24 hours
function trackRequest() {
requestCount++;
if (requestCount > 80) { // 80% of 100 free tier
console.warn(`Approaching rate limit: ${requestCount}/100`);
}
}
๐ก๏ธ Error Handlingโ
Handle All Response Typesโ
async function apiCall(endpoint) {
const response = await fetch(`https://api.web3identity.com${endpoint}`);
switch (response.status) {
case 200:
return { success: true, data: await response.json() };
case 400:
const error = await response.json();
return { success: false, error: error.message };
case 402:
// Payment required - handle x402
const paymentInfo = await response.json();
return { success: false, paymentRequired: true, ...paymentInfo };
case 404:
return { success: false, error: 'Resource not found' };
case 429:
return {
success: false,
rateLimited: true,
retryAfter: response.headers.get('Retry-After')
};
case 500:
return { success: false, error: 'Server error - try again later' };
default:
return { success: false, error: `Unexpected status: ${response.status}` };
}
}
Graceful Degradationโ
async function getENSProfile(name) {
try {
const response = await fetch(
`https://api.web3identity.com/api/ens/profile/${name}`
);
if (!response.ok) throw new Error('API unavailable');
return await response.json();
} catch (error) {
// Fallback: Try basic resolution only
try {
const basic = await fetch(
`https://api.web3identity.com/api/ens/resolve/${name}`
);
return { address: await basic.json().address, partial: true };
} catch {
// Final fallback: Return null, let UI handle
return null;
}
}
}
๐ Monitoring & Observabilityโ
Log API Interactionsโ
async function instrumentedFetch(url, options = {}) {
const start = Date.now();
const requestId = crypto.randomUUID();
console.log(`[${requestId}] โ ${url}`);
try {
const response = await fetch(url, options);
const duration = Date.now() - start;
console.log(`[${requestId}] โ ${response.status} (${duration}ms)`);
// Track metrics
metrics.record({
endpoint: new URL(url).pathname,
status: response.status,
duration,
timestamp: new Date().toISOString()
});
return response;
} catch (error) {
console.error(`[${requestId}] โ ${error.message}`);
throw error;
}
}
Set Up Alertsโ
Monitor for:
- Error rate > 5% of requests
- Latency > 2 seconds average
- 402 responses (budget/quota issues)
- 429 responses (rate limiting)
๐ Data Consistencyโ
Handle Blockchain Reorgsโ
Recent blockchain data can change. For critical operations:
// Wait for finality before considering data permanent
async function getConfirmedTransaction(txHash) {
const tx = await fetch(
`https://api.web3identity.com/api/tx/${txHash}`
).then(r => r.json());
// Ethereum: ~15 blocks for finality
if (tx.confirmations < 15) {
return { ...tx, finalized: false, warning: 'May reorg' };
}
return { ...tx, finalized: true };
}
Validate Addresses Client-Sideโ
function isValidAddress(address) {
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
function isValidENS(name) {
return name.endsWith('.eth') && name.length > 4;
}
// Validate before API call
if (!isValidAddress(userInput) && !isValidENS(userInput)) {
throw new Error('Invalid address or ENS name');
}
๐ฑ Mobile Optimizationโ
Reduce Payload Sizeโ
// Request only needed fields when available
const profile = await fetch(
'https://api.web3identity.com/api/ens/profile/vitalik.eth?fields=address,avatar'
);
Handle Offline Gracefullyโ
async function fetchWithOfflineSupport(url) {
if (!navigator.onLine) {
const cached = await caches.match(url);
if (cached) return cached;
throw new Error('Offline and no cache available');
}
const response = await fetch(url);
const cache = await caches.open('api-cache');
cache.put(url, response.clone());
return response;
}
Next Stepsโ
- Rate Limits โ Understand limits and quotas
- Error Handling โ Complete error reference
- Testing Guide โ Development environment setup
- x402 Payments โ Scale beyond free tier