apistack-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +24 -0
  2. package/index.d.ts +31 -0
  3. package/index.js +71 -0
  4. package/package.json +14 -0
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # apistack-sdk
2
+
3
+ Official JavaScript client for [APIStack](https://apistack.online): 100+ production APIs (email and phone validation, business days and holidays, loan and mortgage calculators, DNS/SSL/WHOIS, reference data, AI text tools) behind one API key. Free plan: 1,000 calls/month.
4
+
5
+ ```bash
6
+ npm install apistack-sdk
7
+ ```
8
+
9
+ ```js
10
+ import { APIStack } from 'apistack-sdk'
11
+
12
+ const apistack = new APIStack() // reads APISTACK_API_KEY
13
+
14
+ const email = await apistack.post('/api/utilities/email-address-validator/v1', { email: 'jane.doe@gmial.com' })
15
+ console.log(email.data.isValid, email.data.typoSuggestion) // false { suggestion: 'gmail.com', ... }
16
+
17
+ const loan = await apistack.get('/api/finance/loan-calculator/v1/payment', { principal: 350000, rate: 6.5, term: 30 })
18
+ console.log(loan.paymentCalculation.monthlyPayment) // 2212.24
19
+ ```
20
+
21
+ Every API's path, parameters and a live tester are in the [catalog](https://apistack.online/tools).
22
+ Errors throw `APIStackError` with `status`, `code` and the response `body`. 429 and 5xx responses are retried with backoff (`maxRetries`, default 2).
23
+
24
+ **AI agents:** you don't need this package. Connect the MCP server at `https://apistack.online/api/mcp/connect` instead ([setup](https://apistack.online/mcp)).
package/index.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export interface APIStackOptions {
2
+ /** Defaults to process.env.APISTACK_API_KEY */
3
+ apiKey?: string
4
+ /** Defaults to https://apistack.online */
5
+ baseUrl?: string
6
+ /** Per-request timeout, default 30000 */
7
+ timeoutMs?: number
8
+ /** Retries for network errors, 429 and 5xx; default 2 */
9
+ maxRetries?: number
10
+ }
11
+
12
+ export interface RequestOptions {
13
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
14
+ body?: unknown
15
+ query?: Record<string, string | number | boolean | null | undefined>
16
+ }
17
+
18
+ export declare class APIStackError extends Error {
19
+ status?: number
20
+ code?: string
21
+ body?: unknown
22
+ }
23
+
24
+ export declare class APIStack {
25
+ constructor(options?: APIStackOptions)
26
+ request<T = any>(path: string, options?: RequestOptions): Promise<T>
27
+ get<T = any>(path: string, query?: RequestOptions['query']): Promise<T>
28
+ post<T = any>(path: string, body?: unknown): Promise<T>
29
+ }
30
+
31
+ export default APIStack
package/index.js ADDED
@@ -0,0 +1,71 @@
1
+ const DEFAULT_BASE_URL = 'https://apistack.online'
2
+
3
+ export class APIStackError extends Error {
4
+ constructor(message, { status, code, body } = {}) {
5
+ super(message)
6
+ this.name = 'APIStackError'
7
+ this.status = status
8
+ this.code = code
9
+ this.body = body
10
+ }
11
+ }
12
+
13
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
14
+
15
+ export class APIStack {
16
+ /**
17
+ * @param {{ apiKey?: string, baseUrl?: string, timeoutMs?: number, maxRetries?: number }} [options]
18
+ */
19
+ constructor({ apiKey = globalThis.process?.env?.APISTACK_API_KEY, baseUrl = DEFAULT_BASE_URL, timeoutMs = 30000, maxRetries = 2 } = {}) {
20
+ if (!apiKey) throw new APIStackError('Missing API key: pass { apiKey } or set APISTACK_API_KEY')
21
+ this.apiKey = apiKey
22
+ this.baseUrl = baseUrl.replace(/\/$/, '')
23
+ this.timeoutMs = timeoutMs
24
+ this.maxRetries = maxRetries
25
+ }
26
+
27
+ /**
28
+ * Call any APIStack endpoint, e.g. request('/api/utilities/base64/v1', { body: { operation: 'encode', text: 'hi' } }).
29
+ * Defaults to POST when a body is given, GET otherwise. Retries 429 and 5xx responses with backoff.
30
+ */
31
+ async request(path, { method, body, query } = {}) {
32
+ const url = new URL(path.startsWith('/') ? path : `/${path}`, this.baseUrl)
33
+ for (const [key, value] of Object.entries(query || {})) {
34
+ if (value !== undefined && value !== null) url.searchParams.set(key, String(value))
35
+ }
36
+ const verb = (method || (body === undefined ? 'GET' : 'POST')).toUpperCase()
37
+
38
+ for (let attempt = 0; ; attempt++) {
39
+ let response
40
+ try {
41
+ response = await fetch(url, {
42
+ method: verb,
43
+ headers: { 'x-api-key': this.apiKey, ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },
44
+ body: body !== undefined ? JSON.stringify(body) : undefined,
45
+ signal: AbortSignal.timeout(this.timeoutMs)
46
+ })
47
+ } catch (error) {
48
+ if (attempt < this.maxRetries) { await sleep(500 * 2 ** attempt); continue }
49
+ throw new APIStackError(`Request failed: ${error.message}`)
50
+ }
51
+
52
+ const text = await response.text()
53
+ let data
54
+ try { data = text ? JSON.parse(text) : null } catch { data = text }
55
+
56
+ if (response.ok) return data
57
+ if ((response.status === 429 || response.status >= 500) && attempt < this.maxRetries) {
58
+ const retryAfter = Number(response.headers.get('retry-after'))
59
+ await sleep(retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** attempt)
60
+ continue
61
+ }
62
+ const message = (data && (data.message || (typeof data.error === 'string' ? data.error : data.error?.message))) || `HTTP ${response.status}`
63
+ throw new APIStackError(message, { status: response.status, code: data?.code || data?.error?.code, body: data })
64
+ }
65
+ }
66
+
67
+ get(path, query) { return this.request(path, { method: 'GET', query }) }
68
+ post(path, body) { return this.request(path, { method: 'POST', body }) }
69
+ }
70
+
71
+ export default APIStack
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "apistack-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official client for APIStack: 100+ developer APIs (validation, dates, finance, web/DNS/SSL, reference data, AI text) behind one key",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": { ".": { "types": "./index.d.ts", "default": "./index.js" } },
9
+ "files": ["index.js", "index.d.ts", "README.md"],
10
+ "engines": { "node": ">=18" },
11
+ "keywords": ["api", "apistack", "mcp", "ai-agents", "validation", "business-days", "finance"],
12
+ "homepage": "https://apistack.online/docs",
13
+ "license": "MIT"
14
+ }