Docs/SDKs

Node.js (server)

Zero runtime dependencies (uses the built-in global fetch). TypeScript-first, ships types.

Package@theaimart/adx-node
LanguageTypeScript
Distributionnpm
RequirementsNode.js >= 18 (global fetch; >= 20 recommended).

Install

npm

bash
npm install @theaimart/adx-node

Usage

Express handler serving a real end user

ts
import { Adx } from '@theaimart/adx-node';

const adx = new Adx({ apiKey: 'pk_your_publisher_key' });

app.get('/ad', async (req, res) => {
  const ad = await adx.requestAd({
    slotId: '8b1f2c3d-....-uuid',
    pageUrl: `https://yoursite.com${req.originalUrl}`,
    // Forward the END USER's context — otherwise every impression looks like one
    // server IP + UA and gets fraud-scored to a no-fill:
    userAgent: req.headers['user-agent'],
    clientIp: req.ip,
  });

  if (!ad.filled) return res.status(204).end();
  res.send(Adx.renderHtml(ad));

  // Later, when your frontend reports the ad was >=50% visible for >=1s:
  if (ad.isTrackable) await adx.reportViewable(ad.impId!);
});

Production hardening

  • SQL-filter-safe encoding of free-text params so a normal pageUrl never trips the backend WAF.
  • Fraud-safe User-Agent: ships theaimart-adx-node/<ver>, never a bot-blocklisted UA — and lets you forward the real end-user UA/IP.
  • 4-way union parsing normalizes internal / openrtb / house / no-fill into one Ad.
  • Fail-closed: any error, timeout, or non-200 -> ad.filled === false, never a throw (unless raiseOnError: true).

These behaviors come directly from the wire contract — see User-Agent rules and WAF-safe encoding for why they matter. Not getting fills? See Troubleshooting.

API

MemberNotes
new Adx({ apiKey, baseUrl?, timeoutMs?, userAgent?, transport?, raiseOnError? })Construct a client.
adx.requestAd({ slotId?, slotName?, pageUrl?, pageKeywords?, usPrivacy?, euconsentV2?, userAgent?, clientIp?, identityCookie? })Returns Promise<Ad>.
adx.reportViewable(impId, { userAgent?, clientIp? }?)Returns Promise<boolean>.
adx.clickUrl(impId)Builds a click-tracker URL (rarely needed directly).
Adx.renderHtml(ad, cssClass?)Renders an Ad to an HTML snippet, escaping all interpolated values.

The normalized Ad model

{ filled, kind ('image'|'html'|'no_fill'), source ('internal'|'openrtb'|'house'), impId, identityId, creative, auction, reason, retryAfterSeconds, isTrackable, raw }. The Transport interface is injectable — swap fetch for undici.request, or mock it in tests.

Build & test

bash
cd packages/node
npm install          # dev deps only (typescript, @types/node)
npm test             # node --test test/*.test.ts (runtime, via native TS strip)
npm run typecheck    # tsc --noEmit
npm run build        # tsc -> dist/ (.js + .d.ts)

The test suite runs with no third-party packages on Node ≥ 23.6 (native type stripping), and includes an end-to-end test against a local mock server reproducing the backend WAF.