Skip to content

Latest commit

 

History

History
298 lines (226 loc) · 10.6 KB

README.md

File metadata and controls

298 lines (226 loc) · 10.6 KB

Newsdata.io logo

Newsdata.io React.js Client

npm version npm downloads CI React License OpenAPI

Official React hooks SDK for the Newsdata.io News API. Drop-in useLatestNews, useArchiveNews, useCryptoNews, useMarketNews, useNewsCount, … hooks plus a <NewsDataProvider> to share one client. Built on the same proven core as the Node client — validation, typed errors, retries with exponential backoff — and ships first-class TypeScript definitions.

Zero runtime dependencies, no build step, React 18+ as a peer dependency.

Installation

npm install newsdataapi
# react is a peer dependency
npm install react

Quickstart

import { NewsDataProvider, useLatestNews } from 'newsdataapi';

function Headlines() {
  const { data, error, isLoading } = useLatestNews({
    q: 'bitcoin',
    country: ['us', 'gb'],
    language: 'en',
  });

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data.results.map((article) => (
        <li key={article.article_id}>
          <a href={article.link}>{article.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default function App() {
  return (
    <NewsDataProvider apiKey={process.env.REACT_APP_NEWSDATA_API_KEY}>
      <Headlines />
    </NewsDataProvider>
  );
}

Hooks

Hook Endpoint Notes
useLatestNews(params) /1/latest Real-time news
useArchiveNews(params) /1/archive Historical news
useNewsSources(params) /1/sources Available sources (single page)
useCryptoNews(params) /1/crypto Cryptocurrency news
useMarketNews(params) /1/market Market / financial news
useNewsCount(params) /1/count Aggregate counts (requires from_date, to_date)
useCryptoCount(params) /1/crypto/count Aggregate crypto counts
useMarketCount(params) /1/market/count Aggregate market counts
useNewsStream(registrationId) wss://ws.newsdata.io/ws/event Real-time stream (below)

Every hook has the same shape:

const { data, error, isLoading, refetch } = useLatestNews(params, options);
  • data — the API response (null until the first fetch resolves)
  • error — a typed NewsdataError (null on success)
  • isLoading — true while a request is in flight
  • refetch() — re-run the request; returns the underlying Promise

options

useLatestNews(params, { enabled: false })  // skip the request until enabled

options.enabled (default true) defers fetching — handy when params aren't ready (e.g. waiting on user input).

Parameter values

Values can be a single string or an array of strings (sent comma-joined), and parameter names are case-insensitive — qInTitle and qintitle are equivalent:

useLatestNews({ country: ['us', 'gb'], language: 'en', size: 20 });

Inline param objects are safe — the hook compares by value, not reference, so re-renders only re-fetch when the values change.

Real-time news (WebSocket)

useNewsStream opens a real-time connection for a registered query and accumulates the matching articles. Register the query first — the returned registration_id identifies it from then on:

import { useEffect, useState } from 'react';
import { useNewsDataClient, useNewsStream } from 'newsdataapi';

function BitcoinTicker() {
  const client = useNewsDataClient();
  const [registrationId, setRegistrationId] = useState(null);

  useEffect(() => {
    client.websocketRegister({ q: 'bitcoin', language: 'en' })
      .then(({ results }) => setRegistrationId(results.registration_id));
  }, [client]);

  // A falsy id defers connecting until registration resolves.
  const { articles, error, isConnected } = useNewsStream(registrationId);

  if (error) return <p>Stream error: {error.message}</p>;

  return (
    <>
      <p>{isConnected ? 'live' : 'connecting…'}</p>
      <ul>
        {articles.map((a) => <li key={a.article_id}>{a.title}</li>)}
      </ul>
    </>
  );
}

The hook returns { articles, latest, error, isConnected }. Articles accumulate newest first and are capped at maxArticles (default 100) so a long-lived stream can't grow without bound. The connection opens on mount, closes on unmount, and reconnects through a capped exponential backoff.

const { articles, latest } = useNewsStream(registrationId, {
  enabled: true,      // defer connecting when false
  maxArticles: 100,   // cap on retained articles
  reconnect: true,    // auto-reconnect on transient drops
});

error is set for a permanent rejection — bad API key or unknown registration_id, exhausted API credits, or too many simultaneous devices — which surfaces as NewsdataWebSocketAuthError and is not retried.

The server always accepts the handshake and then closes with code 1008 when the connection is refused, carrying one of three reasons: invalid credentials or registration not found, api limit reached, or device limit reached (more than 5 devices on one registration_id). Every other close code — including 1013 (send timeout, meaning the client read too slowly) — is transient and reconnects.

Each delivered article consumes 1 API credit per connected device.

Transient drops reconnect silently and leave error null.

Managing registered queries goes through the client: websocketRegister(params), websocketFetch(), and websocketDelete(registrationId). Registering an identical query twice rejects with a NewsdataApiError whose statusCode is 409; the existing id is at err.responseBody.results.registration_id.

Outside React, use the same NewsDataApiWebSocket class the hook wraps:

import { NewsDataApiWebSocket } from 'newsdataapi';

const ws = new NewsDataApiWebSocket(client);
for await (const response of ws.stream(registrationId)) {
  console.log(response.results);
}

Provider

Two ways to wire it up:

// 1. Let the provider construct the client.
<NewsDataProvider apiKey="..." options={{ timeout: 10_000, maxRetries: 3 }}>
  <App />
</NewsDataProvider>

// 2. Or pass your own pre-built client (full control over its lifecycle).
import { NewsDataApiClient } from 'newsdataapi';
const client = new NewsDataApiClient(apiKey);

<NewsDataProvider client={client}>
  <App />
</NewsDataProvider>

Anywhere inside the provider you can grab the client directly:

import { useNewsDataClient } from 'newsdataapi';

function ExportButton() {
  const client = useNewsDataClient();
  return <button onClick={() => client.archiveApi({ q: 'x' }).then(save)}>Export</button>;
}

Errors

All hook errors are instances of the typed hierarchy from the core SDK:

import {
  NewsdataValidationError, NewsdataAuthError, NewsdataRateLimitError,
  NewsdataApiError, NewsdataNetworkError,
} from 'newsdataapi';

if (error instanceof NewsdataRateLimitError) {
  console.log('retry after', error.retryAfter, 'seconds');
}
NewsdataError                       (catch-all base)
├── NewsdataValidationError         (.param)
├── NewsdataApiError                (.statusCode, .responseBody)
│   ├── NewsdataAuthError           (401 / 403)
│   ├── NewsdataRateLimitError      (429; .retryAfter)
│   └── NewsdataServerError         (5xx)
├── NewsdataNetworkError            (.cause)
└── NewsdataWebSocketError          (real-time stream)
    └── NewsdataWebSocketAuthError  (policy-violation close 1008)

Validation errors are thrown before the request is sent (no API quota spent) — e.g. setting q and qInTitle together, an unsupported parameter for that endpoint, or missing from_date/to_date on a count endpoint.

Direct client access

You can also use the underlying client without React — same surface as newsdata-nodejs-client:

import { NewsDataApiClient } from 'newsdataapi';

const client = new NewsDataApiClient(apiKey, { timeout: 10_000 });

// scroll: follow nextPage cursors and merge.
const merged = await client.latestApi({ q: 'news', scroll: true, maxResult: 200 });

// paginate: async generator, one page at a time.
for await (const page of client.latestApi({ q: 'news', paginate: true, maxPages: 5 })) {
  console.log(page.results.length);
}

Development

npm install
npm test          # node:test, 34 tests, no API key required

Related libraries

Official Newsdata.io clients across languages and runtimes:

Also see free news datasets for ML / NLP work.

License

MIT