Source: RouterLocation/geoCode.js

import express from 'express';
import fetch from 'node-fetch';

const api = express()


// api wrapper for LocationIQ geo location api in order to protect the api key

const LOCATIONIQ_API_KEY = process.env.locationIqToken;
const LOCATIONIQ_BASE_URL = "https://eu1.locationiq.com/v1";

// Rate limiting for LocationIQ free tier (max 2 requests/second, we use 1 to be safe)
const RATE_LIMIT_MS = 1000; // 1 request per second
const MAX_QUEUE_SIZE = 10;

let lastRequestTime = 0;
let requestQueue = [];
let isProcessing = false;

/**
 * Rate limiter that ensures we don't exceed LocationIQ free tier limits
 * @param {Function} requestFn - The async function to execute
 * @returns {Promise} - Result of the request function
 */
async function rateLimitedRequest(requestFn) {
    return new Promise((resolve, reject) => {
        if (requestQueue.length >= MAX_QUEUE_SIZE) {
            reject(new Error('Rate limit queue full. Please try again later.'));
            return;
        }

        requestQueue.push({ requestFn, resolve, reject });
        processQueue();
    });
}

async function processQueue() {
    if (isProcessing || requestQueue.length === 0) {
        return;
    }

    isProcessing = true;

    while (requestQueue.length > 0) {
        const now = Date.now();
        const timeSinceLastRequest = now - lastRequestTime;

        if (timeSinceLastRequest < RATE_LIMIT_MS) {
            // Wait for the remaining time
            await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_MS - timeSinceLastRequest));
        }

        const { requestFn, resolve, reject } = requestQueue.shift();
        lastRequestTime = Date.now();

        try {
            const result = await requestFn();
            resolve(result);
        } catch (error) {
            reject(error);
        }
    }

    isProcessing = false;
}

api.get('/:place', async (req,res)=> 
{
    try {
        const result = await rateLimitedRequest(async () => {
            const url = `${LOCATIONIQ_BASE_URL}/search?key=${LOCATIONIQ_API_KEY}&q=${encodeURIComponent(req.params.place)}&format=json`;
            const response = await fetch(url);
            return await response.json();
        });
        
        if(result && result.length > 0)
        {
            res.json({success: true, result: result[0]})
        }
        else
        {
            res.json({success: false, message: 'No results found'})
        }
    } catch (error) {
        res.json({success: false, message: error.message})
    }
})


api.get('/:lat/:lng', async (req,res)=>{
    try {
        const result = await rateLimitedRequest(async () => {
            const url = `${LOCATIONIQ_BASE_URL}/reverse?key=${LOCATIONIQ_API_KEY}&lat=${req.params.lat}&lon=${req.params.lng}&format=json`;
            const response = await fetch(url);
            return await response.json();
        });
        
        if(result && result.display_name)
        {
            res.json({success:true, result: result.display_name})
        }
        else
        {
            res.json({success: false, message: 'No results found'})
        }
    } catch (error) {
        res.json({success: false, message: error.message})
    }
})

export default api