Integration Patterns
This guide covers common integration patterns and architectural approaches for building applications with the NUVA Labs APIs.
Overview
Understanding integration patterns helps you:
- Choose the right architecture for your use case
- Implement scalable and maintainable solutions
- Handle common challenges and edge cases
- Optimize performance and costs
Common Patterns
1. Real-time Data Streaming
For applications that need live updates, use WebSocket connections combined with REST APIs.
Architecture:
Application → WebSocket API (real-time updates)
→ REST APIs (historical data, operations)
Example Use Cases:
- Trading dashboards
- Real-time portfolio tracking
- Live transaction monitoring
Implementation:
// Connect to WebSocket for real-time data
const ws = new WebSocket('wss://api.provlabs.com/ws');
ws.subscribe('account_balance', { address: userAddress });
ws.subscribe('transactions', { address: userAddress });
// Use REST API for operations
const transfer = await fetch('/api/transfers', {
method: 'POST',
body: JSON.stringify(transferData),
});
2. Batch Processing
For applications processing large volumes of data, implement batch operations.
Architecture:
Batch Job → Queue → Worker → NUVA Labs APIs
Example Use Cases:
- Bulk asset transfers
- Historical data processing
- Report generation
Implementation:
// Process transfers in batches
async function processBatchTransfers(transfers) {
const batchSize = 10;
const batches = chunk(transfers, batchSize);
for (const batch of batches) {
await Promise.all(batch.map((transfer) => createTransfer(transfer)));
// Rate limiting
await delay(1000);
}
}
3. Event-Driven Architecture
Use webhooks and event streams to build reactive applications.
Architecture:
NUVA Labs APIs → Webhook → Event Bus → Application Services
Example Use Cases:
- Automated trading systems
- Compliance monitoring
- Notification systems
Implementation:
// Webhook handler
app.post('/webhook/transfer-completed', (req, res) => {
const { transferId, status } = req.body;
if (status === 'completed') {
eventBus.emit('transfer.completed', { transferId });
}
res.status(200).send('OK');
});
4. Caching Strategy
Implement intelligent caching to reduce API calls and improve performance.
Architecture:
Application → Cache Layer → NUVA Labs APIs
Cache Types:
- Account Data: Cache for 5-10 minutes
- Market Data: Cache for 30 seconds
- Static Data: Cache for hours/days
Implementation:
class CachedAPI {
constructor(apiClient, cache) {
this.api = apiClient;
this.cache = cache;
}
async getAccount(address) {
const cacheKey = `account:${address}`;
let account = await this.cache.get(cacheKey);
if (!account) {
account = await this.api.getAccount(address);
await this.cache.set(cacheKey, account, 300); // 5 minutes
}
return account;
}
}
5. Error Handling and Retry Logic
Implement robust error handling with exponential backoff.
Implementation:
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
if (error.status === 429) {
// Rate limited - wait longer
await delay(Math.pow(2, i) * 1000);
} else if (error.status >= 500) {
// Server error - retry
await delay(Math.pow(2, i) * 1000);
} else {
// Client error - don't retry
throw error;
}
}
}
}
Architecture Patterns
Microservices Architecture
Split your application into focused services:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Service │ │ Trading Service│ │ Data Service │
│ │ │ │ │ │
│ - Authentication│ │ - Order Mgmt │ │ - Market Data │
│ - User Profiles │ │ - Portfolio │ │ - Analytics │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌─────────────────┐
│ NUVA Labs APIs │
│ │
│ - All Endpoints │
└─────────────────┘
Serverless Architecture
Use serverless functions for event-driven processing:
# serverless.yml
functions:
processTransfer:
handler: handlers.transfer.process
events:
- http:
path: /transfers
method: post
environment:
NU_API_KEY: ${env:NU_API_KEY}
webhookHandler:
handler: handlers.webhook.handle
events:
- http:
path: /webhooks
method: post
Security Patterns
API Key Management
Environment Variables:
# .env
NU_API_KEY=your_api_key_here
NU_WEBHOOK_SECRET=your_webhook_secret
Key Rotation:
class APIKeyManager {
constructor() {
this.primaryKey = process.env.NU_API_KEY;
this.backupKey = process.env.NU_API_KEY_BACKUP;
}
async getValidKey() {
try {
await this.testKey(this.primaryKey);
return this.primaryKey;
} catch (error) {
return this.backupKey;
}
}
}
Request Signing
For enhanced security, implement request signing:
function signRequest(method, path, body, timestamp, apiKey) {
const message = `${method}${path}${body}${timestamp}`;
const signature = crypto
.createHmac('sha256', apiKey)
.update(message)
.digest('hex');
return signature;
}
Performance Optimization
Connection Pooling
Reuse HTTP connections:
const https = require('https');
const agent = new https.Agent({
keepAlive: true,
maxSockets: 10,
});
const apiClient = axios.create({
baseURL: 'https://api.provlabs.com',
httpsAgent: agent,
});
Parallel Processing
Process multiple requests concurrently:
async function getMultipleAccounts(addresses) {
const promises = addresses.map((address) =>
apiClient.get(`/accounts/${address}`)
);
const results = await Promise.allSettled(promises);
return results
.filter((result) => result.status === 'fulfilled')
.map((result) => result.value.data);
}
Monitoring and Observability
Logging
Implement structured logging:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [new winston.transports.File({ filename: 'app.log' })],
});
// Log API calls
logger.info('API call', {
endpoint: '/transfers',
method: 'POST',
duration: 150,
status: 200,
});
Metrics
Track key performance indicators:
const prometheus = require('prom-client');
const apiCallDuration = new prometheus.Histogram({
name: 'api_call_duration_seconds',
help: 'Duration of API calls',
labelNames: ['endpoint', 'method', 'status'],
});
// Record metrics
const timer = apiCallDuration.startTimer();
try {
const result = await apiCall();
timer({ endpoint: '/transfers', method: 'POST', status: '200' });
return result;
} catch (error) {
timer({ endpoint: '/transfers', method: 'POST', status: '500' });
throw error;
}
Testing Patterns
Unit Testing
Test individual components:
describe('TransferService', () => {
it('should create transfer successfully', async () => {
const mockAPI = {
createTransfer: jest.fn().mockResolvedValue({ transferId: '123' }),
};
const service = new TransferService(mockAPI);
const result = await service.createTransfer(transferData);
expect(result.transferId).toBe('123');
expect(mockAPI.createTransfer).toHaveBeenCalledWith(transferData);
});
});
Integration Testing
Test API interactions:
describe('API Integration', () => {
it('should handle rate limiting', async () => {
const requests = Array(10)
.fill()
.map(() => apiClient.get('/accounts/test'));
const results = await Promise.allSettled(requests);
const rateLimited = results.filter((r) => r.status === 'rejected');
expect(rateLimited.length).toBeGreaterThan(0);
});
});
Next Steps
- Learn about Troubleshooting for common issues
- Check out Best Practices for production deployment
- Explore API Reference for detailed endpoint documentation
- Review Use Cases for practical implementation examples
Ready to start building? Let's explore Troubleshooting for common issues and solutions!