MindPeeker Logo
Developers

Best Practices

Recommended best practices for developing with MindPeeker platform

Overview

These best practices will help you build robust, secure, and efficient applications with the MindPeeker platform. Following these guidelines will ensure optimal performance, security, and user experience.

Security Best Practices

API Key Management

Never expose API keys in client-side code

// ❌ BAD - Exposed in frontend
const client = new MindPeekerClient({
  apiKey: 'pk_live_1234567890abcdef' // Visible to users
});

// ✅ GOOD - Server-side only
// frontend.js
async function createSession(target) {
  const response = await fetch('/api/sessions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ target })
  });
  return response.json();
}

// backend.js
const client = new MindPeekerClient({
  apiKey: process.env.MINDPEEKER_API_KEY // Secure environment variable
});

Use environment variables for configuration

MINDPEEKER_API_KEY=your_api_key_here
MINDPEEKER_WEBHOOK_SECRET=your_webhook_secret
MINDPEEKER_API_URL=https://api.mindpeeker.com/v1
// config.js
const config = {
  apiKey: process.env.MINDPEEKER_API_KEY,
  webhookSecret: process.env.MINDPEEKER_WEBHOOK_SECRET,
  apiUrl: process.env.MINDPEEKER_API_URL || 'https://api.mindpeeker.com/v1'
};

// Validate required configuration
const requiredEnvVars = ['MINDPEEKER_API_KEY'];
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);

if (missingVars.length > 0) {
  throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
}

Implement API key rotation

class ApiKeyManager {
  constructor() {
    this.currentKey = process.env.MINDPEEKER_API_KEY;
    this.backupKey = process.env.MINDPEEKER_BACKUP_API_KEY;
    this.currentKeyIndex = 0;
    this.keys = [this.currentKey, this.backupKey];
  }
  
  getClient() {
    return new MindPeekerClient({
      apiKey: this.keys[this.currentKeyIndex]
    });
  }
  
  async rotateKey() {
    // Try backup key first
    this.currentKeyIndex = 1;
    const testClient = this.getClient();
    
    try {
      await testClient.sessions.list({ limit: 1 });
      // Backup key works, make it primary
      this.keys.reverse();
      this.currentKeyIndex = 0;
      console.log('API key rotation successful');
    } catch (error) {
      // Backup key failed, revert to original
      this.currentKeyIndex = 0;
      throw new Error('API key rotation failed: backup key invalid');
    }
  }
}

Input Validation and Sanitization

Validate all user inputs

const Joi = require('joi');

const sessionSchema = Joi.object({
  type: Joi.string().valid('remote_viewing', 'dowsing', 'automatic_writing').required(),
  target: Joi.string().min(1).max(1000).required(),
  modality: Joi.string().valid('visual', 'kinesthetic', 'auditory').optional(),
  duration_minutes: Joi.number().integer().min(5).max(180).optional()
});

function validateSessionInput(data) {
  const { error, value } = sessionSchema.validate(data);
  if (error) {
    throw new Error(`Validation error: ${error.details[0].message}`);
  }
  return value;
}

// Usage
app.post('/api/sessions', async (req, res) => {
  try {
    const validatedData = validateSessionInput(req.body);
    const session = await mindpeekerClient.sessions.create(validatedData);
    res.json(session);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Sanitize outputs to prevent data leakage

function sanitizeSessionResults(results) {
  const sanitized = {
    sessionId: results.session_id,
    status: results.status,
    confidenceScore: results.confidence_score,
    completedAt: results.completed_at
  };
  
  // Only include results if they exist and user has permission
  if (results.results && hasPermissionToViewResults(req.user)) {
    sanitized.results = {
      coordinates: results.results.coordinates,
      descriptors: results.results.descriptors,
      // Exclude sensitive internal data
      internalMetadata: undefined,
      analystNotes: undefined
    };
  }
  
  return sanitized;
}

Webhook Security

Verify webhook signatures

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)

def verify_webhook_signature(payload, signature, secret):
    """Verify webhook signature using HMAC-SHA256"""
    if not signature:
        return False
    
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(
        f'sha256={expected_signature}',
        signature
    )

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-MindPeeker-Signature')
    payload = request.data
    
    if not verify_webhook_signature(payload, signature, app.config['WEBHOOK_SECRET']):
        abort(401)  # Unauthorized
    
    # Process webhook
    event = request.get_json()
    process_webhook_event(event)
    
    return '', 200

Implement replay protection

import redis
from datetime import datetime, timedelta

redis_client = redis.Redis(host='localhost', port=6379, db=0)

@app.route('/webhook', methods=['POST'])
def webhook():
    # Verify signature first
    if not verify_webhook_signature(...):
        abort(401)
    
    event = request.get_json()
    event_id = event.get('id')
    
    # Check for duplicate events
    if redis_client.exists(f'webhook_event:{event_id}'):
        return '', 200  # Already processed
    
    # Mark event as processed
    redis_client.setex(
        f'webhook_event:{event_id}',
        timedelta(hours=24),  # Expire after 24 hours
        'processed'
    )
    
    # Process event
    process_webhook_event(event)
    
    return '', 200

Performance Best Practices

Connection Management

Use connection pooling

const { MindPeekerClient } = require('@mindpeeker/javascript');

class MindPeekerPool {
  constructor(options = {}) {
    this.maxSize = options.maxSize || 10;
    this.minSize = options.minSize || 2;
    this.clients = [];
    this.availableClients = [];
    this.waitingQueue = [];
    
    this.initializePool();
  }
  
  async initializePool() {
    for (let i = 0; i < this.minSize; i++) {
      const client = new MindPeekerClient({
        apiKey: process.env.MINDPEEKER_API_KEY,
        timeout: 30000,
        retryAttempts: 3
      });
      
      this.clients.push(client);
      this.availableClients.push(client);
    }
  }
  
  async getClient() {
    if (this.availableClients.length > 0) {
      return this.availableClients.pop();
    }
    
    if (this.clients.length < this.maxSize) {
      const client = new MindPeekerClient({
        apiKey: process.env.MINDPEEKER_API_KEY
      });
      this.clients.push(client);
      return client;
    }
    
    // Wait for available client
    return new Promise((resolve) => {
      this.waitingQueue.push(resolve);
    });
  }
  
  releaseClient(client) {
    if (this.waitingQueue.length > 0) {
      const resolve = this.waitingQueue.shift();
      resolve(client);
    } else {
      this.availableClients.push(client);
    }
  }
}

// Usage
const pool = new MindPeekerPool({ maxSize: 5 });

async function createSession(target) {
  const client = await pool.getClient();
  try {
    return await client.sessions.create({ target, type: 'remote_viewing' });
  } finally {
    pool.releaseClient(client);
  }
}

Implement intelligent caching

const NodeCache = require('node-cache');

class MindPeekerCache {
  constructor(options = {}) {
    this.cache = new NodeCache({
      stdTTL: options.defaultTTL || 300, // 5 minutes
      checkperiod: options.checkPeriod || 60, // 1 minute
      useClones: false
    });
    
    this.client = new MindPeekerClient({
      apiKey: process.env.MINDPEEKER_API_KEY
    });
  }
  
  async getSessionResults(sessionId, options = {}) {
    const cacheKey = `session_results:${sessionId}`;
    let results = this.cache.get(cacheKey);
    
    if (results) {
      return results;
    }
    
    // Fetch from API
    results = await this.client.sessions.getResults(sessionId);
    
    // Cache based on session status
    if (results.status === 'completed') {
      this.cache.set(cacheKey, results, options.completedTTL || 3600); // 1 hour
    } else if (results.status === 'failed') {
      this.cache.set(cacheKey, results, options.failedTTL || 300); // 5 minutes
    } else {
      this.cache.set(cacheKey, results, options.pendingTTL || 60); // 1 minute
    }
    
    return results;
  }
  
  invalidateSession(sessionId) {
    this.cache.del(`session_results:${sessionId}`);
  }
  
  clearCache() {
    this.cache.flushAll();
  }
  
  getStats() {
    return this.cache.getStats();
  }
}

Batch Operations

Process multiple requests efficiently

class BatchProcessor {
  constructor(client, options = {}) {
    this.client = client;
    this.batchSize = options.batchSize || 10;
    this.concurrency = options.concurrency || 3;
    this.delay = options.delay || 100; // ms between batches
  }
  
  async processSessions(sessionIds, processor) {
    const results = [];
    
    for (let i = 0; i < sessionIds.length; i += this.batchSize) {
      const batch = sessionIds.slice(i, i + this.batchSize);
      
      const batchResults = await Promise.all(
        batch.map(sessionId => 
          this.processWithRetry(sessionId, processor)
        )
      );
      
      results.push(...batchResults);
      
      // Rate limiting between batches
      if (i + this.batchSize < sessionIds.length) {
        await this.delay(this.delay);
      }
    }
    
    return results;
  }
  
  async processWithRetry(sessionId, processor, maxRetries = 3) {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await processor(sessionId);
      } catch (error) {
        if (attempt === maxRetries) {
          throw error;
        }
        
        // Exponential backoff
        const delay = Math.pow(2, attempt) * 1000;
        await this.delay(delay);
      }
    }
  }
  
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const batchProcessor = new BatchProcessor(mindpeekerClient, {
  batchSize: 5,
  concurrency: 2,
  delay: 200
});

const sessionIds = ['sess_1', 'sess_2', 'sess_3', 'sess_4', 'sess_5'];
const results = await batchProcessor.processSessions(
  sessionIds,
  async (sessionId) => {
    return await mindpeekerClient.sessions.getResults(sessionId);
  }
);

Error Handling Best Practices

Structured Error Handling

Implement custom error classes

class MindPeekerError extends Error {
  constructor(message, code, details = {}) {
    super(message);
    this.name = 'MindPeekerError';
    this.code = code;
    this.details = details;
    this.timestamp = new Date().toISOString();
  }
}

class RateLimitError extends MindPeekerError {
  constructor(retryAfter) {
    super('Rate limit exceeded', 'RATE_LIMIT_EXCEEDED', { retryAfter });
    this.name = 'RateLimitError';
  }
}

class AuthenticationError extends MindPeekerError {
  constructor() {
    super('Authentication failed', 'AUTHENTICATION_ERROR');
    this.name = 'AuthenticationError';
  }
}

class ValidationError extends MindPeekerError {
  constructor(field, value) {
    super(`Invalid ${field}: ${value}`, 'VALIDATION_ERROR', { field, value });
    this.name = 'ValidationError';
  }
}

Centralized error handling middleware

function errorHandler(err, req, res, next) {
  // Log error
  console.error(`Error ${err.name}: ${err.message}`, {
    code: err.code,
    details: err.details,
    stack: err.stack,
    requestId: req.id,
    userId: req.user?.id,
    timestamp: err.timestamp
  });
  
  // Determine response based on error type
  if (err instanceof RateLimitError) {
    return res.status(429).json({
      error: {
        code: 'RATE_LIMIT_EXCEEDED',
        message: 'Too many requests',
        retryAfter: err.details.retryAfter
      }
    });
  }
  
  if (err instanceof AuthenticationError) {
    return res.status(401).json({
      error: {
        code: 'AUTHENTICATION_ERROR',
        message: 'Invalid credentials'
      }
    });
  }
  
  if (err instanceof ValidationError) {
    return res.status(400).json({
      error: {
        code: 'VALIDATION_ERROR',
        message: err.message,
        field: err.details.field
      }
    });
  }
  
  // Default error response
  res.status(500).json({
    error: {
      code: 'INTERNAL_ERROR',
      message: 'An unexpected error occurred'
    }
  });
}

Retry Logic

Exponential backoff with jitter

class RetryHandler {
  constructor(options = {}) {
    this.maxRetries = options.maxRetries || 3;
    this.baseDelay = options.baseDelay || 1000;
    this.maxDelay = options.maxDelay || 30000;
    this.jitterFactor = options.jitterFactor || 0.1;
  }
  
  async executeWithRetry(operation, context = {}) {
    let lastError;
    
    for (let attempt = 1; attempt <= this.maxRetries + 1; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;
        
        // Don't retry on certain errors
        if (this.isNonRetryableError(error)) {
          throw error;
        }
        
        // Don't retry on last attempt
        if (attempt > this.maxRetries) {
          break;
        }
        
        const delay = this.calculateDelay(attempt);
        console.log(`Attempt ${attempt} failed, retrying in ${delay}ms:`, error.message);
        await this.sleep(delay);
      }
    }
    
    throw lastError;
  }
  
  isNonRetryableError(error) {
    const nonRetryableCodes = [
      'AUTHENTICATION_ERROR',
      'VALIDATION_ERROR',
      'PERMISSION_DENIED'
    ];
    
    return nonRetryableCodes.includes(error.code) ||
           error.status === 401 ||
           error.status === 403 ||
           error.status === 422;
  }
  
  calculateDelay(attempt) {
    // Exponential backoff with jitter
    const exponentialDelay = Math.min(
      this.baseDelay * Math.pow(2, attempt - 1),
      this.maxDelay
    );
    
    // Add jitter to prevent thundering herd
    const jitter = exponentialDelay * this.jitterFactor * Math.random();
    
    return Math.floor(exponentialDelay + jitter);
  }
  
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const retryHandler = new RetryHandler({
  maxRetries: 3,
  baseDelay: 1000,
  maxDelay: 10000
});

async function createSessionWithRetry(sessionData) {
  return await retryHandler.executeWithRetry(async () => {
    return await mindpeekerClient.sessions.create(sessionData);
  });
}

Monitoring and Observability

Structured Logging

Implement comprehensive logging

const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: {
    service: 'mindpeeker-integration',
    version: process.env.APP_VERSION || '1.0.0'
  },
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({
      format: winston.format.simple()
    })
  ]
});

// Logging middleware
function requestLogger(req, res, next) {
  const requestId = req.id || generateRequestId();
  req.requestId = requestId;
  
  logger.info('Request started', {
    requestId,
    method: req.method,
    url: req.url,
    userAgent: req.get('User-Agent'),
    ip: req.ip,
    userId: req.user?.id
  });
  
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    
    logger.info('Request completed', {
      requestId,
      statusCode: res.statusCode,
      duration,
      userId: req.user?.id
    });
  });
  
  next();
}

Metrics Collection

Track key performance indicators

class MetricsCollector {
  constructor() {
    this.metrics = {
      requests: {
        total: 0,
        successful: 0,
        failed: 0,
        responseTime: []
      },
      sessions: {
        created: 0,
        completed: 0,
        failed: 0,
        averageCompletionTime: 0
      },
      errors: {
        rateLimit: 0,
        authentication: 0,
        validation: 0,
        server: 0
      }
    };
  }
  
  recordRequest(duration, success, statusCode) {
    this.metrics.requests.total++;
    this.metrics.requests.responseTime.push(duration);
    
    if (success) {
      this.metrics.requests.successful++;
    } else {
      this.metrics.requests.failed++;
      this.categorizeError(statusCode);
    }
  }
  
  categorizeError(statusCode) {
    if (statusCode === 429) {
      this.metrics.errors.rateLimit++;
    } else if (statusCode === 401 || statusCode === 403) {
      this.metrics.errors.authentication++;
    } else if (statusCode >= 400 && statusCode < 500) {
      this.metrics.errors.validation++;
    } else if (statusCode >= 500) {
      this.metrics.errors.server++;
    }
  }
  
  recordSession(action, duration = null) {
    switch (action) {
      case 'created':
        this.metrics.sessions.created++;
        break;
      case 'completed':
        this.metrics.sessions.completed++;
        if (duration) {
          this.updateAverageCompletionTime(duration);
        }
        break;
      case 'failed':
        this.metrics.sessions.failed++;
        break;
    }
  }
  
  updateAverageCompletionTime(duration) {
    const total = this.metrics.sessions.completed;
    const current = this.metrics.sessions.averageCompletionTime;
    this.metrics.sessions.averageCompletionTime = ((current * (total - 1)) + duration) / total;
  }
  
  getMetrics() {
    return {
      ...this.metrics,
      successRate: this.calculateSuccessRate(),
      averageResponseTime: this.calculateAverageResponseTime(),
      p95ResponseTime: this.calculateP95ResponseTime()
    };
  }
  
  calculateSuccessRate() {
    const total = this.metrics.requests.total;
    return total > 0 ? (this.metrics.requests.successful / total) * 100 : 0;
  }
  
  calculateAverageResponseTime() {
    const times = this.metrics.requests.responseTime;
    return times.length > 0 ? times.reduce((a, b) => a + b, 0) / times.length : 0;
  }
  
  calculateP95ResponseTime() {
    const times = [...this.metrics.requests.responseTime].sort((a, b) => a - b);
    const index = Math.floor(times.length * 0.95);
    return times[index] || 0;
  }
}

Testing Best Practices

Test Organization

Structure tests logically

// tests/unit/sessions.test.js
describe('Sessions API', () => {
  let mockClient;
  let sessionsAPI;
  
  beforeEach(() => {
    mockClient = new MockMindPeekerClient();
    sessionsAPI = new SessionsAPI(mockClient);
  });
  
  describe('createSession', () => {
    it('should create a session with valid parameters', async () => {
      // Arrange
      const sessionData = {
        type: 'remote_viewing',
        target: 'Test target',
        modality: 'visual'
      };
      
      mockClient.setMockResponse('sessions.create', {
        sessionId: 'test_123',
        status: 'initiated'
      });
      
      // Act
      const result = await sessionsAPI.createSession(sessionData);
      
      // Assert
      expect(result.sessionId).toBe('test_123');
      expect(result.status).toBe('initiated');
      expect(mockClient.requests).toHaveLength(1);
      expect(mockClient.requests[0].params).toMatchObject(sessionData);
    });
    
    it('should throw validation error for invalid parameters', async () => {
      // Arrange
      const invalidData = {
        type: 'invalid_type',
        target: '',
        modality: 'invalid_modality'
      };
      
      // Act & Assert
      await expect(sessionsAPI.createSession(invalidData))
        .rejects
        .toThrow(ValidationError);
    });
  });
});

Test Data Management

Use factories for test data

// tests/factories/session-factory.js
class SessionFactory {
  static create(overrides = {}) {
    return {
      type: 'remote_viewing',
      target: 'Test investigation target',
      modality: 'visual',
      duration_minutes: 30,
      privacy_level: 'private',
      ...overrides
    };
  }
  
  static createCompleted(overrides = {}) {
    return {
      sessionId: 'completed_session_123',
      status: 'completed',
      confidenceScore: 0.85,
      completedAt: '2025-01-15T10:30:00Z',
      results: {
        coordinates: { latitude: 40.7128, longitude: -74.0060 },
        descriptors: ['test location', 'urban environment']
      },
      ...overrides
    };
  }
  
  static createFailed(overrides = {}) {
    return {
      sessionId: 'failed_session_123',
      status: 'failed',
      error: 'Session timeout',
      ...overrides
    };
  }
}

// Usage in tests
const validSession = SessionFactory.create();
const completedSession = SessionFactory.createCompleted({
  confidenceScore: 0.92
});

Documentation Best Practices

API Documentation

Maintain comprehensive API docs

/**
 * Creates a new psychic investigation session
 * 
 * @param {Object} sessionData - Session configuration
 * @param {string} sessionData.type - Type of psychic investigation
 * @param {'remote_viewing'|'dowsing'|'automatic_writing'} sessionData.type
 * @param {string} sessionData.target - Target description (1-1000 chars)
 * @param {string} [sessionData.modality] - Psychic modality
 * @param {'visual'|'kinesthetic'|'auditory'} [sessionData.modality]
 * @param {number} [sessionData.duration_minutes=30] - Session duration in minutes
 * @param {string} [sessionData.privacy_level='private'] - Privacy level
 * @param {'public'|'private'|'classified'} [sessionData.privacy_level]
 * 
 * @returns {Promise<Session>} Created session object
 * @throws {ValidationError} When input parameters are invalid
 * @throws {AuthenticationError} When API credentials are invalid
 * @throws {RateLimitError} When rate limit is exceeded
 * 
 * @example
 * const session = await createSession({
 *   type: 'remote_viewing',
 *   target: 'Missing person investigation',
 *   modality: 'visual',
 *   duration_minutes: 45
 * });
 */
async function createSession(sessionData) {
  // Implementation
}

Support Resources