---
title: "Validating SDK WebSocket Events at Runtime"
description: "Validate a Bybit public-trade event at the application boundary while preserving decimal strings, scoped identifiers, and the original payload for recording."
canonical: "https://siebly.io/blog/websocket-data-parsing-in-javascript-a-practical-engineering-guide"
---

# Validating SDK WebSocket Events at Runtime

Validate a Bybit public-trade event at the application boundary while preserving decimal strings, scoped identifiers, and the original payload for recording.

## Technical Disclaimer

These articles are software engineering references for exchange API integrations. They are not financial, investment, legal, tax, compliance, or trading advice. Use public data, demo, testnet, or paper workflows first. Keep API credentials out of frontend code and disable withdrawal permissions for automation keys.

## Parse One Event Contract {#checking-the-object-before-you-use-it}

An SDK can decode a WebSocket frame into an object that the application still cannot use. A subscription response, a different topic, or a trade missing its identifier must not enter a trade recorder merely because JSON parsing succeeded.

Validate the fields consumed by the application after the SDK emits an event. Raw gzip handling and frame decoding belong to a different layer and are unnecessary in this consumer.

<span id="overview"></span><span id="key-takeaways"></span><span id="decoding-a-raw-frame"></span><span id="streams-and-the-websocket-api-client"></span><span id="build-the-split-and-then-stop"></span><span id="frequently-asked-questions"></span><span id="can-javascript-parse-websocket-messages-automatically"></span><span id="do-browser-and-node-js-websocket-messages-have-the-same-data-type"></span><span id="how-should-i-validate-parsed-websocket-data-in-typescript"></span><span id="should-i-parse-websocket-messages-manually-or-use-an-sdk"></span>

This module consumes only Bybit linear BTCUSDT public trades. The [public trade documentation](https://bybit-exchange.github.io/docs/v5/websocket/public/trade) defines the batch and native fields. Save it as `trade-event.mjs`:

```js title="trade-event.mjs"
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
const positiveDecimal = (value) =>
  typeof value === 'string' &&
  /^\d+(?:\.\d+)?$/.test(value) &&
  Number.isFinite(Number(value)) &&
  Number(value) > 0;

export function decodeBybitTrades(message) {
  if (!record(message) || message.topic !== 'publicTrade.BTCUSDT') return [];
  if (!Array.isArray(message.data)) throw new Error('Trade batch is not an array');

  return message.data.map((trade) => {
    if (
      !record(trade) ||
      trade.s !== 'BTCUSDT' ||
      typeof trade.i !== 'string' ||
      trade.i.length === 0 ||
      !Number.isSafeInteger(trade.T) ||
      trade.T < 0 ||
      !positiveDecimal(trade.p) ||
      !positiveDecimal(trade.v) ||
      !['Buy', 'Sell'].includes(trade.S)
    ) {
      throw new Error('Malformed BTCUSDT trade');
    }
    return {
      id: trade.i,
      symbol: trade.s,
      exchangeTime: trade.T,
      price: trade.p,
      quantity: trade.v,
      side: trade.S,
      raw: trade,
    };
  });
}
```

The module keeps prices, quantities, and IDs as strings. Numeric conversion above only checks whether a decimal is finite and positive; it does not replace the value stored by the application. Exact calculations need a representation appropriate to the instrument.

<span id="how-do-i-parse-json-from-a-websocket-message-in-javascript"></span><span id="how-do-i-test-a-websocket-parser-without-live-credentials"></span>

## Separate Unrelated and Invalid Messages {#keep-the-parser-away-from-the-socket}

An unrelated topic returns an empty list. A malformed message on the expected trade topic throws. That distinction lets a recorder report a potential data gap instead of silently treating invalid data as an idle market.

The module validates the whole batch before returning records. If one item is invalid, the caller can stop or quarantine the batch deliberately. This example chooses strict failure; a consumer choosing partial acceptance must record which items it rejected.

<span id="what-you-are-actually-parsing"></span><span id="what-happens-if-json-parse-fails-on-a-websocket-message"></span>

## Test Without Opening a Socket {#what-the-sdk-hands-you}

Pass fixtures directly to the exported function. Include an unrelated acknowledgement, a valid multi-trade batch, a large string ID, a missing ID, an invalid timestamp, and a symbol that conflicts with the topic.

Also test duplicate delivery downstream. A valid event can repeat and still pass its schema. [Deduplication](/blog/websocket-message-deduplication-in-nodejs-a-practical-guide) requires the record's identity and scope; validation alone cannot establish exactly-once storage.

The [public collector](/blog/public-market-data-collection-in-nodejs-a-practical-engineering-guide) imports this module. Other products and topics need their own contracts. In particular, a partial ticker update or order-book delta should not be validated as though it were a complete trade.

## Related articles

- [Consuming Coinbase Market and User WebSocket Feeds](/blog/implementing-a-reliable-coinbase-websocket-feed-in-nodejs)
- [Consuming Binance miniTicker Updates in Node.js](/blog/binance-mini-ticker-in-javascript-build-a-reliable-market-data-consumer)
- [Initializing and Recovering Account State](/blog/account-state-management-for-crypto-apis-nodejs-guide)


## Related Siebly Resources

- [Deduplication](/blog/websocket-message-deduplication-in-nodejs-a-practical-guide)
- [public collector](/blog/public-market-data-collection-in-nodejs-a-practical-engineering-guide)
