Skip to main content

JavaScript Examples

This page provides comprehensive JavaScript examples for both Node.js and browser environments. All examples demonstrate best practices for authentication, error handling, and context retrieval.

Node.js Setup

npm install node-fetch dotenv
// config.js

export const CONFIG = {
  BASE_URL: "https://www.goharvest.ai/api/v1/developer",
  API_KEY: process.env.HARVEST_API_KEY,
  HEADERS: {
    "API-Key": process.env.HARVEST_API_KEY,
    "Content-Type": "application/json",
  },
};

Complete Context Retrieval Client

// HarvestContextClient.js

import { CONFIG } from './config.js';

export class HarvestContextClient {
  constructor(apiKey = CONFIG.API_KEY) {
    this.apiKey = apiKey;
    this.baseUrl = CONFIG.BASE_URL;
    this.headers = {
      'API-Key': apiKey,
      'Content-Type': 'application/json'
    };
  }

  async listIndexes() {
    /**
     * List all available indexed services
     */
    const url = `${this.baseUrl}/list-indexes`;

    try {
      const response = await fetch(url, {
        headers: this.headers,
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const result = await response.json();

      if (result.ok) {
        console.log(`✅ Found ${result.data.indexes.length} indexed services`);
        return result.data.indexes;
      } else {
        console.error(`❌ Error: ${result.error}`);
        return [];
      }
    } catch (error) {
      console.error('❌ Error listing indexes:', error);
      return [];
    }
  }

  async getContext(indexId, query) {
    /**
     * Retrieve context from a specific indexed service
     */
    const url = `${this.baseUrl}/get-context`;
    const payload = {
      query: query,
      index_id: indexId
    };

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: this.headers,
        body: JSON.stringify(payload),
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const result = await response.json();

      if (result.ok) {
        const context = result.data.context;
        console.log(`✅ Retrieved context (${context.length} characters)`);
        return context;
      } else {
        console.error(`❌ Error: ${result.error}`);
        return null;
      }
    } catch (error) {
      console.error('❌ Error getting context:', error);
      return null;
    }
  }

  async searchByServiceName(serviceName, query) {
    /**
     * Find a service by name and retrieve context
     */
    const indexes = await this.listIndexes();

    // Find matching service (case-insensitive)
    const matchingService = indexes.find(idx =>
      idx.name.toLowerCase().includes(serviceName.toLowerCase())
    );

    if (!matchingService) {
      console.error(`❌ Service '${serviceName}' not found`);
      console.log(`Available services: ${indexes.map(idx => idx.name).join(', ')}`);
      return null;
    }

    console.log(`🔍 Found service: ${matchingService.name} (${matchingService.type})`);
    return this.getContext(matchingService.index_id, query);
  }

  async queryMultipleServices(query, serviceTypes = null) {
    /**
     * Query multiple services with the same question
     * Optionally filter by service types (e.g., ['SDK', 'API'])
     */
    const indexes = await this.listIndexes();

    // Filter by type if specified
    const filteredIndexes = serviceTypes
      ? indexes.filter(idx => serviceTypes.includes(idx.type))
      : indexes;

    console.log(`🔍 Querying ${filteredIndexes.length} services for: "${query}"`);

    const results = [];

    for (const index of filteredIndexes) {
      console.log(`\n📖 Querying ${index.name} (${index.type})...`);
      const context = await this.getContext(index.index_id, query);

      results.push({
        service: index.name,
        type: index.type,
        index_id: index.index_id,
        context: context,
        success: context !== null
      });
    }

    return results;
  }
}

// Usage examples
async function main() {
  const client = new HarvestContextClient();

  // Example 1: List all available services
  console.log("Example 1: Listing all services\n");
  const indexes = await client.listIndexes();
  indexes.forEach(idx => {
    console.log(`  - ${idx.name} (${idx.type}): ${idx.index_id}`);
  });

  // Example 2: Get context from first available service
  if (indexes.length > 0) {
    console.log("\nExample 2: Getting context\n");
    const firstIndex = indexes[0];
    const context = await client.getContext(
      firstIndex.index_id,
      "how to make authenticated POST request"
    );
    if (context) {
      const preview = context.length > 500 ? context.substring(0, 500) + "..." : context;
      console.log(preview);
    }
  }

  // Example 3: Search by service name
  console.log("\nExample 3: Search by service name\n");
  const context = await client.searchByServiceName(
    "Python",
    "how to handle exceptions"
  );

  // Example 4: Query all SDK services
  console.log("\nExample 4: Query multiple services by type\n");
  const results = await client.queryMultipleServices(
    "authentication example",
    ['SDK', 'API']
  );

  console.log("\n📊 Results summary:");
  results.forEach(result => {
    const status = result.success ? "✅" : "❌";
    const length = result.context ? result.context.length : 0;
    console.log(`${status} ${result.service}: ${length} characters`);
  });
}

// Run if this is the main module
if (import.meta.url === `file://${process.argv[1]}`) {
  main().catch(console.error);
}

Browser-Based Example

For client-side applications:
<!DOCTYPE html>
<html>
<head>
    <title>Harvest Context Demo</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
        select, input, button { padding: 10px; margin: 5px 0; width: 100%; }
        #result { background: #f5f5f5; padding: 20px; margin-top: 20px; white-space: pre-wrap; }
        .loading { color: #666; font-style: italic; }
    </style>
</head>
<body>
    <h1>Harvest Context API Demo</h1>

    <div>
        <label>Service:</label>
        <select id="serviceSelect">
            <option>Loading services...</option>
        </select>
    </div>

    <div>
        <label>Query:</label>
        <input type="text" id="queryInput" placeholder="e.g., how to authenticate">
    </div>

    <button onclick="getContext()">Get Context</button>

    <div id="result"></div>

    <script>
        const API_KEY = 'your-api-key-here'; // In production, use secure method
        const BASE_URL = 'https://www.goharvest.ai/api/v1/developer';

        let indexes = [];

        // Load services on page load
        window.onload = async () => {
            try {
                const response = await fetch(`${BASE_URL}/list-indexes`, {
                    headers: { 'API-Key': API_KEY }
                });
                const result = await response.json();

                if (result.ok) {
                    indexes = result.data.indexes;
                    const select = document.getElementById('serviceSelect');
                    select.innerHTML = indexes.map((idx, i) =>
                        `<option value="${i}">${idx.name} (${idx.type})</option>`
                    ).join('');
                }
            } catch (error) {
                document.getElementById('serviceSelect').innerHTML =
                    '<option>Error loading services</option>';
                console.error(error);
            }
        };

        async function getContext() {
            const select = document.getElementById('serviceSelect');
            const queryInput = document.getElementById('queryInput');
            const resultDiv = document.getElementById('result');

            const selectedIndex = indexes[select.value];
            const query = queryInput.value;

            if (!query) {
                alert('Please enter a query');
                return;
            }

            resultDiv.innerHTML = '<div class="loading">Loading context...</div>';

            try {
                const response = await fetch(`${BASE_URL}/get-context`, {
                    method: 'POST',
                    headers: {
                        'API-Key': API_KEY,
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        query: query,
                        index_id: selectedIndex.index_id
                    })
                });

                const result = await response.json();

                if (result.ok) {
                    resultDiv.textContent = result.data.context;
                } else {
                    resultDiv.innerHTML = `<div style="color: red;">Error: ${result.error}</div>`;
                }
        } catch (error) {
                resultDiv.innerHTML = `<div style="color: red;">Error: ${error.message}</div>`;
            }
        }
    </script>
</body>
</html>

Advanced: Context Caching

import fs from 'fs/promises';
import path from 'path';
import crypto from 'crypto';

class CachedHarvestClient extends HarvestContextClient {
  constructor(apiKey, cacheDir = '.harvest_cache', cacheTTLHours = 24) {
    super(apiKey);
    this.cacheDir = cacheDir;
    this.cacheTTL = cacheTTLHours * 60 * 60 * 1000; // Convert to milliseconds
  }

  async ensureCacheDir() {
    try {
      await fs.mkdir(this.cacheDir, { recursive: true });
    } catch (error) {
      console.error('Failed to create cache directory:', error);
    }
  }

  getCacheKey(indexId, query) {
    const content = `${indexId}:${query}`;
    return crypto.createHash('md5').update(content).digest('hex');
  }

  getCachePath(cacheKey) {
    return path.join(this.cacheDir, `${cacheKey}.json`);
  }

  async readCache(cacheKey) {
    const cachePath = this.getCachePath(cacheKey);

    try {
      const data = await fs.readFile(cachePath, 'utf-8');
      const cached = JSON.parse(data);

      const age = Date.now() - cached.timestamp;
      if (age < this.cacheTTL) {
        console.log(`✅ Cache hit (age: ${Math.round(age / 1000 / 60)} minutes)`);
        return cached.context;
    } else {
        console.log(`⚠️  Cache expired`);
        return null;
      }
    } catch (error) {
      // Cache miss or read error
      return null;
    }
  }

  async writeCache(cacheKey, context) {
    await this.ensureCacheDir();
    const cachePath = this.getCachePath(cacheKey);

    try {
      const data = JSON.stringify({
        timestamp: Date.now(),
        context: context
      });
      await fs.writeFile(cachePath, data);
      console.log(`💾 Cached result`);
    } catch (error) {
      console.error('⚠️  Cache write error:', error);
    }
  }

  async getContext(indexId, query, useCache = true) {
    const cacheKey = this.getCacheKey(indexId, query);

    // Try cache first
    if (useCache) {
      const cachedContext = await this.readCache(cacheKey);
      if (cachedContext) {
        return cachedContext;
      }
    }

    // Fetch from API
    const context = await super.getContext(indexId, query);

    if (context) {
      await this.writeCache(cacheKey, context);
    }

    return context;
  }
}

Environment Variables

Create a .env file:
# .env
HARVEST_API_KEY=your-api-key-here

Package.json

{
  "name": "harvest-context-client",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node main.js"
  },
  "dependencies": {
    "node-fetch": "^3.3.0",
    "dotenv": "^16.0.3"
  }
}

Next Steps

I