← back to AbramsOS
lib/fda-fetcher.js
46 lines
// openFDA drug-enforcement (recall) lookup. Free public API — a drug NAME is not
// identifying, so no PHI leaves the box beyond the med name itself (same posture as
// checking whether a product you own was recalled). Optional OPENFDA_API_KEY raises
// the rate limit but is not required.
const BASE = 'https://api.fda.gov/drug/enforcement.json';
function isoFromFdaDate(d) {
// openFDA dates are YYYYMMDD strings
if (!d || d.length !== 8) return null;
return `${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}`;
}
function normalize(r) {
return {
recall_number: r.recall_number || null,
classification: r.classification || null,
status: r.status || null,
reason: r.reason_for_recall || null,
product_description: r.product_description || null,
recall_initiation_date: isoFromFdaDate(r.recall_initiation_date),
url: r.recall_number
? `https://www.accessdata.fda.gov/scripts/ires/index.cfm?Product=${encodeURIComponent(r.recall_number)}`
: 'https://www.fda.gov/drugs/drug-safety-and-availability/drug-recalls',
raw: r,
};
}
// Look up recalls for a single medication name. Matches brand OR generic OR
// product description. Returns [] when the FDA has no matching recall (404).
async function fetchDrugRecalls(name, { limit = 10 } = {}) {
if (!name) return [];
const q = String(name).trim().replace(/"/g, '');
const search = `openfda.brand_name:"${q}"+openfda.generic_name:"${q}"+product_description:"${q}"`;
const key = process.env.OPENFDA_API_KEY ? `&api_key=${process.env.OPENFDA_API_KEY}` : '';
const url = `${BASE}?search=${encodeURIComponent(search).replace(/%2B/g, '+')}&limit=${limit}${key}`;
const res = await fetch(url, { headers: { 'User-Agent': 'AbramsOS/1.0 (personal household admin)' } });
if (res.status === 404) return []; // openFDA returns 404 for "no matches"
if (!res.ok) throw new Error(`openFDA ${res.status}`);
const json = await res.json();
return (json.results || []).map(normalize);
}
module.exports = { fetchDrugRecalls, normalize };