afpnews-api
v2.4.0
Published
Node helper functions to authenticate and fetch AFP Core API
Readme
AfpNews API
A TypeScript client library for the AFP Core API. Provides authentication, document search, notification management, saved filters, and social story retrieval for both Node.js and browser environments.
Installation
Node.js
npm install afpnews-apiimport { ApiCore } from 'afpnews-api'
// or CommonJS
const { ApiCore } = require('afpnews-api')Browser (UMD)
<script src="https://unpkg.com/afpnews-api/dist/bundles/apicore.min.js"></script>Browser (ESM)
import { ApiCore } from 'https://cdn.jsdelivr.net/npm/afpnews-api/dist/bundles/apicore.min.mjs'Prerequisites
You need an API key or client credentials (client ID + secret) to connect. For user-authenticated requests, you also need a username and password.
Quick Start
import { ApiCore } from 'afpnews-api'
// Initialize with client credentials
const afp = new ApiCore({ clientId: 'your-id', clientSecret: 'your-secret' })
// Or with an API key
const afp = new ApiCore({ apiKey: 'your-api-key' })
// Optionally override the base URL
const afp = new ApiCore({ clientId: 'your-id', clientSecret: 'your-secret', baseUrl: 'https://custom-api.afp.com' })Authentication
// Anonymous authentication (uses API key or client credentials)
await afp.authenticate()
// Authenticate with user credentials
await afp.authenticate({ username: 'user', password: 'pass' })
// Listen for token changes
afp.on('tokenChanged', (token) => {
console.log(token)
// { accessToken, refreshToken, tokenExpires, authType }
})
// Token is automatically refreshed when expiredLatest Documents
Get the most recent documents:
const { count, documents } = await afp.latest({ lang: 'fr', tz: 'Europe/Paris' })Searching Documents
Basic Search
const { count, documents } = await afp.search()Search with Parameters
const { count, documents } = await afp.search({
query: 'Macron',
langs: ['fr', 'en'],
dateFrom: '2024-01-01',
dateTo: '2024-12-31',
size: 20,
sortField: 'published',
sortOrder: 'desc',
tz: 'Europe/Paris',
dateGap: '+1HOUR',
startAt: 0,
wantedFacets: { slug: { size: 10, minDocCount: 1 }, country: { size: 5, minDocCount: 1 } },
sort: [{ sortField: 'published', sortOrder: 'desc' }]
})Additional Filtering Parameters
Any extra key-value pair is passed as an additional query filter:
const { documents } = await afp.search({
query: 'climate',
country: 'fra',
urgency: 3,
slug: ['politics', 'economy']
})You can also use include/exclude syntax:
const { documents } = await afp.search({
country: { in: ['fra', 'deu'] },
product: { exclude: ['photo'] }
})Specify Response Fields
Pass an array of field names to limit the returned fields:
const { documents } = await afp.search({}, ['uno', 'title', 'published'])Paginated Search
Use searchAll() to iterate over large result sets automatically:
for await (const doc of afp.searchAll({ size: 5000, query: 'climate' })) {
console.log(doc.uno)
}Query Syntax
The query parameter supports a boolean query DSL:
| Syntax | Example |
|---|---|
| Simple term | Macron |
| Field search | title:Macron |
| AND | Macron AND Merkel |
| OR | title:Macron OR title:Merkel |
| NOT | Macron AND NOT country:fra |
| Parentheses | (title:Macron OR title:Merkel) AND country:fra |
| Quoted phrase | title:"climate change" |
| Implicit AND | Macron France (space-separated terms) |
Retrieving a Single Document
const document = await afp.get('uno')More Like This
Find documents similar to a given one:
const { count, documents } = await afp.mlt('uno', 'en', 10)Listing Facet Values
Retrieve the most used values for a specific facet:
const { count, keywords } = await afp.list('slug')
// With custom search scope and minimum document count
const { keywords } = await afp.list('country', { dateFrom: 'now-7d', langs: ['en'] }, 5)Field Mapping
Get the API field mapping:
const mapping = await afp.mapping('en')Filter Center
Manage saved search filters.
const fc = afp.filterCenter
// Create a filter
await fc.add('breaking-politics', { query: 'urgency:1', country: 'fra' })
// Update a filter
await fc.update('breaking-politics', { query: 'urgency:1 OR urgency:2' })
// Get a specific filter
const filter = await fc.get('breaking-politics')
// List all filters
const allFilters = await fc.all()
// Delete a filter
await fc.delete('breaking-politics')Search with Saved Filter
const { count, documents } = await afp.searchWithFilter('my-filter', {
startat: 0,
size: 50
})RSS/ATOM Feed
Retrieve an RSS/ATOM feed based on a saved filter:
const xmlContent = await afp.feed('my-filter', { size: 20 })Notification Center
Subscribe to real-time document notifications via mail, REST, SQS, or JMS services.
const nc = afp.notificationCenter
// Register a REST service
const serviceId = await nc.registerService({
name: 'my-webhook',
type: 'rest',
datas: { href: 'https://example.com/webhook' }
})
// Add a subscription
const subId = await nc.addSubscription('breaking-news', 'my-webhook', {
query: 'urgency:1',
langs: ['en']
})
// List services and subscriptions
const services = await nc.listServices()
const subscriptions = await nc.listSubscriptions()
const subs = await nc.subscriptionsInService('my-webhook')
// Cleanup
await nc.deleteSubscription('my-webhook', 'breaking-news')
await nc.removeSubscriptionsFromService('my-webhook', ['sub1', 'sub2'])
await nc.deleteService('my-webhook')Social Stories
Retrieve the embeddable HTML for a social story document:
const html = afp.getStoryHtml(doc)Search Parameters Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
| query | string | — | Boolean query string (see Query Syntax) |
| langs | string[] | — | Filter by language codes |
| dateFrom | string | '1980-01-01' | Start date (ISO date or relative like 'now-7d') |
| dateTo | string | 'now' | End date |
| size | number | 10 | Number of results (max 1000 per request) |
| sortField | string | 'published' | Field to sort by |
| sortOrder | 'asc' \| 'desc' | 'desc' | Sort direction |
| startAt | number | — | Offset for pagination |
| tz | string | — | Timezone (e.g. 'Europe/Paris') |
| dateGap | string | — | Date gap for facet ranges (e.g. '+1HOUR', '+1DAY') |
| wantedFacets | WantedFacets | — | Facets configuration { facetName: { size, minDocCount }, empty?: boolean } |
| sort | SortEntry[] | — | Multi-field sort [{ sortField, sortOrder }] |
Any additional key-value pairs are treated as field filters.
Development
# Install dependencies
npm install
# Build (clean + parser + types + ESM/CJS/bundles)
npm run build
# Development with auto-rebuild
npm run build:watch
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Lint
npm run lintAuthor
License
MIT - see LICENSE.md
