guardian-api-wrapper
v1.1.0
Published
Production-ready JavaScript/TypeScript wrapper for The Guardian Content API. Caching, rate limiting, pagination, validation.
Downloads
15
Maintainers
Readme
guardian-api-wrapper
Production-ready JavaScript/TypeScript wrapper for The Guardian Content API. Caching (LRU), rate limiting, pagination, validation, custom errors, and hooks.
- Caching — LRU cache (5 min TTL default), configurable or disabled.
- Rate limiting — Bottleneck (~12 req/min default for free tier), configurable or disabled.
- Pagination —
searchAll(query, options)async generator for all pages. - Validation — Dates (ISO), page/pageSize; throws
GuardianValidationError. - Custom errors —
GuardianAuthError,GuardianRateLimitError,GuardianTimeoutError,GuardianAPIError. - Hooks —
onRequest,onResponsefor logging or auth (no API key in logs). - TypeScript — Strict types, generics, full JSDoc.
- Node 14+ — Uses native
fetch(Node 18+); for Node 14–17 use a global fetch polyfill.
Table of contents
- Installation
- API key
- Quick start
- Configuration
- Usage
- API reference
- Errors
- TypeScript
- Scripts
- What to do next
- License & links
Installation
npm install guardian-api-wrapperRequirements: Node.js 14+ (native fetch in Node 18+; for 14–17 use a fetch polyfill).
API key
Security: Never commit or log your API key. Use environment variables (e.g. process.env.GUARDIAN_API_KEY) and never pass the key to hooks or loggers.
- Go to The Guardian Open Platform.
- Register and create an application.
- Copy your API key and pass it via config (see Configuration).
Rate limits: The Guardian API has usage limits. This library includes optional rate limiting (~12 req/min default); you can disable it or tune it.
Quick start
const { GuardianAPI } = require('guardian-api-wrapper');
const api = new GuardianAPI(process.env.GUARDIAN_API_KEY);
const data = await api.search('climate change', { pageSize: 5 });
console.log(data.response.results);Configuration
Constructor accepts a string (API key only) or a config object:
const api = new GuardianAPI({
apiKey: process.env.GUARDIAN_API_KEY,
baseUrl: 'https://content.guardianapis.com', // optional
timeout: 10_000, // ms, AbortController (default 10s)
cache: true, // LRU cache (default true)
cacheTtlMs: 5 * 60 * 1000, // 5 min default
cacheMaxSize: 100, // max entries
rateLimit: true, // bottleneck (default true)
rateLimitMinTimeMs: 5000, // ~12 req/min default
logger: myLogger, // optional (e.g. winston-compatible)
onRequest: (url, init) => {}, // optional hook (no key in url if you strip it)
onResponse: (url, data) => {}, // optional hook
});To disable cache or rate limiting (e.g. in tests):
const api = new GuardianAPI({ apiKey: 'key', cache: false, rateLimit: false });Usage
CommonJS
const { GuardianAPI } = require('guardian-api-wrapper');
const api = new GuardianAPI(process.env.GUARDIAN_API_KEY);
const data = await api.search('climate change', { pageSize: 5 });
console.log(data.response.results);ES modules
import GuardianAPI from 'guardian-api-wrapper';
const api = new GuardianAPI(process.env.GUARDIAN_API_KEY);
const data = await api.search('climate change', { pageSize: 5 });
console.log(data.response.results);Caching (default on)
Responses are cached by URL (API key not stored in cache key). Second identical request returns cached result within TTL.
const api = new GuardianAPI({ apiKey: process.env.GUARDIAN_API_KEY });
await api.search('brexit'); // hits API
await api.search('brexit'); // from cache (if within TTL)Pagination with searchAll
Async generator yields each page:
const api = new GuardianAPI(process.env.GUARDIAN_API_KEY);
for await (const page of api.searchAll('brexit', { pageSize: 50 })) {
console.log(page.response.results.length);
console.log(page.response.currentPage, page.response.pages);
}Hooks (logging without exposing API key)
const api = new GuardianAPI({
apiKey: process.env.GUARDIAN_API_KEY,
onRequest: (url) => {
// log path only; do not log full url (contains api-key)
const path = new URL(url).pathname;
logger.info('Guardian API request', { path });
},
onResponse: (url, data) => {
const path = new URL(url).pathname;
logger.info('Guardian API response', { path, total: data?.response?.total });
},
});API reference
All methods are async and return a Promise (except searchAll, which is an async generator).
| Method | Description |
|--------|-------------|
| search(query?, options?) | Search content. Options: page, pageSize (max 200), fromDate, toDate, tag, section, showFields, etc. |
| searchAll(query?, options?) | Async generator yielding each page of search results. |
| getContent(itemId, options?) | Get a single item by path (e.g. 'world/2023/oct/01/example'). |
| getTags(options?) | Fetch tags (options: type, q, page, pageSize). |
| getSingleTag(tagId, options?) | Fetch a single tag by ID (e.g. 'sport/rugbyunion'). |
| getSections(options?) | Fetch sections. |
| getEditions(options?) | Fetch editions. |
| getPillars(options?) | Fetch pillars. |
Search/list options use camelCase and are mapped to API kebab-case (e.g. pageSize → page-size). Dates must be YYYY-MM-DD. See The Guardian API documentation for full parameters.
Errors
The library throws custom errors (all extend GuardianError):
| Error | When |
|-------|------|
| GuardianAuthError | Invalid/missing API key (401). |
| GuardianRateLimitError | Rate limit exceeded (429). |
| GuardianTimeoutError | Request timed out (AbortController). |
| GuardianAPIError | Non-2xx, invalid JSON, or response.status === 'error'. |
| GuardianValidationError | Invalid input (e.g. bad date, pageSize > 200). |
Handle by type:
const { GuardianAPI, GuardianAuthError, GuardianRateLimitError } = require('guardian-api-wrapper');
try {
const data = await api.search('test');
} catch (err) {
if (err instanceof GuardianAuthError) {
// invalid key
} else if (err instanceof GuardianRateLimitError) {
// rate limited; consider retry after err.retryAfter
}
}TypeScript
Types are included. Use generics for responses:
import GuardianAPI, {
type SearchOptions,
type GuardianSearchResponse,
type ResponseContent,
} from 'guardian-api-wrapper';
const api = new GuardianAPI(process.env.GUARDIAN_API_KEY!);
const opts: SearchOptions = { pageSize: 10, showFields: 'all' };
const data: GuardianSearchResponse = await api.search('news', opts);
const first: ResponseContent | undefined = data.response?.results?.[0];Scripts
| Command | Description |
|--------|-------------|
| npm run build | Compile TypeScript and bundle (tsc && rollup). |
| npm test | Build then run Jest (uses jest-fetch-mock). |
| npm run lint | ESLint on src/**/*.ts. |
What to do next (todo list)
- [ ] Set
repository.urlandauthorinpackage.json. - [ ] Run
npm install && npm run build && npm test. - [ ] Use env var for API key in examples; never commit keys.
- [ ] Add CI (e.g. GitHub Actions) for build + test + lint.
- [ ] See CONTRIBUTING.md for extending the package.
License & links
- License: MIT. See LICENSE.
Guardian API:
