Skip to main content

Overview

Following these best practices will help you get the most out of SnakeQuery while building reliable, performant applications. These guidelines are based on real-world usage patterns and common pitfalls.
Quick Start Checklist: Use environment variables, always implement error handling, define response schemas, and write specific queries.

๐Ÿ” Security Best Practices

API Key Management

Never expose API keys in client-side code or commit them to version control.

โœ… Correct Approaches

Environment Variables (Recommended)
# .env file
SNAKE_QUERY_API_KEY=your_api_key_here
// Node.js
const client = new SnakeQuery(process.env.SNAKE_QUERY_API_KEY);
# Python
import os
client = SnakeQuery(api_key=os.environ.get('SNAKE_QUERY_API_KEY'))
Backend Proxy for Frontend Applications
// Backend endpoint
app.post('/api/query', async (req, res) => {
  try {
    const response = await fetch('https://app.snakequery.com/api/query', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SNAKE_QUERY_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(req.body)
    });
    
    const result = await response.json();
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Frontend usage
const result = await fetch('/api/query', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: 'Find products under $100',
    data: products
  })
});

โŒ Common Mistakes

// DON'T: Hardcode API keys
const client = new SnakeQuery('sk-1234567890abcdef...');

// DON'T: Expose in frontend JavaScript
const apiKey = 'sk-1234567890abcdef...';
fetch('https://app.snakequery.com/api/query', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

// DON'T: Commit to version control
git add .env  // Contains API keys

Input Validation

Always validate and sanitize user-provided data:
function validateQuery(userInput) {
  // Validate query length
  if (!userInput || userInput.length > 1000) {
    throw new Error('Invalid query length');
  }
  
  // Sanitize potentially dangerous content
  const sanitized = userInput.replace(/[<>\"']/g, '');
  
  return sanitized;
}

// Usage
const userQuery = validateQuery(req.body.query);
const result = await client.query({
  query: userQuery,
  data: safeData
});

๐Ÿ“Š Schema Design Best Practices

Always Use Schemas in Production

Schemas provide type safety, validation, and consistent response structures essential for production applications.

โœ… Production-Ready Schema

const productSchema = {
  type: 'array',
  items: {
    type: 'object',
    properties: {
      productId: { type: 'string' },
      name: { type: 'string', minLength: 1 },
      price: { type: 'number', minimum: 0, maximum: 999999 },
      category: { type: 'string' },
      inStock: { type: 'boolean' }
    },
    required: ['productId', 'name', 'price']
  }
};

const result = await client.query({
  query: 'Find available products under $500',
  data: products,
  responseSchema: productSchema  // Ensures consistent structure
});

โŒ Avoid Free-Form Responses in Production

// DON'T: No schema in production
const result = await client.query({
  query: 'Find products',
  data: products
  // Missing responseSchema - unpredictable response structure
});

Schema Versioning

As your application evolves, version your schemas:
const productSchemaV1 = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    price: { type: 'number' }
  }
};

const productSchemaV2 = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    price: { type: 'number' },
    currency: { type: 'string', enum: ['USD', 'EUR', 'GBP'] },
    availability: {
      type: 'object',
      properties: {
        inStock: { type: 'boolean' },
        quantity: { type: 'integer', minimum: 0 }
      }
    }
  },
  required: ['name', 'price', 'currency']
};

// Use version-specific schemas
const currentSchema = productSchemaV2;

โšก Performance Optimization

Query Specificity

Write specific, focused queries for better performance and accuracy:

โœ… Specific Queries

// GOOD: Specific and focused
const result = await client.query({
  query: 'Find the 5 most expensive electronics products under $1000, show name, price, and brand',
  data: products,
  responseSchema: topProductsSchema
});

// GOOD: Clear constraints
const result = await client.query({
  query: 'Show products added in the last 30 days with ratings above 4 stars',
  fetchUrl: 'https://api.store.com/recent-products',
  responseSchema: recentProductsSchema
});

โŒ Overly Broad Queries

// BAD: Too vague and broad
const result = await client.query({
  query: 'Show me everything about all products',
  data: products
});

// BAD: No constraints
const result = await client.query({
  query: 'Find products',
  fetchUrl: 'https://api.store.com/products'
});

Data Source Selection

Choose the appropriate data source for your query:

Direct Data (Best for Small Datasets)

// Use for arrays with < 1000 items
const smallProductList = products.slice(0, 100);

const result = await client.query({
  query: 'Find products by category',
  data: smallProductList,
  responseSchema: schema
});

URL Endpoints (Best for Large Datasets)

// Use for large datasets or when data is already available via API
const result = await client.query({
  query: 'Analyze sales trends over the last year',
  fetchUrl: 'https://api.company.com/sales/yearly-data',
  responseSchema: analyticsSchema
});

Result Caching

Implement caching for frequently used queries:
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function cachedQuery(queryOptions) {
  const cacheKey = JSON.stringify(queryOptions);
  const cached = cache.get(cacheKey);
  
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.result;
  }
  
  const result = await client.query(queryOptions);
  cache.set(cacheKey, {
    result,
    timestamp: Date.now()
  });
  
  return result;
}

// Usage
const result = await cachedQuery({
  query: 'Get product categories with counts',
  fetchUrl: 'https://api.store.com/products'
});

๐Ÿšจ Error Handling

Comprehensive Error Handling

Always implement robust error handling:
async function robustQuery(queryOptions, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await client.query(queryOptions);
      return result;
    } catch (error) {
      console.error(`Query attempt ${attempt} failed:`, error.message);
      
      // Handle specific error types
      if (error.status === 401) {
        throw new Error('Invalid API key - check your credentials');
      }
      
      if (error.status === 402) {
        throw new Error('Insufficient credits - please upgrade your plan');
      }
      
      if (error.status === 429) {
        // Rate limit exceeded - wait and retry
        if (attempt < maxRetries) {
          console.log(`Rate limited, waiting ${attempt * 1000}ms before retry...`);
          await new Promise(resolve => setTimeout(resolve, attempt * 1000));
          continue;
        }
      }
      
      if (error.status >= 500 && attempt < maxRetries) {
        // Server error - retry with exponential backoff
        const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
        console.log(`Server error, retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      // Re-throw if not retryable or max retries reached
      throw error;
    }
  }
}

// Usage with error handling
try {
  const result = await robustQuery({
    query: 'Analyze customer data',
    fetchUrl: 'https://api.company.com/customers'
  });
  
  console.log('Success:', result.response);
} catch (error) {
  console.error('Query failed permanently:', error.message);
  // Handle gracefully - show user-friendly message, use default data, etc.
}

Error Response Handling

Handle different error scenarios appropriately:
function handleQueryError(error) {
  switch (error.status) {
    case 400:
      return {
        userMessage: 'Please refine your search query',
        action: 'retry_with_different_query'
      };
      
    case 401:
      return {
        userMessage: 'Authentication failed',
        action: 'check_api_key'
      };
      
    case 402:
      return {
        userMessage: 'Usage limit reached',
        action: 'upgrade_plan'
      };
      
    case 429:
      return {
        userMessage: 'Too many requests, please wait',
        action: 'retry_later'
      };
      
    case 504:
      return {
        userMessage: 'Query too complex, please simplify',
        action: 'simplify_query'
      };
      
    default:
      return {
        userMessage: 'Service temporarily unavailable',
        action: 'try_again_later'
      };
  }
}

Health Checks

Monitor SnakeQuery API health:
async function healthCheck() {
  try {
    const result = await client.query({
      query: 'Count items',
      data: [{ test: 'item' }],
      responseSchema: { type: 'number' }
    });
    
    return {
      status: 'healthy',
      responseTime: Date.now() - start,
      tokensUsed: result.usageCount.totalTokens
    };
  } catch (error) {
    return {
      status: 'unhealthy',
      error: error.message,
      timestamp: new Date().toISOString()
    };
  }
}

// Run health checks periodically
setInterval(async () => {
  const health = await healthCheck();
  console.log('SnakeQuery Health:', health);
}, 5 * 60 * 1000); // Every 5 minutes

Request Deduplication

Avoid duplicate queries:
const activeRequests = new Map();

async function deduplicatedQuery(queryOptions) {
  const requestKey = JSON.stringify(queryOptions);
  
  // Return existing promise if request is already in progress
  if (activeRequests.has(requestKey)) {
    return activeRequests.get(requestKey);
  }
  
  // Create new request
  const requestPromise = client.query(queryOptions)
    .finally(() => {
      // Clean up after completion
      activeRequests.delete(requestKey);
    });
  
  activeRequests.set(requestKey, requestPromise);
  return requestPromise;
}

๐Ÿ“ˆ Monitoring and Analytics

Usage Tracking

Track your SnakeQuery usage patterns:
class SnakeQueryMonitor {
  constructor(client) {
    this.client = client;
    this.metrics = {
      totalQueries: 0,
      totalTokens: 0,
      errorCount: 0,
      responseTimeSum: 0,
      queryPatterns: {}
    };
  }
  
  async query(queryOptions) {
    const startTime = Date.now();
    const queryPattern = this.extractPattern(queryOptions.query);
    
    try {
      const result = await this.client.query(queryOptions);
      
      // Track success metrics
      this.metrics.totalQueries++;
      this.metrics.totalTokens += result.usageCount.totalTokens;
      this.metrics.responseTimeSum += Date.now() - startTime;
      this.metrics.queryPatterns[queryPattern] = 
        (this.metrics.queryPatterns[queryPattern] || 0) + 1;
      
      return result;
    } catch (error) {
      this.metrics.errorCount++;
      throw error;
    }
  }
  
  extractPattern(query) {
    // Extract query pattern for analytics
    return query.toLowerCase()
      .replace(/\d+/g, 'N')
      .replace(/"[^"]*"/g, '"STRING"')
      .trim();
  }
  
  getAnalytics() {
    return {
      ...this.metrics,
      averageResponseTime: this.metrics.responseTimeSum / this.metrics.totalQueries,
      errorRate: this.metrics.errorCount / this.metrics.totalQueries,
      averageTokensPerQuery: this.metrics.totalTokens / this.metrics.totalQueries
    };
  }
}

// Usage
const monitoredClient = new SnakeQueryMonitor(client);

Cost Optimization

Monitor and optimize API costs:
function calculateCost(usageCount, pricePerToken = 0.001) {
  return {
    inputCost: usageCount.promptTokens * pricePerToken,
    outputCost: usageCount.completionTokens * (pricePerToken * 2),
    totalCost: usageCount.totalTokens * pricePerToken
  };
}

async function costAwareQuery(queryOptions, maxCostCents = 10) {
  const result = await client.query(queryOptions);
  const cost = calculateCost(result.usageCount);
  
  if (cost.totalCost * 100 > maxCostCents) {
    console.warn(`High cost query: $${cost.totalCost.toFixed(4)}`);
  }
  
  return {
    ...result,
    estimatedCost: cost
  };
}

๐Ÿงช Testing Best Practices

Unit Testing

Test your SnakeQuery integration:
// Mock for testing
const mockSnakeQuery = {
  query: jest.fn()
};

describe('Product Search', () => {
  beforeEach(() => {
    mockSnakeQuery.query.mockReset();
  });
  
  test('should handle successful product search', async () => {
    const mockResponse = {
      response: [{ name: 'iPhone', price: 999 }],
      usageCount: { totalTokens: 100 }
    };
    
    mockSnakeQuery.query.mockResolvedValue(mockResponse);
    
    const result = await searchProducts('phones under $1000', mockSnakeQuery);
    
    expect(result).toEqual(mockResponse.response);
    expect(mockSnakeQuery.query).toHaveBeenCalledWith({
      query: 'phones under $1000',
      data: expect.any(Array),
      responseSchema: expect.any(Object)
    });
  });
  
  test('should handle API errors gracefully', async () => {
    mockSnakeQuery.query.mockRejectedValue(new Error('API Error'));
    
    const result = await searchProducts('test query', mockSnakeQuery);
    
    expect(result).toEqual([]);  // Should return empty array on error
  });
});

Integration Testing

Test with real API in staging:
describe('SnakeQuery Integration', () => {
  const testClient = new SnakeQuery(process.env.SNAKE_QUERY_TEST_API_KEY);
  
  test('should query test data successfully', async () => {
    const testData = [
      { name: 'Test Product', price: 100, category: 'test' }
    ];
    
    const result = await testClient.query({
      query: 'Find products under $200',
      data: testData,
      responseSchema: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            name: { type: 'string' },
            price: { type: 'number' }
          }
        }
      }
    });
    
    expect(result.response).toHaveLength(1);
    expect(result.usageCount.totalTokens).toBeGreaterThan(0);
  });
});

๐Ÿ“‹ Development Checklist

Before deploying to production, ensure:

Security โœ…

  • API keys stored in environment variables
  • No hardcoded credentials in code
  • Input validation implemented
  • Backend proxy for frontend access

Error Handling โœ…

  • Comprehensive try-catch blocks
  • Retry logic for transient failures
  • User-friendly error messages
  • Fallback behavior defined

Performance โœ…

  • Response schemas defined
  • Query specificity optimized
  • Caching implemented for frequent queries
  • Appropriate data source selection

Monitoring โœ…

  • Usage tracking implemented
  • Cost monitoring in place
  • Health checks configured
  • Alert systems set up

Testing โœ…

  • Unit tests for query functions
  • Integration tests with real API
  • Error scenario testing
  • Performance benchmarking

Common Pitfalls to Avoid

1. Schema Mismatches

// DON'T: Expect different structure than schema
const schema = {
  type: 'array',
  items: { type: 'string' }
};

// Query expects objects but schema defines strings
const result = await client.query({
  query: 'Show product names and prices',  // Returns objects
  data: products,
  responseSchema: schema  // Expects strings
});

2. Overly Complex Queries

// DON'T: Combine too many operations
const result = await client.query({
  query: 'Find products, group by category, calculate averages, sort by ratings, show trends, and predict future sales',
  data: products
});

// DO: Break into separate queries
const categorized = await client.query({
  query: 'Group products by category with averages',
  data: products,
  responseSchema: categorySchema
});

const trends = await client.query({
  query: 'Analyze sales trends',
  data: salesData,
  responseSchema: trendSchema
});

Next Steps

โŒ˜I