UNPKG

serpnode-js

Version:

Serpnode.com API client for Node.js

111 lines (92 loc) 2.96 kB
const DEFAULT_BASE_URL = "https://api.serpnode.com/v1"; function buildQueryString(params) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params || {})) { if (value === undefined || value === null) continue; if (Array.isArray(value)) { for (const v of value) { if (v === undefined || v === null) continue; searchParams.append(key, String(v)); } } else if (typeof value === "object") { searchParams.append(key, JSON.stringify(value)); } else { searchParams.append(key, String(value)); } } return searchParams.toString(); } export class SerpnodeClient { constructor(options) { const { apiKey, baseUrl = DEFAULT_BASE_URL, authInQuery = false, timeoutMs = 30000, defaultHeaders = {}, } = options || {}; if (!apiKey || typeof apiKey !== "string") { throw new Error("SerpnodeClient: 'apiKey' is required and must be a string"); } this.apiKey = apiKey; this.baseUrl = baseUrl.replace(/\/$/, ""); this.authInQuery = Boolean(authInQuery); this.timeoutMs = Number(timeoutMs) || 30000; this.defaultHeaders = { ...defaultHeaders }; } setApiKey(apiKey) { if (!apiKey || typeof apiKey !== "string") { throw new Error("SerpnodeClient.setApiKey: 'apiKey' must be a non-empty string"); } this.apiKey = apiKey; } async _get(path, params) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), this.timeoutMs); try { const allParams = { ...(params || {}) }; if (this.authInQuery) { allParams["apikey"] = this.apiKey; } const qs = buildQueryString(allParams); const url = `${this.baseUrl}/${path}${qs ? `?${qs}` : ""}`; const headers = { Accept: "application/json", ...this.defaultHeaders, }; if (!this.authInQuery) { headers["apikey"] = this.apiKey; } const response = await fetch(url, { method: "GET", headers, signal: controller.signal, }); const contentType = response.headers.get("content-type") || ""; const isJson = contentType.includes("application/json"); const body = isJson ? await response.json().catch(() => undefined) : await response.text(); if (!response.ok) { const error = new Error(`Serpnode API error: ${response.status} ${response.statusText}`); error.status = response.status; error.details = body; throw error; } return body; } finally { clearTimeout(timeout); } } async status() { return this._get("status", {}); } async search(params) { return this._get("search", params); } async options(params) { return this._get("options", params); } async locations(params) { return this._get("locations", params); } } export default SerpnodeClient;