11. Message Types

Unlike other AMPS client libraries, the JavaScript library parses message data contents before delivering the message to a handler. This happens for all the supported message types.

By default, the client supports the following message types:

  • JSON;
  • FIX / NVFIX - via FixTypeHelper class;
  • Binary.

Messages of these types are automatically parsed into native JavaScript data structures and are ready for consumption.

The client library utilizes the concept of a type helper for message data processing. It’s an inteface with a set of methods for parsing and serializing message data of a paricular type.

The provided static TypeHelper class is used for fine grained control over message types. It is used to register new composite and custom message types, or replace type helpers for existing types.

Composite Message Types

The TypeHelper.compositeHelper static method handles creating and parsing composite message types:

// Register the json-xml-json-binary composite message type
TypeHelper.helper(
  // The name of the new composite message type
  'compositejxjb',

  // create the composite type helper
  TypeHelper.compositeHelper(
    'json',    // Part 1: JSON
    'xml',     // Part 2: XML
    'json',    // Part 3: JSON
    'binary'   // Part 4: Binary
  )
);

Example 11.1: Register a composite type helper

The composite type helper created with TypeHelper.compositeHelper will automatically build data to send from the array of the message parts and parse multi-part messages from the received raw data. All types used in the new composite type helper should be registered before creating it.

Building Composite Messages

Once the composite type helper was registered, it becomes very easy to build composite messages of that type. In the following example, we register a new composite message type, compositejjjb that consists of three JSON parts and one binary part. Then, we create the composite message and publish it. The type helper will take care of converting message parts into the raw message data that will be sent:

// Before creating a client, register the composite message type
TypeHelper.helper(
  'compositejjjb',
  TypeHelper.compositeHelper('json', 'json', 'json', 'binary')
);

const client = new Client('composite-sender');
await client.connect('ws://localhost:9000/amps/compositejjjb');

// create the array with message parts
const parts = [
  {id: 5, value: 'part1'},        // JSON part 1
  {value: 'part2', data: 22.22},  // JSON part 2
  {value: 'part3', data: 33.33},  // JSON part 3
  'XXXXXXXXXXXXXXXXXXXXXXXXXXX'   // Binary data
];

// Publish the composite message
client.publish('composite-topic', parts);

Example 11.2: Publish a composite message

Parsing Composite Messages

Once the composite type helper is registered, the composite messages of that type will be parsed automatically. The Message.data field will contain the array of message parts, parsed according to their types:

await client.subscribe(
  message => {
    const parts = message.data;
    console.log('Received message with', parts.length, 'parts');
    parts.forEach(part => console.log(part));
  },
  'composite-topic'
);

Example 11.3: Consume a composite message

Notice that the receiving application is written with explicit knowledge of the structure and content of the composite message type.

Custom Message Types

If a message type used in your application is not supported by default, it is possible to create a new type helper for it. Another situation in which you might need a custom type helper is when the default implementation does not fit your needs. For example, the default implementation of FIX / NVFIX message types assumes that the keys in each message are unique, thus overriding values if the same key occurs twice in the same message. If your messages contain duplicated keys and that is expected behavior, you need to override the default type helper with a custom implementation.

Each type helper is an object that must contain the following methods:

  • serialize(data: any): string[] - this method is used to serialize data in order send it to the server. All data chunks should be converted into an array of strings.
  • deserialize(data: any): any - this method deserializes data from the Server into a format that will be consumed by the message handler. It can be any format as long as the consumer of the data is aware of it.

Below we provide some examples on how to utilize TypeHelper functionality.

Create New Type Helpers

Create and register a custom type helper for XML messages:

class XMLTypeHelper {
  serialize(data) {
    return [new XMLSerializer().serializeToString(data)];
  }

  deserialize(data) {
    if (data.constructor === String) {
      return new DOMParser().parseFromString(data);
    }

    // Binary buffer, need to decode utf8
    return new DOMParser().parseFromString(
      decodeURIComponent(escape(Uint8ToString(data)))
    );
  }
}

// Register the XML helper for parsing XML messages
TypeHelper.helper('xml', new XMLTypeHelper());

Example 11.4: New Type Helper for XML Messages

Override Default Type Helpers

In case the custom parsing behavior is expected, it is possible to override the default type helper:

/**
* create a JSON type helper that does not parse JSON data into native
* JS objects keeping it in form of a string
*/
const jsonHelper = {
  serialize: data => [data],
  deserialize: data => JSON.stringify(data)
};

// override the default type helper
TypeHelper.helper('json', jsonHelper);

Example 11.5: Overriding the default type helper

More examples are provided in the JavaScript client library API Documentation.

FIX / NVFIX delimiters

In some cases the delimiter value used in FIX / NVFIX messages differs from the default (\x01). In this situation you don’t have to implement a custom type helper; instead, the custom delimiter can be set:

TypeHelper.helper('nvfix').delimiter('%01');

Example 11.6: Custom Delimiter for NVFIX