marketstackClient.ts•2.61 kB
// src/marketstackClient.ts
import { LRUCache } from 'lru-cache';
import { getToolTTL } from './cacheConfig.js';
export type MarketstackApiParams = {
endpoint: string; // e.g., 'eod', 'intraday', 'tickers'
[key: string]: string | number | boolean | undefined;
};
export type MarketstackClient = {
fetchApiData: (params: MarketstackApiParams) => Promise<any>;
};
type CacheOptions = {
max: number;
defaultTTL: number;
};
export const createMarketstackClient = (
apiKey: string,
cacheOptions: CacheOptions = { max: 500, defaultTTL: 60 * 60 * 1000 } // Default TTL 1 hour
): MarketstackClient => {
const cache = new LRUCache<string, any>({
max: cacheOptions.max,
ttl: cacheOptions.defaultTTL,
});
const generateCacheKey = (params: MarketstackApiParams): string => {
const { endpoint, ...otherParams } = params;
// Only include params that are actually sent to the API
const sortedParams = Object.fromEntries(Object.entries(otherParams).sort());
return `${endpoint}::${JSON.stringify(sortedParams)}`;
};
const fetchApiData = async (params: MarketstackApiParams): Promise<any> => {
const { endpoint, ...otherParams } = params;
if (!endpoint) throw new Error('endpoint is required');
const cacheKey = generateCacheKey(params);
const ttl = getToolTTL(endpoint);
// Check cache
const cached = cache.get(cacheKey);
if (cached !== undefined) {
console.log(`Cache hit for ${endpoint}`);
return cached;
}
console.log(`Cache miss for ${endpoint}`);
// Build query params for Marketstack API
const baseUrl = 'https://api.marketstack.com/v2';
const urlParams = new URLSearchParams({
access_key: apiKey,
});
for (const [k, v] of Object.entries(otherParams)) {
if (v !== undefined) urlParams.append(k, String(v));
}
const url = `${baseUrl}/${endpoint}?${urlParams.toString()}`;
const response = await fetch(url);
const data = await response.json();
// Marketstack error handling
if (!response.ok) {
if (data && data.error) {
throw new Error(`Marketstack API Error: ${data.error.code} - ${data.error.message}`);
}
throw new Error(`Marketstack API request failed: ${response.status} ${response.statusText}`);
}
// Check for specific Marketstack error structure even on 200 OK (e.g., validation errors)
if (data && data.error) {
throw new Error(`Marketstack API Error: ${data.error.code} - ${data.error.message}`);
}
cache.set(cacheKey, data, { ttl });
return data;
};
return {
fetchApiData,
};
};