Best Practices
Comprehensive guide to best practices for building production-ready applications with NUVA Labs APIs. This guide covers security, performance, reliability, and maintainability.
Security Best Practices
1. Authentication and Authorization
Use Environment Variables
// ❌ Don't hardcode credentials
const client = new NUClient({
clientId: 'hardcoded-id',
clientSecret: 'hardcoded-secret',
});
// ✅ Use environment variables
const client = new NUClient({
clientId: process.env.NU_CLIENT_ID,
clientSecret: process.env.NU_CLIENT_SECRET,
environment: process.env.NU_ENVIRONMENT,
});
Implement Proper Error Handling
try {
const result = await client.getAccounts();
} catch (error) {
if (error.code === 'AUTHENTICATION_FAILED') {
// Handle authentication failure
console.error('Authentication failed:', error.message);
} else if (error.code === 'AUTHORIZATION_FAILED') {
// Handle authorization failure
console.error('Insufficient permissions:', error.message);
} else {
// Handle other errors
console.error('Unexpected error:', error.message);
}
}
Rotate Credentials Regularly
// Implement credential rotation
class CredentialManager {
constructor() {
this.credentials = {
primary: { clientId: 'primary-id', clientSecret: 'primary-secret' },
secondary: { clientId: 'secondary-id', clientSecret: 'secondary-secret' },
};
}
async getActiveCredentials() {
// Check if primary credentials are still valid
try {
const client = new NUClient(this.credentials.primary);
await client.authenticate();
return this.credentials.primary;
} catch (error) {
// Fall back to secondary credentials
return this.credentials.secondary;
}
}
}
2. Input Validation
Validate All Inputs
class InputValidator {
static validateAccountData(data) {
if (!data.name || typeof data.name !== 'string') {
throw new Error('Name is required and must be a string');
}
if (!data.email || !this.isValidEmail(data.email)) {
throw new Error('Valid email is required');
}
if (
!data.type ||
!['user', 'organization', 'service'].includes(data.type)
) {
throw new Error('Type must be user, organization, or service');
}
return true;
}
static isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
}
Sanitize User Input
class InputSanitizer {
static sanitizeString(input) {
if (typeof input !== 'string') return '';
// Remove potentially dangerous characters
return input
.replace(/[<>]/g, '') // Remove HTML tags
.replace(/['"]/g, '') // Remove quotes
.trim();
}
static sanitizeNumber(input) {
const num = parseFloat(input);
return isNaN(num) ? 0 : Math.max(0, num);
}
}
3. Secure Communication
Use HTTPS Only
// Always use HTTPS in production
const client = new NUClient({
clientId: process.env.NU_CLIENT_ID,
clientSecret: process.env.NU_CLIENT_SECRET,
environment: 'production',
secure: true, // Ensure HTTPS
});
Implement Request Signing
class SecureClient extends NUClient {
async makeRequest(endpoint, data) {
const timestamp = Date.now();
const signature = this.generateSignature(endpoint, data, timestamp);
return super.makeRequest(endpoint, {
...data,
timestamp,
signature,
});
}
generateSignature(endpoint, data, timestamp) {
// Implement your signature algorithm
const payload = `${endpoint}${JSON.stringify(data)}${timestamp}`;
return this.sign(payload);
}
}
Performance Best Practices
1. Caching
Implement Response Caching
class CachedClient extends NUClient {
constructor(config) {
super(config);
this.cache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
}
async getAccounts() {
const cacheKey = 'accounts';
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
const data = await super.getAccounts();
this.cache.set(cacheKey, {
data,
timestamp: Date.now(),
});
return data;
}
}
Use Redis for Distributed Caching
import Redis from 'ioredis';
class RedisCachedClient extends NUClient {
constructor(config) {
super(config);
this.redis = new Redis(process.env.REDIS_URL);
}
async getAccounts() {
const cacheKey = 'accounts';
const cached = await this.redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const data = await super.getAccounts();
await this.redis.setex(cacheKey, 300, JSON.stringify(data)); // 5 minutes
return data;
}
}
2. Connection Pooling
Implement Connection Pooling
class PooledClient extends NUClient {
constructor(config) {
super(config);
this.connectionPool = new Map();
this.maxConnections = 10;
}
async getConnection() {
if (this.connectionPool.size >= this.maxConnections) {
// Wait for available connection
await this.waitForConnection();
}
const connection = await this.createConnection();
this.connectionPool.set(connection.id, connection);
return connection;
}
async releaseConnection(connection) {
this.connectionPool.delete(connection.id);
}
}
3. Batch Operations
Use Batch Operations When Possible
class BatchClient extends NUClient {
async createMultipleAccounts(accountsData) {
// Instead of multiple individual requests
const batch = await this.client.createAccounts(accountsData);
return batch;
}
async transferMultipleAssets(transfers) {
// Batch transfer operations
const batch = await this.client.batchTransfer(transfers);
return batch;
}
}
Reliability Best Practices
1. Error Handling
Implement Comprehensive Error Handling
class ReliableClient extends NUClient {
async makeRequest(endpoint, data, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await super.makeRequest(endpoint, data);
} catch (error) {
if (attempt === retries) {
throw error;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await this.sleep(delay);
}
}
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
Handle Rate Limiting
class RateLimitedClient extends NUClient {
constructor(config) {
super(config);
this.rateLimiter = new Map();
}
async makeRequest(endpoint, data) {
const key = endpoint;
const now = Date.now();
const requests = this.rateLimiter.get(key) || [];
// Remove old requests
const recentRequests = requests.filter((time) => now - time < 60000); // 1 minute
if (recentRequests.length >= 100) {
// Rate limit
const oldestRequest = Math.min(...recentRequests);
const waitTime = 60000 - (now - oldestRequest);
await this.sleep(waitTime);
}
recentRequests.push(now);
this.rateLimiter.set(key, recentRequests);
return super.makeRequest(endpoint, data);
}
}
2. Monitoring and Logging
Implement Comprehensive Logging
class LoggedClient extends NUClient {
constructor(config) {
super(config);
this.logger = new Logger();
}
async makeRequest(endpoint, data) {
const startTime = Date.now();
const requestId = this.generateRequestId();
this.logger.info('API Request', {
requestId,
endpoint,
timestamp: new Date().toISOString(),
});
try {
const result = await super.makeRequest(endpoint, data);
this.logger.info('API Response', {
requestId,
endpoint,
duration: Date.now() - startTime,
status: 'success',
});
return result;
} catch (error) {
this.logger.error('API Error', {
requestId,
endpoint,
duration: Date.now() - startTime,
error: error.message,
status: 'error',
});
throw error;
}
}
}
Implement Health Checks
class HealthCheckedClient extends NUClient {
constructor(config) {
super(config);
this.healthStatus = 'unknown';
this.lastHealthCheck = 0;
}
async checkHealth() {
try {
const health = await this.client.getHealth();
this.healthStatus = health.status;
this.lastHealthCheck = Date.now();
return true;
} catch (error) {
this.healthStatus = 'unhealthy';
return false;
}
}
async makeRequest(endpoint, data) {
// Check health before making request
if (Date.now() - this.lastHealthCheck > 60000) {
// 1 minute
await this.checkHealth();
}
if (this.healthStatus !== 'healthy') {
throw new Error('Service is unhealthy');
}
return super.makeRequest(endpoint, data);
}
}
Maintainability Best Practices
1. Code Organization
Use Modular Architecture
// api/accounts.js
export class AccountsAPI {
constructor(client) {
this.client = client;
}
async getAccounts() {
return this.client.get('/accounts');
}
async createAccount(data) {
return this.client.post('/accounts', data);
}
}
// api/assets.js
export class AssetsAPI {
constructor(client) {
this.client = client;
}
async getAssets() {
return this.client.get('/assets');
}
async createAsset(data) {
return this.client.post('/assets', data);
}
}
// main.js
import { AccountsAPI } from './api/accounts.js';
import { AssetsAPI } from './api/assets.js';
class NUClient {
constructor(config) {
this.accounts = new AccountsAPI(this);
this.assets = new AssetsAPI(this);
}
}
Implement Configuration Management
class ConfigManager {
constructor() {
this.config = {
api: {
baseUrl: process.env.NU_API_BASE_URL || 'https://api.provlabs.com',
timeout: parseInt(process.env.NU_API_TIMEOUT) || 30000,
retries: parseInt(process.env.NU_API_RETRIES) || 3,
},
auth: {
clientId: process.env.NU_CLIENT_ID,
clientSecret: process.env.NU_CLIENT_SECRET,
environment: process.env.NU_ENVIRONMENT || 'production',
},
cache: {
enabled: process.env.NU_CACHE_ENABLED === 'true',
ttl: parseInt(process.env.NU_CACHE_TTL) || 300,
},
};
}
get(key) {
return this.config[key];
}
update(key, value) {
this.config[key] = { ...this.config[key], ...value };
}
}
2. Testing
Implement Unit Tests
import { describe, it, expect, beforeEach } from 'jest';
import { NUClient } from '../src/nu-client.js';
describe('NUClient', () => {
let client;
beforeEach(() => {
client = new NUClient({
clientId: 'test-client-id',
clientSecret: 'test-client-secret',
environment: 'sandbox',
});
});
it('should authenticate successfully', async () => {
const result = await client.authenticate();
expect(result.success).toBe(true);
});
it('should get accounts', async () => {
const accounts = await client.getAccounts();
expect(Array.isArray(accounts)).toBe(true);
});
});
Implement Integration Tests
describe('Integration Tests', () => {
it('should create account and get details', async () => {
const account = await client.createAccount({
name: 'Test User',
email: 'test@example.com',
type: 'user',
});
expect(account.id).toBeDefined();
const details = await client.getAccount(account.id);
expect(details.id).toBe(account.id);
});
});
3. Documentation
Document Your Code
/**
* NUVA Labs API Client for interacting with Provenance Blockchain
* @class NUClient
* @param {Object} config - Configuration object
* @param {string} config.clientId - API client ID
* @param {string} config.clientSecret - API client secret
* @param {string} config.environment - Environment (production/sandbox)
*/
class NUClient {
/**
* Authenticate with the API
* @returns {Promise<Object>} Authentication result
*/
async authenticate() {
// Implementation
}
/**
* Get user accounts
* @param {Object} options - Query options
* @param {number} options.limit - Number of accounts to return
* @param {number} options.offset - Starting position
* @returns {Promise<Array>} Array of accounts
*/
async getAccounts(options = {}) {
// Implementation
}
}
Deployment Best Practices
1. Environment Configuration
Use Environment Variables
# .env.production
NU_CLIENT_ID=your-production-client-id
NU_CLIENT_SECRET=your-production-client-secret
NU_ENVIRONMENT=production
NU_API_BASE_URL=https://api.provlabs.com
NU_CACHE_ENABLED=true
NU_CACHE_TTL=300
Implement Configuration Validation
class ConfigValidator {
static validate(config) {
const required = ['clientId', 'clientSecret', 'environment'];
for (const field of required) {
if (!config[field]) {
throw new Error(`Missing required configuration: ${field}`);
}
}
if (!['production', 'sandbox'].includes(config.environment)) {
throw new Error('Environment must be production or sandbox');
}
return true;
}
}
2. Monitoring and Alerting
Implement Application Monitoring
class MonitoringClient extends NUClient {
constructor(config) {
super(config);
this.metrics = new MetricsCollector();
}
async makeRequest(endpoint, data) {
const startTime = Date.now();
try {
const result = await super.makeRequest(endpoint, data);
this.metrics.recordSuccess(endpoint, Date.now() - startTime);
return result;
} catch (error) {
this.metrics.recordError(endpoint, error);
throw error;
}
}
}
Set Up Alerting
class AlertingClient extends NUClient {
constructor(config) {
super(config);
this.alertManager = new AlertManager();
}
async makeRequest(endpoint, data) {
try {
return await super.makeRequest(endpoint, data);
} catch (error) {
if (error.code === 'RATE_LIMITED') {
this.alertManager.sendAlert('Rate limit exceeded', {
endpoint,
timestamp: new Date().toISOString(),
});
}
throw error;
}
}
}
Next Steps
- Explore Integration Patterns for advanced integration techniques
- Learn about Troubleshooting for common issues and solutions
- Check out API Reference for detailed endpoint documentation
- Review Use Cases for practical implementation examples
Ready to start building? Let's explore Integration Patterns for advanced integration techniques!