Skip to main content

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

Ready to start building? Let's explore Troubleshooting for common issues and solutions!