The code examples in this guide demonstrate integration patterns using pseudocode. For actual API implementation, use direct HTTP requests to the Nudj API endpoints documented in our API Reference.
Core Integration Patterns
Request-Response Pattern
Synchronous API IntegrationCopy
Ask AI
// Simple synchronous pattern for real-time operations
class UserService {
constructor(private nudjClient: NudjClient) {}
async createUser(userData: CreateUserRequest): Promise<User> {
try {
// Create user in your system
const localUser = await this.db.users.create(userData);
// Sync to Nudj platform
const nudjUser = await this.nudjClient.users.create({
email: localUser.email,
username: localUser.username,
externalUserId: localUser.id
});
// Update local record with Nudj ID
await this.db.users.update(localUser.id, {
nudjUserId: nudjUser.id
});
return localUser;
} catch (error) {
// Handle rollback if needed
await this.handleUserCreationError(error);
throw error;
}
}
}
- Real-time user operations
- Interactive challenge completion
- Immediate reward redemption
- Administrative operations
Event-Driven Pattern
Asynchronous Event ProcessingCopy
Ask AI
// Event-driven pattern for scalable, decoupled operations
class EngagementEventHandler {
constructor(
private eventBus: EventBus,
private nudjClient: NudjClient
) {}
async handleUserRegistration(event: UserRegisteredEvent) {
// Process in background
await this.eventBus.publish('user.sync.requested', {
userId: event.userId,
timestamp: event.timestamp
});
}
async syncUserToNudj(event: UserSyncRequestedEvent) {
const user = await this.db.users.findById(event.userId);
await this.nudjClient.users.create({
email: user.email,
username: user.username,
externalUserId: user.id
});
await this.eventBus.publish('user.synced', {
userId: event.userId,
nudjUserId: user.nudjUserId
});
}
}
- High-volume user operations
- Background data synchronization
- Complex workflow orchestration
- System decoupling
Batch Processing Pattern
Bulk Data OperationsCopy
Ask AI
// Batch processing for efficient bulk operations
class BatchSyncService {
constructor(private nudjClient: NudjClient) {}
async syncUsersBatch(userIds: string[], batchSize = 100) {
const batches = this.chunk(userIds, batchSize);
for (const batch of batches) {
await this.processBatch(batch);
await this.delay(1000); // Rate limiting
}
}
private async processBatch(userIds: string[]) {
const users = await this.db.users.findByIds(userIds);
const nudjUsers = users.map(user => ({
email: user.email,
username: user.username,
externalUserId: user.id
}));
try {
const results = await this.nudjClient.users.bulkCreate(nudjUsers);
await this.updateLocalRecords(users, results);
} catch (error) {
await this.handleBatchError(userIds, error);
}
}
}
- Initial data migration
- Daily/periodic synchronization
- Bulk user imports
- Data cleansing operations
Authentication Patterns
API Key Management
Secure API Key HandlingCopy
Ask AI
class SecureNudjClient {
private apiKey: string;
private keyRotationSchedule: NodeJS.Timer;
constructor(config: ClientConfig) {
this.apiKey = this.loadApiKey();
this.setupKeyRotation();
}
private loadApiKey(): string {
// Load from secure vault/key management system
return process.env.NUDJ_API_KEY || this.vault.getSecret('nudj-api-key');
}
private setupKeyRotation() {
// Rotate API keys periodically
this.keyRotationSchedule = setInterval(async () => {
await this.rotateApiKey();
}, 30 * 24 * 60 * 60 * 1000); // 30 days
}
private async rotateApiKey() {
const newKey = await this.nudjClient.auth.rotateApiKey();
await this.vault.setSecret('nudj-api-key', newKey);
this.apiKey = newKey;
}
}
Token-Based Authentication
JWT Token ManagementCopy
Ask AI
import jwt
from datetime import datetime, timedelta
from nudj import NudjClient
class TokenManager:
def __init__(self, client_id: str, client_secret: str):
self.client_id = client_id
self.client_secret = client_secret
self.token = None
self.expires_at = None
async def get_valid_token(self) -> str:
if self.token and self.expires_at > datetime.utcnow():
return self.token
return await self.refresh_token()
async def refresh_token(self) -> str:
response = await self.nudj_client.auth.get_token({
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': 'client_credentials'
})
self.token = response.access_token
self.expires_at = datetime.utcnow() + timedelta(
seconds=response.expires_in - 300 # 5-minute buffer
)
return self.token
Data Synchronization Patterns
Bidirectional Sync
Two-Way Data SynchronizationCopy
Ask AI
class BidirectionalSyncService {
constructor(
private nudjClient: NudjClient,
private localDb: Database
) {}
async syncUserData(userId: string) {
const [localUser, nudjUser] = await Promise.all([
this.localDb.users.findById(userId),
this.nudjClient.users.get(userId)
]);
// Determine which record is more recent
const localUpdated = new Date(localUser.updatedAt);
const nudjUpdated = new Date(nudjUser.updatedAt);
if (localUpdated > nudjUpdated) {
// Local is newer, sync to Nudj
await this.syncToNudj(localUser);
} else if (nudjUpdated > localUpdated) {
// Nudj is newer, sync to local
await this.syncToLocal(nudjUser);
}
// If equal, no sync needed
}
private async syncToNudj(localUser: LocalUser) {
await this.nudjClient.users.update(localUser.nudjUserId, {
displayName: localUser.displayName,
bio: localUser.bio,
profilePictureUrl: localUser.avatarUrl
});
}
private async syncToLocal(nudjUser: NudjUser) {
await this.localDb.users.update(nudjUser.externalUserId, {
displayName: nudjUser.displayName,
bio: nudjUser.bio,
avatarUrl: nudjUser.profilePictureUrl,
updatedAt: nudjUser.updatedAt
});
}
}
Change Data Capture
CDC-Based SynchronizationCopy
Ask AI
class ChangeDataCaptureSync:
def __init__(self, nudj_client: NudjClient):
self.nudj_client = nudj_client
self.last_sync_timestamp = self.get_last_sync_timestamp()
async def sync_changes(self):
# Get changes since last sync
changes = await self.get_local_changes(self.last_sync_timestamp)
for change in changes:
await self.process_change(change)
# Update sync timestamp
self.update_last_sync_timestamp(datetime.utcnow())
async def process_change(self, change: DataChange):
if change.operation == 'INSERT':
await self.handle_create(change)
elif change.operation == 'UPDATE':
await self.handle_update(change)
elif change.operation == 'DELETE':
await self.handle_delete(change)
async def handle_create(self, change: DataChange):
if change.table == 'users':
await self.nudj_client.users.create(change.new_values)
elif change.table == 'challenges':
await self.nudj_client.challenges.create(change.new_values)
Error Handling Patterns
Circuit Breaker Pattern
Resilient API IntegrationCopy
Ask AI
class CircuitBreaker {
private failures = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor(
private threshold = 5,
private timeout = 60000,
private retryTimeout = 30000
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime < this.timeout) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
private onFailure() {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.threshold) {
this.state = 'OPEN';
}
}
}
// Usage
const circuitBreaker = new CircuitBreaker();
const nudjClientWithCircuitBreaker = {
users: {
get: (id: string) => circuitBreaker.execute(() => nudjClient.users.get(id))
}
};
Retry with Exponential Backoff
Intelligent Retry LogicCopy
Ask AI
import asyncio
import random
from typing import Callable, TypeVar, Any
T = TypeVar('T')
class RetryWithBackoff:
def __init__(
self,
max_retries: int = 3,
base_delay: float = 1.0,
max_delay: float = 60.0,
jitter: bool = True
):
self.max_retries = max_retries
self.base_delay = base_delay
self.max_delay = max_delay
self.jitter = jitter
async def execute(
self,
operation: Callable[[], Awaitable[T]],
retryable_exceptions: tuple = (Exception,)
) -> T:
last_exception = None
for attempt in range(self.max_retries + 1):
try:
return await operation()
except retryable_exceptions as e:
last_exception = e
if attempt == self.max_retries:
break
delay = min(
self.base_delay * (2 ** attempt),
self.max_delay
)
if self.jitter:
delay *= (0.5 + random.random() * 0.5)
await asyncio.sleep(delay)
raise last_exception
# Usage
retry_handler = RetryWithBackoff()
async def get_user_with_retry(user_id: str):
return await retry_handler.execute(
lambda: nudj_client.users.get(user_id),
retryable_exceptions=(ConnectionError, TimeoutError)
)
Caching Patterns
Multi-Level Caching
Layered Caching StrategyCopy
Ask AI
interface CacheLayer {
get(key: string): Promise<any>;
set(key: string, value: any, ttl?: number): Promise<void>;
delete(key: string): Promise<void>;
}
class MultiLevelCache {
constructor(
private l1Cache: CacheLayer, // Memory cache
private l2Cache: CacheLayer, // Redis cache
private l3Cache: CacheLayer // Database cache
) {}
async get(key: string): Promise<any> {
// Try L1 cache first
let value = await this.l1Cache.get(key);
if (value) return value;
// Try L2 cache
value = await this.l2Cache.get(key);
if (value) {
await this.l1Cache.set(key, value, 300); // 5 min TTL
return value;
}
// Try L3 cache
value = await this.l3Cache.get(key);
if (value) {
await this.l2Cache.set(key, value, 3600); // 1 hour TTL
await this.l1Cache.set(key, value, 300);
return value;
}
return null;
}
async set(key: string, value: any) {
await Promise.all([
this.l1Cache.set(key, value, 300),
this.l2Cache.set(key, value, 3600),
this.l3Cache.set(key, value, 86400) // 24 hours
]);
}
}
class CachedNudjClient {
constructor(
private nudjClient: NudjClient,
private cache: MultiLevelCache
) {}
async getUser(userId: string): Promise<User> {
const cacheKey = `user:${userId}`;
let user = await this.cache.get(cacheKey);
if (user) return user;
user = await this.nudjClient.users.get(userId);
await this.cache.set(cacheKey, user);
return user;
}
}
Cache Invalidation
Smart Cache InvalidationCopy
Ask AI
class CacheInvalidationService:
def __init__(self, cache: CacheLayer, nudj_client: NudjClient):
self.cache = cache
self.nudj_client = nudj_client
self.setup_webhooks()
def setup_webhooks(self):
# Listen for user update events
self.nudj_client.webhooks.subscribe(
'user.updated',
self.handle_user_updated
)
async def handle_user_updated(self, event: WebhookEvent):
user_id = event.data.user_id
# Invalidate all caches for this user
await self.invalidate_user_cache(user_id)
async def invalidate_user_cache(self, user_id: str):
cache_keys = [
f'user:{user_id}',
f'user:profile:{user_id}',
f'user:challenges:{user_id}',
f'user:rewards:{user_id}'
]
for key in cache_keys:
await self.cache.delete(key)
Monitoring & Observability Patterns
Comprehensive Logging
Structured Logging with Correlation IDsCopy
Ask AI
class ObservableNudjClient {
constructor(
private nudjClient: NudjClient,
private logger: Logger,
private metrics: MetricsCollector
) {}
async getUser(userId: string, correlationId?: string): Promise<User> {
const startTime = Date.now();
const requestId = correlationId || generateRequestId();
this.logger.info('Nudj API request started', {
operation: 'getUser',
userId,
requestId,
timestamp: new Date().toISOString()
});
try {
const user = await this.nudjClient.users.get(userId);
const duration = Date.now() - startTime;
this.logger.info('Nudj API request completed', {
operation: 'getUser',
userId,
requestId,
duration,
success: true
});
this.metrics.increment('nudj.api.requests', {
operation: 'getUser',
status: 'success'
});
this.metrics.histogram('nudj.api.duration', duration, {
operation: 'getUser'
});
return user;
} catch (error) {
const duration = Date.now() - startTime;
this.logger.error('Nudj API request failed', {
operation: 'getUser',
userId,
requestId,
duration,
error: error.message,
stack: error.stack
});
this.metrics.increment('nudj.api.requests', {
operation: 'getUser',
status: 'error'
});
throw error;
}
}
}
Health Check Pattern
Service Health MonitoringCopy
Ask AI
from typing import Dict, Any
from datetime import datetime, timedelta
class HealthCheckService:
def __init__(self, nudj_client: NudjClient):
self.nudj_client = nudj_client
self.last_successful_call = None
self.consecutive_failures = 0
async def check_health(self) -> Dict[str, Any]:
health_status = {
'status': 'healthy',
'timestamp': datetime.utcnow().isoformat(),
'checks': {}
}
# Check API connectivity
api_health = await self.check_api_connectivity()
health_status['checks']['nudj_api'] = api_health
# Check authentication
auth_health = await self.check_authentication()
health_status['checks']['authentication'] = auth_health
# Overall status
all_healthy = all(
check['status'] == 'healthy'
for check in health_status['checks'].values()
)
health_status['status'] = 'healthy' if all_healthy else 'unhealthy'
return health_status
async def check_api_connectivity(self) -> Dict[str, Any]:
try:
start_time = datetime.utcnow()
await self.nudj_client.health_check()
duration = (datetime.utcnow() - start_time).total_seconds()
self.last_successful_call = datetime.utcnow()
self.consecutive_failures = 0
return {
'status': 'healthy',
'response_time_ms': duration * 1000,
'last_success': self.last_successful_call.isoformat()
}
except Exception as e:
self.consecutive_failures += 1
return {
'status': 'unhealthy',
'error': str(e),
'consecutive_failures': self.consecutive_failures,
'last_success': self.last_successful_call.isoformat() if self.last_successful_call else None
}
Scaling Patterns
Load Balancing
API Client Load BalancingCopy
Ask AI
class LoadBalancedNudjClient {
private clients: NudjClient[] = [];
private currentIndex = 0;
constructor(endpoints: string[], apiKey: string) {
this.clients = endpoints.map(endpoint =>
new NudjClient({ baseURL: endpoint, apiKey })
);
}
private getNextClient(): NudjClient {
const client = this.clients[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.clients.length;
return client;
}
async getUser(userId: string): Promise<User> {
const maxAttempts = this.clients.length;
let lastError: Error;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
const client = this.getNextClient();
return await client.users.get(userId);
} catch (error) {
lastError = error;
console.warn(`Client attempt ${attempt + 1} failed:`, error.message);
}
}
throw lastError;
}
}
Rate Limiting
Client-Side Rate LimitingCopy
Ask AI
import asyncio
from datetime import datetime, timedelta
from collections import deque
class RateLimiter:
def __init__(self, max_requests: int, time_window: int):
self.max_requests = max_requests
self.time_window = time_window
self.requests = deque()
self.lock = asyncio.Lock()
async def acquire(self):
async with self.lock:
now = datetime.utcnow()
# Remove requests outside the time window
while (self.requests and
now - self.requests[0] > timedelta(seconds=self.time_window)):
self.requests.popleft()
# Check if we can make a request
if len(self.requests) >= self.max_requests:
# Calculate wait time
oldest_request = self.requests[0]
wait_time = (oldest_request + timedelta(seconds=self.time_window) - now).total_seconds()
await asyncio.sleep(wait_time)
return await self.acquire()
# Record this request
self.requests.append(now)
class RateLimitedNudjClient:
def __init__(self, nudj_client: NudjClient, rate_limiter: RateLimiter):
self.nudj_client = nudj_client
self.rate_limiter = rate_limiter
async def get_user(self, user_id: str):
await self.rate_limiter.acquire()
return await self.nudj_client.users.get(user_id)
Testing Patterns
Integration Testing
Comprehensive Integration TestsCopy
Ask AI
describe('Nudj Integration', () => {
let testClient: NudjClient;
let testUser: User;
beforeAll(async () => {
testClient = new NudjClient({
apiKey: process.env.NUDJ_TEST_API_KEY,
baseURL: process.env.NUDJ_TEST_BASE_URL
});
});
describe('User Management', () => {
it('should create, update, and delete user', async () => {
// Create user
const userData = {
email: 'test@example.com',
username: 'testuser',
displayName: 'Test User'
};
testUser = await testClient.users.create(userData);
expect(testUser.email).toBe(userData.email);
// Update user
const updatedData = { displayName: 'Updated User' };
const updatedUser = await testClient.users.update(testUser.id, updatedData);
expect(updatedUser.displayName).toBe(updatedData.displayName);
// Delete user
await testClient.users.delete(testUser.id);
// Verify deletion
await expect(testClient.users.get(testUser.id))
.rejects.toThrow('User not found');
});
});
describe('Challenge Workflow', () => {
it('should complete full challenge workflow', async () => {
// Create challenge
const challenge = await testClient.challenges.create({
title: 'Test Challenge',
communityId: 'test-community'
});
// User starts challenge
await testClient.challenges.start(challenge.id, testUser.id);
// User completes challenge
const completion = await testClient.challenges.complete(
challenge.id,
testUser.id,
{ score: 100 }
);
expect(completion.completed).toBe(true);
expect(completion.score).toBe(100);
});
});
});
Mock Integration
Testing with MocksCopy
Ask AI
import pytest
from unittest.mock import AsyncMock, patch
from nudj import NudjClient
@pytest.fixture
def mock_nudj_client():
client = AsyncMock(spec=NudjClient)
# Mock user operations
client.users.get.return_value = {
'id': 'user-123',
'email': 'test@example.com',
'username': 'testuser'
}
client.users.create.return_value = {
'id': 'user-123',
'email': 'test@example.com',
'username': 'testuser'
}
return client
async def test_user_service_with_mock(mock_nudj_client):
service = UserService(mock_nudj_client)
user = await service.get_user('user-123')
assert user['email'] == 'test@example.com'
mock_nudj_client.users.get.assert_called_once_with('user-123')
Best Practices Summary
Architecture Principles
Design Guidelines- Idempotency: Design all operations to be safely retried
- Fault Tolerance: Handle failures gracefully with circuit breakers
- Observability: Implement comprehensive logging and monitoring
- Security: Use secure authentication and validate all inputs
- Performance: Implement caching and rate limiting appropriately
Integration Checklist
Pre-Production Checklist- Authentication configured securely
- Error handling and retries implemented
- Logging and monitoring in place
- Rate limiting configured
- Integration tests passing
- Performance testing completed
- Security review conducted
- Documentation updated
Common Pitfalls
Avoid These Mistakes- Not implementing proper error handling
- Missing idempotency considerations
- Insufficient logging for debugging
- Hard-coding API keys in source code
- Not handling rate limits appropriately
- Ignoring webhook signature verification
- Missing correlation IDs for request tracing
Getting Started
Choose the integration pattern that best fits your use case:Simple Integration
Start with the Request-Response pattern for straightforward, real-time operations.
Scalable Architecture
Use Event-Driven patterns for high-volume, resilient integrations.
Enterprise Setup
Implement comprehensive patterns with monitoring, caching, and fault tolerance.