Skip to content

docs(clients): document QWP in the Node.js client and the new browser client - #569

Merged
javier merged 19 commits into
mainfrom
ia_js_client
Oct 29, 2026
Merged

javier merged 19 commits into
mainfrom
ia_js_client

Conversation

@glasstiger

@glasstiger glasstiger commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Rewrites the Node.js client page for @questdb/nodejs-client 5.0.0, which ingests and queries over QWP. It covers the pooled connectQwpNodeClient() handle, Sender with ws:: and wss:: connect strings, connectQwpNodeSender() and connectQwpNodeEgress(), every column setter, compiled object-row writers, null handling, acknowledgements and transactions, durable ACK, backpressure, store-and-forward, streaming queries with the complete bind-parameter and result-value mappings, DDL and DML, timeouts and cancellation, zero-copy result views, failover with the onFailoverReset duplicate-row warning, connection events, error payloads, UDP, a production example, and a migration guide from ILP. ILP keeps a short legacy section.
  • Adds a browser client page for @questdb/browser-client: same-origin deployment behind a reverse proxy with an nginx example, applications on their own origin listed in qwp.browser.allowed.origins, session-cookie bootstrap and the auth subprotocol credential with refreshed OIDC tokens, which credentials a browser application may hold, in-memory replay, queries, the pooled client, failover, error handling, and the differences from the Node.js client.
  • Documents the qwp.browser.allowed.origins and qwp.browser.tls.termination.enabled server settings on the QWP configuration page, with defaults and reloadability taken from SHOW PARAMETERS on QuestDB 10.1.0.
  • Adds the Node.js defaults and differences to the connect-string, store-and-forward, and QWP client-behavior references: PEM-only tls_roots, single-letter size suffixes, 0 rather than off for the row and interval auto-flush triggers, sf_durability=append, the connect_timeout default, pooled slot names, and the close-drain default.
  • Marks Node.js and the browser client as Beta in the Connect overview, adds both to the client grid with a new JavaScript logo, adds the browser page to the sidebar, and updates the PGWire Node.js tip, the QWP wire-protocol introductions, and the changelog.

Every TypeScript example on both pages type-checks under strict and was run against QuestDB 10.1.0: the Node.js examples on Node.js 24, and the browser examples in headless Chromium, including session-cookie and auth authentication, an allowed cross-origin application, and a TLS-terminating nginx proxy.

Dependencies

  • Merge once @questdb/nodejs-client 5.0.0 and @questdb/browser-client 5.0.0 are on npm. npm currently serves @questdb/nodejs-client 4.2.0 and has no @questdb/browser-client.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🚀 Build success!

Latest successful preview: https://preview-569--questdb-documentation.netlify.app/docs/

Commit SHA: e706384

📦 Build generates a preview & updates the link on each commit.

@glasstiger glasstiger changed the title docs(clients): document QWP ingestion and queries in the JavaScript client docs(clients): document QWP ingestion and queries in the Node.js client Sep 30, 2026
@glasstiger glasstiger changed the title docs(clients): document QWP ingestion and queries in the Node.js client docs(clients): document QWP ingestion and queries in the Node.js and browser clients Oct 23, 2026
… client

Rewrite the Node.js client page for @questdb/nodejs-client 5.0.0, which
ingests and queries over QWP, and add a page for @questdb/browser-client.
Document the qwp.browser.allowed.origins and
qwp.browser.tls.termination.enabled server settings, and add the Node.js
defaults and differences to the shared connect-string and
store-and-forward references.
@glasstiger glasstiger changed the title docs(clients): document QWP ingestion and queries in the Node.js and browser clients docs(clients): document QWP in the Node.js client and the new browser client Oct 26, 2026
glasstiger and others added 13 commits October 26, 2026 17:04
- Node.js production example: read the feed in upstream batches, flush
  the end of each batch, and commit it upstream once it is in the
  store-and-forward journal (sf_durability=append). Create trades with
  DEDUP UPSERT KEYS(timestamp, symbol, trade_id) so replays cannot
  duplicate or merge trades.
- Document that QuestDB closes idle connections after
  http.net.connection.timeout (5 minutes) and nginx after 60 seconds.
  Add proxy_read_timeout to the nginx example, and drive the connection
  examples from all-endpoints-unreachable instead of disconnected.
- Show where onSenderError, onError, and reconnect.onEvent go on the
  pooled clients (ingress and egress, not cluster), and list the
  connection event fields on the browser page.
- Browser: explain that sessionBootstrap cannot refresh an expiring
  credential, recommend an auth function for OIDC access tokens, and
  use one in the pooled example.
- Store-and-forward concepts: the Node.js close() rejects with
  QwpSenderCloseTimeoutError instead of logging a warning.
- Connect overview: note the browser client's exceptions to
  store-and-forward and connect strings.
- Name JavaScript and TypeScript in the client page titles,
  descriptions, intros, sidebar label, and client grid tile.
… clients

The pages merged from #575 said that only a connected Java sender retries
rejected credentials. A connected Node.js sender that uses store-and-forward
or initial_connect_retry=async, and a browser sender started with
initialConnectMode "async", also keep retrying: they buffer rows, report
auth-failed on each attempt, and resume once QuestDB accepts the credential
again. Say so on the QWP client behavior, QWP ingress, client failover, and
connect string pages.

Narrow the browser session-cookie caution accordingly: a rejected
sessionBootstrap ends a query session and a sender in the default mode,
while an "async" sender keeps retrying without delivering its rows.
…he JavaScript client pages

- Wait for sender.publishedSequence after flush() in the waitForAck example:
  flushAndGetSequence() returns -1n when auto-flush already sent the rows,
  and waitForAck(-1n) resolves at once.
- Document recovering a store-and-forward journal after a crash in a
  container, where the lock names a live PID or another host name.
- Correct when the Node.js and browser clients retry rejected credentials:
  a browser sender with `auth` retries a rejection like an outage and reports
  endpoint-attempt-failed. Fix the neighbour pages that said otherwise.
- Read the quick-start row back by its timestamp, so a rerun cannot match an
  older row.
- Add a browser options table with defaults, the initialConnectMode default,
  the writer field table, and examples for designated timestamps,
  transactions, binds, timeouts, and zero-copy views. Document auth_timeout_ms
  for Node.js.
…locked journals

- QuestDB 10.1 does not see a cancellation while it streams a result faster
  than the client reads it. Explain that cancel(), timeouts, and leaving a
  for-await loop early need a positive initialCredit, and that without it the
  session fails after cancelDrainTimeoutMs (query_close_timeout_ms). Set the
  credit in the Node.js timeout example, and add
  QwpEgressQueryCancelTimeoutError and QwpEgressSessionClosedError to the
  error tables.
- Warn that a query session runs an INSERT or UPDATE again after a reconnect,
  so a statement whose reply was lost takes effect twice, and list the ways
  to avoid it.
- Explain that a batch QuestDB rejects stays in the store-and-forward journal
  and blocks it, and how to recover: fix the cause and restart, or move the
  journal aside.
Resolve conflicts with the Ruby client (#592):

- connect/overview: list Ruby alongside web browsers, and add Ruby (Beta)
  to the QWP support table next to Node.js and Browser (JavaScript).
- wire-protocols/qwp-ingress-websocket: drop Ruby from the "bring QWP to"
  examples, since it now has a client.
- wire-protocols/qwp-egress-websocket: same fix. This sentence merged
  without a textual conflict but went stale, because the Ruby client also
  runs queries over QWP.
…nt in the JavaScript client pages

- Query status table: QuestDB 10.1 never sends status 3 for queries. Bind
  value errors arrive as 5 or 6, and 11 also covers the server's query
  timeout and memory limit.
- Terminal rejections: later calls reject with QwpReplayRejectedError, and
  without store-and-forward every unacknowledged batch is lost, including
  batches flushed after the rejected one and other tables' rows in it.
- Error tables: plain Error from setters, the error close() rejects with
  when publishing fails first, QwpFailoverError on browser connects, and a
  browser error-class table.
- Idle query sessions use up their failover limits in a short outage, and
  the pool lends the failed connection once.
- Browser page: open source and Enterprise credentials and TLS, read-only
  dashboards, OIDC table creation, and a local development tip.
- qwp.browser.allowed.origins: QuestDB's own origin must be listed for auth.
- Mark the Node.js and browser clients stable in the overview and grid.
- Align the store-and-forward, connect string, client behaviour, client
  failover, and PGWire pages with the new client pages.
…client, and harden browser auth

- Row errors: at() also rejects when the auto-flush that the row triggered
  fails, and the row then stays queued. The Node.js and browser examples
  catch setter errors only and let at() propagate, so a flush failure is no
  longer reported as a skipped row that a caller would write twice.
- atNow(): the server stamps the batch, not the row. Rows flushed together
  share one timestamp and can collapse into one row on a deduplicated table.
- Node.js: recommend connectQwpNodeClient() for new services, including
  ingest-only and query-only ones with query_pool_min=0 or
  sender_pool_min=0, move The pooled client after Connecting, and run the
  query examples on pooled leases, so they take the same connect string.
- Browser: recommend one pooled client per application and move its section
  after Authentication. Choose sessionBootstrap or auth by credential
  lifetime as well as page origin. Mark the session cookie Secure at a
  TLS-terminating proxy with proxy_cookie_flags. Add a UI component example
  that shares one client, refreshes a view, and cleans up on unmount.
- Connect string and store-and-forward pages: the Node.js .lock.owner
  directory survives a crash, unlike the kernel-released flock.
- QWP egress and client behaviour pages: a browser query session that uses
  auth fails over on a rejected credential, and a Node.js store-and-forward
  or async sender retries rejections once it has connected.
… browser exposure risks

- Pooled client: connectQwpNodeClient() and a borrow that opens a
  connection reject with QwpPoolResourceError, whose cause holds the
  QwpUpgradeError or QwpReplayStoreLockedError. The pooled client,
  authentication, store-and-forward, and error sections now say so.
- lazy_connect sets initial_connect_retry=async and query_pool_min=0, so it
  rejects a positive query_pool_min, another initial_connect_retry, and
  reconnect: false under ingress.
- idle_timeout_ms and max_lifetime_ms close only idle connections above the
  pool minimum and open no replacements. The connect string reference notes
  the Node.js difference.
- Callbacks and reconnect placed under cluster apply to both sides in plain
  JavaScript instead of being ignored.
- Transactions: QuestDB acknowledges a transaction's batches only at commit,
  so a transaction must fit in the replay queue, or at() and flush() reject
  and none of its rows are committed.
- Browser: warn that the origin check is not access control, so QuestDB
  exposed without authentication opens every table. An auth function that
  throws raises endpoint-attempt-failed only, so an identity provider outage
  does not show in an all-endpoints-unreachable banner.
…origin check

- Node.js: rename "Failover and high availability" to "Reconnection and
  failover" and limit the Enterprise note to failing over between hosts.
  Reconnects, replay, re-executed queries, and the retry limits apply to a
  single open source server too.
- Both client pages: add a "Delivery is at least once" caution that points
  to DEDUP UPSERT KEYS.
- Node.js: choose the entry point by what the service does. The pooled
  client suits services that query or write from several tasks, and a
  standalone sender suits one producer, because its close() waits for
  acknowledgements and its journal is <sf_dir>/<sender_id>. The production
  example now says why it uses a standalone Sender.
- Browser: the origin check covers only /write/v4 and /read/v1 and compares
  Origin with Host. It does not protect /exec or the other REST endpoints,
  and a DNS-rebinding page gets through.
Comment thread documentation/configuration/qwp.md Resolved
Comment thread documentation/connect/clients/nodejs.md Resolved
Comment thread documentation/connect/clients/nodejs.md Resolved
Comment thread documentation/connect/clients/nodejs.md Resolved
Comment thread documentation/connect/clients/nodejs.md Resolved
…tup without a primary

- PGWire Node.js page: recommend the Node.js client for queries as well as
  ingestion, and keep PGWire for compatibility: tools and libraries that
  already speak the PostgreSQL protocol, existing pg or postgres code, and
  QuestDB versions before 10.0. pg is now recommended among the PGWire
  clients only.
- Node.js: a process that starts while no primary is reachable, for example
  during a failover when only replicas answer, fails its first connection.
  lazy_connect=on on the pooled client, or initial_connect_retry=async on a
  Sender, starts anyway and queues rows until a primary appears.
A replica answers a Node.js sender's upgrade with HTTP 421 Misdirected
Request, which the client reports as a QwpUpgradeError of kind
role-rejected.
With store-and-forward, a new Node.js sender continues the sequence of the
unacknowledged frames it recovers from its journal, so publishedSequence
and ackedSequence also count frames that an earlier process journalled.
When there is nothing to recover, the sequence starts again from 0.
…metrics

The memory queue fields in metrics.ingress do not apply to store-and-forward:
waitingMemoryReplayAppends stays 0 and memoryReplayUsedBytes is undefined,
even during an outage. pendingReplayFrames and pendingReplayBytes report the
unacknowledged backlog in either mode.
…drains

The Node.js client emits primary-unavailable only from orphan journal
drains, when every endpoint rejects the drain with HTTP 421. A sender's own
connection never reports it: when no primary is reachable, it reports
all-endpoints-unreachable.
@javier
javier merged commit 5daa0e4 into main Oct 29, 2026
3 checks passed
@javier
javier deleted the ia_js_client branch October 29, 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.

2 participants