JUHE API Marketplace
Comprehensive Documentation

API Documentation

Everything you need to integrate and use our APIs effectively with guides, references, and examples

Node.js

8 min read

This guide provides a comprehensive Node.js implementation for integrating with JUHE API, featuring complete error handling and retry logic.

Prerequisites

  • Node.js 12.x or higher
  • npm or yarn package manager
  • Basic understanding of JavaScript/TypeScript and async programming

Installation

First, install the required packages:

npm install axios dotenv node-cache

For TypeScript users (recommended):

npm install typescript @types/node @types/axios --save-dev

Client Implementation

Let's create a robust client for the JUHE API:

// juheAPI.js
const axios = require('axios');
const NodeCache = require('node-cache');

class JuheAPIClient {
  constructor(apiKey, options = {}) {
    if (!apiKey) {
      throw new Error('API key is required');
    }

    this.apiKey = apiKey;
    this.baseURL = '<https://hub.juheapi.com>';
    this.timeout = options.timeout || 10000;
    this.maxRetries = options.maxRetries || 3;

    // Setup cache if enabled
    this.cache = options.enableCache ?
      new NodeCache({ stdTTL: options.cacheTTL || 300 }) : null;

    // Create axios client
    this.client = axios.create({
      baseURL: this.baseURL,
      timeout: this.timeout,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      }
    });

    // Request interceptor for authentication
    this.client.interceptors.request.use(config => {
      config.headers.Authorization = `Bearer ${this.apiKey}`;

      if (config.method === 'get') {
        config.params = config.params || {};
        config.params.key = this.apiKey;
      }

      return config;
    });

    // Response interceptor for error handling
    this.client.interceptors.response.use(
      response => response.data,
      error => this._handleRequestError(error)
    );
  }

  async _handleRequestError(error) {
    // Extract information from error
    const { config, response } = error;

    // Get error details
    let statusCode = 500;
    let errorCode = 'UNKNOWN_ERROR';
    let errorMessage = 'An unknown error occurred';

    if (response) {
      statusCode = response.status;

      if (response.data) {
        errorCode = response.data.code || errorCode;
        errorMessage = response.data.message || errorMessage;
      }
    } else if (error.code === 'ECONNABORTED') {
      errorCode = 'TIMEOUT';
      errorMessage = 'Request timed out';
    } else if (error.code === 'ENOTFOUND') {
      errorCode = 'CONNECTION_ERROR';
      errorMessage = 'Could not connect to server';
    }

    // Implement retry logic for server errors
    if (
      config &&
      (!config.__retryCount || config.__retryCount < this.maxRetries) &&
      (!response || response.status >= 500 || response.status === 429)
    ) {
      // Initialize or increment retry count
      config.__retryCount = (config.__retryCount || 0) + 1;

      // Exponential backoff with jitter
      const delay = Math.min(
        Math.pow(2, config.__retryCount) * 100 + Math.random() * 100,
        3000
      );

      // Wait and retry
      await new Promise(resolve => setTimeout(resolve, delay));
      return this.client(config);
    }

    // If we've exhausted retries or shouldn't retry, throw a nice error
    const apiError = new Error(errorMessage);
    apiError.code = errorCode;
    apiError.statusCode = statusCode;
    apiError.response = response;

    throw apiError;
  }

  // Base HTTP methods
  async get(endpoint, params = {}, options = {}) {
    const cacheKey = options.cacheKey || `${endpoint}:${JSON.stringify(params)}`;

    // Check cache
    if (this.cache && !options.skipCache) {
      const cachedData = this.cache.get(cacheKey);
      if (cachedData) return cachedData;
    }

    const response = await this.client.get(endpoint, { params });

    // Cache the response
    if (this.cache && !options.skipCache) {
      this.cache.set(cacheKey, response);
    }

    return response;
  }

  async post(endpoint, data = {}, params = {}) {
    return this.client.post(endpoint, data, { params });
  }

  async put(endpoint, data = {}, params = {}) {
    return this.client.put(endpoint, data, { params });
  }

  async delete(endpoint, params = {}) {
    return this.client.delete(endpoint, { params });
  }

  // API-specific methods
  async getWeather(location) {
    return this.get('/weather/current', { location });
  }

  async getWeatherForecast(location, days = 5) {
    return this.get('/weather/forecast', { location, days });
  }

  async batch(requests) {
    return this.post('/batch', { requests });
  }
}

module.exports = JuheAPIClient;

Basic Usage Example

Here's how to use the client in a simple application:

// app.js
require('dotenv').config();
const JuheAPIClient = require('./juheAPI');

const client = new JuheAPIClient(process.env.JUHE_API_KEY, {
  enableCache: true,
  cacheTTL: 300, // 5 minutes
  timeout: 5000  // 5 seconds
});

async function main() {
  try {
    // Get current weather for London
    const weather = await client.getWeather('London');
    console.log('Current weather in London:');
    console.log(`Temperature: ${weather.data.current.temp_c}°C`);
    console.log(`Condition: ${weather.data.current.condition.text}`);

    // Get weather forecast for New York
    const forecast = await client.getWeatherForecast('New York', 3);
    console.log('\\n3-day forecast for New York:');
    forecast.data.forecast.forEach(day => {
      console.log(`${day.date}: ${day.condition} (${day.min_temp_c}°C - ${day.max_temp_c}°C)`);
    });

  } catch (error) {
    console.error(`Error: ${error.code} - ${error.message}`);
    if (error.statusCode === 429) {
      console.error('Rate limit exceeded. Please try again later.');
    }
  }
}

main();

Express.js Integration

Integrate the JUHE API with an Express.js application:

// server.js
const express = require('express');
const JuheAPIClient = require('./juheAPI');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;

// Create JUHE API client
const juheClient = new JuheAPIClient(process.env.JUHE_API_KEY, {
  enableCache: true
});

// Weather endpoint
app.get('/api/weather/:location', async (req, res) => {
  try {
    const { location } = req.params;
    const weather = await juheClient.getWeather(location);

    res.json({
      success: true,
      data: {
        location: weather.data.location.name,
        temperature: weather.data.current.temp_c,
        condition: weather.data.current.condition.text,
        humidity: weather.data.current.humidity
      }
    });
  } catch (error) {
    res.status(error.statusCode || 500).json({
      success: false,
      error: {
        code: error.code,
        message: error.message
      }
    });
  }
});

// Forecast endpoint
app.get('/api/weather/:location/forecast', async (req, res) => {
  try {
    const { location } = req.params;
    const { days = 5 } = req.query;

    const forecast = await juheClient.getWeatherForecast(
      location,
      parseInt(days, 10)
    );

    res.json({
      success: true,
      data: {
        location: forecast.data.location.name,
        forecast: forecast.data.forecast.map(day => ({
          date: day.date,
          condition: day.condition,
          minTemp: day.min_temp_c,
          maxTemp: day.max_temp_c,
          chanceOfRain: day.chance_of_rain
        }))
      }
    });
  } catch (error) {
    res.status(error.statusCode || 500).json({
      success: false,
      error: {
        code: error.code,
        message: error.message
      }
    });
  }
});

app.listen(port, () => {
  console.log(`Server running at <http://localhost>:${port}`);
});

Batch Processing Example

Process multiple API requests efficiently:

async function getMultipleLocations(locations) {
  try {
    // Build batch requests
    const requests = locations.map(location => ({
      method: 'GET',
      path: '/weather/current',
      params: { location }
    }));

    console.log(`Fetching weather for ${locations.length} locations in a single batch...`);

    // Send batch request
    const response = await client.batch(requests);

    // Process results
    const results = response.results.map((result, index) => {
      if (result.status === 'success') {
        const weather = result.data;
        return {
          location: locations[index],
          temperature: weather.current.temp_c,
          condition: weather.current.condition.text
        };
      } else {
        return {
          location: locations[index],
          error: `Error: ${result.code} - ${result.message}`
        };
      }
    });

    return results;
  } catch (error) {
    console.error(`Batch error: ${error.message}`);
    throw error;
  }
}

// Usage
getMultipleLocations(['London', 'New York', 'Tokyo', 'Sydney'])
  .then(results => {
    results.forEach(result => {
      if (result.error) {
        console.log(`${result.location}: ${result.error}`);
      } else {
        console.log(`${result.location}: ${result.temperature}°C, ${result.condition}`);
      }
    });
  })
  .catch(error => console.error('Failed to fetch locations:', error.message));

React Integration with Hooks

For frontend applications using React:

// useJuheAPI.js
import { useState, useEffect, useCallback } from 'react';

// Create a simple frontend API client
function createClient(apiKey) {
  const baseURL = '<https://hub.juheapi.com>';

  return {
    async get(endpoint, params = {}) {
      // Add API key to params
      const queryParams = new URLSearchParams({
        ...params,
        key: apiKey
      });

      const response = await fetch(`${baseURL}${endpoint}?${queryParams}`, {
        headers: {
          'Accept': 'application/json',
          'Authorization': `Bearer ${apiKey}`
        }
      });

      const data = await response.json();

      if (!response.ok || data.status === 'error') {
        const error = new Error(data.message || 'API request failed');
        error.code = data.code;
        error.status = response.status;
        throw error;
      }

      return data;
    }
  };
}

// Weather hook
export function useWeather(location, apiKey) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const fetchWeather = useCallback(async () => {
    if (!location) return;

    setLoading(true);
    setError(null);

    try {
      const client = createClient(apiKey);
      const result = await client.get('/weather/current', { location });
      setData(result.data);
    } catch (err) {
      setError({
        message: err.message,
        code: err.code
      });
    } finally {
      setLoading(false);
    }
  }, [location, apiKey]);

  useEffect(() => {
    fetchWeather();
  }, [fetchWeather]);

  return { data, loading, error, refetch: fetchWeather };
}

// Usage in a React component
function WeatherDisplay({ location }) {
  const API_KEY = process.env.REACT_APP_JUHE_API_KEY;
  const { data, loading, error } = useWeather(location, API_KEY);

  if (loading) return <p>Loading weather data...</p>;
  if (error) return <p>Error: {error.message}</p>;
  if (!data) return <p>No weather data available</p>;

  return (
    <div className="weather-card">
      <h2>{data.location.name}</h2>
      <div className="temperature">{data.current.temp_c}°C</div>
      <div className="condition">{data.current.condition.text}</div>
      <div className="details">
        <p>Humidity: {data.current.humidity}%</p>
        <p>Wind: {data.current.wind_kph} km/h</p>
      </div>
    </div>
  );
}

Error Handling Best Practices

Our client implements several error handling best practices:

  1. Custom Error Objects: With status code, error code, and message
  2. Automatic Retries: Using exponential backoff for transient errors
  3. Rate Limiting Handling: Special handling for 429 responses
  4. Timeouts: Configurable timeout settings
  5. Consistent Formatting: Standard error response format

Error handling example:

async function safeApiCall() {
  try {
    const result = await client.getWeather('London');
    return result;
  } catch (error) {
    // Handle specific error codes
    switch (error.code) {
      case 'RATE_LIMIT_EXCEEDED':
        console.error('Rate limit hit. Trying again later...');
        // Implement delayed retry or fallback
        break;

      case 'INVALID_API_KEY':
        console.error('API key invalid. Check configuration.');
        // Alert administrator
        break;

      case 'TIMEOUT':
        console.error('Request timed out. Network may be slow.');
        // Retry with increased timeout
        break;

      default:
        console.error(`Unexpected error: ${error.code} - ${error.message}`);
    }

    // Return fallback data or rethrow
    throw error;
  }
}

Next Steps

  • Explore Best Practices for API integration
  • Check out Java Integration examples
  • Learn about Advanced Topics for optimization