Skip to content

Add real-time WebSocket support; require Node 22 - #2

Merged
arjunjain merged 1 commit into
mainfrom
feature/websocket
Aug 26, 2026
Merged

Add real-time WebSocket support; require Node 22#2
arjunjain merged 1 commit into
mainfrom
feature/websocket

Conversation

@arjunjain

@arjunjain arjunjain commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Syncs this client with python-client 0.3.0 (PR #27), which added the real-time WebSocket service.

What's here

Query management — three REST endpoints, verified against the OpenAPI 3.1 spec:

Endpoint Method
websocket/register POST (query params, no body)
websocket/fetch GET
websocket/delete DELETE

Register once, stream many times. A registered query has no date or paging filters — it matches news as it is published. Registering identical filters twice answers 409 with the existing registration_id.

Streaming over wss://ws.newsdata.io/ws/event?apikey=…&registration_id=…, with a capped exponential backoff (1s → 30s) and malformed frames skipped.

Failure model

Per the spec, the handshake is always accepted; a refused connection is then closed with code 1008 carrying one of three reasons — invalid credentials or registration not found, api limit reached, or device limit reached (>5 devices on one registration_id). Those raise the new typed auth error and are never retried. Every other close code, including 1013 (send timeout — the client read too slowly), is transient and reconnects.

⚠️ Each delivered article consumes 1 API credit per connected device.

Shared plumbing

The HTTP layer gained a per-endpoint method (it was GET-only), and the websocket endpoints are exempt from the results-present success check since their success envelope may omit results. Both touch the request path used by every endpoint — worth a look.

⚠️ Breaking: Node 18 and 20 are dropped

engines moves from >=18 to >=22, CI matrix to [22, 24]. Streaming uses the global WebSocket, which Node ships from v22 — this keeps the package dependency-free, which is the reason for the bump. Users on older runtimes can pass options.WebSocket (e.g. the ws package).

This warrants a version bump that signals the break.

Node specifics

stream(id) is an async generator; break out of the loop or call ws.close() to stop.

I originally added an HTTP probe to recover a 401/403 handshake status. The spec confirms the handshake is always accepted, so that was dead weight firing a spurious request on every failed connect — removed. There is now a test per documented 1008 reason, plus one proving 1013 reconnects.

47 tests pass under Node 22.

Sync with python-client 0.3.0. Adds the three query-management endpoints
(websocket/register, /fetch, /delete) and NewsDataApiWebSocket#stream,
an async generator over the news matching a registered query.

BREAKING: engines is now >=22 (was >=18). Streaming uses the global
WebSocket, which Node ships from v22, keeping the package dependency
free; the CI matrix moves to [22, 24]. Pass options.WebSocket to run on
an older runtime.

The server accepts every handshake and then closes with code 1008 on a
permanent rejection (invalid credentials, api limit, device limit);
those throw NewsdataWebSocketAuthError and are never retried. Every
other close code, 1013 send-timeout included, reconnects with a capped
exponential backoff.

#request() now carries a per-endpoint HTTP method, and the websocket
endpoints are exempt from the results-present success check.
@arjunjain
arjunjain merged commit 9e012e4 into main Aug 26, 2026
2 checks passed
@arjunjain
arjunjain deleted the feature/websocket branch August 26, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant