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