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.
npm install afpnews-apiimport { ApiCore } from 'afpnews-api'
// or CommonJS
const { ApiCore } = require('afpnews-api')<script src="https://unpkg.com/afpnews-api/dist/bundles/apicore.min.js"></script>import { ApiCore } from 'https://cdn.jsdelivr.net/npm/afpnews-api/dist/bundles/apicore.min.mjs'You need an API key or client credentials (client ID + secret) to connect. For user-authenticated requests, you also need a username and password.
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' })// 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 expiredGet the most recent documents:
const { count, documents } = await afp.latest({ lang: 'fr', tz: 'Europe/Paris' })const { count, documents } = await afp.search()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' }]
})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'] }
})Pass an array of field names to limit the returned fields:
const { documents } = await afp.search({}, ['uno', 'title', 'published'])Use searchAll() to iterate over large result sets automatically:
for await (const doc of afp.searchAll({ size: 5000, query: 'climate' })) {
console.log(doc.uno)
}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) |
const document = await afp.get('uno')Find documents similar to a given one:
const { count, documents } = await afp.mlt('uno', 'en', 10)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)Get the API field mapping:
const mapping = await afp.mapping('en')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')const { count, documents } = await afp.searchWithFilter('my-filter', {
startat: 0,
size: 50
})Retrieve an RSS/ATOM feed based on a saved filter:
const xmlContent = await afp.feed('my-filter', { size: 20 })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')Retrieve the embeddable HTML for a social story document:
const html = afp.getStoryHtml(doc)| 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.
# 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 lintMIT - see LICENSE.md