Axios ArrayBuffer Uploads for Binary Protocols

This article explains how the Axios HTTP client handles raw ArrayBuffer data for binary-based network protocols such as Protocol Buffers, MessagePack, or custom binary payloads. It covers the underlying transport mechanisms in both browser and Node.js environments, required header configurations, proper payload handling, and how to avoid common serialization pitfalls.

Environment-Specific Transport Mechanisms

Axios automatically adapts how it transmits binary data depending on the execution runtime:

  • Browser Environment: Axios utilizes the XMLHttpRequest (or the Fetch adapter in modern versions). When an ArrayBuffer is passed as the data property, Axios passes the reference directly to the native xhr.send() method without serializing it to a string. The browser handles the raw bytes natively over the wire.
  • Node.js Environment: Axios uses the native http and https modules. In Node.js, an ArrayBuffer or a TypedArray (such as Uint8Array) is internally converted into a Node.js Buffer instance before being written directly to the writable request stream via req.write().

Setting Request Headers for Binary Payloads

When transmitting raw binary protocols, you must explicitly define the Content-Type header. If omitted, Axios or the receiving server might attempt to infer the MIME type or default to application/x-www-form-urlencoded or application/json.

Common headers for binary uploads include:

  • Content-Type: application/octet-stream for generic binary streams.
  • Content-Type: application/x-protobuf for Protocol Buffers.
  • Content-Type: application/msgpack for MessagePack data.

Code Example: Sending an ArrayBuffer

import axios from 'axios';

// Create binary data
const buffer = new ArrayBuffer(8);
const view = new Int32Array(buffer);
view[0] = 42;
view[1] = 84;

async function uploadBinaryData() {
  try {
    const response = await axios.post('https://api.example.com/binary-endpoint', buffer, {
      headers: {
        'Content-Type': 'application/octet-stream',
      },
      // Ensure the response is also handled as binary if needed
      responseType: 'arraybuffer',
      // Explicit maxBodyLength setting for large binary uploads in Node.js
      maxBodyLength: Infinity,
      maxContentLength: Infinity,
    });

    console.log('Binary upload successful:', response.data);
  } catch (error) {
    console.error('Binary upload failed:', error);
  }
}

uploadBinaryData();

Handling TypedArrays vs. ArrayBuffers

Axios accepts both ArrayBuffer instances and typed array views (like Uint8Array, Float32Array, or DataView).

If you are working with a typed array that represents a sub-slice of a larger buffer, pass the typed array directly or pass the specific sliced buffer:

const uint8Array = new Uint8Array([0x01, 0x02, 0x03, 0x04]);

// Passing TypedArray directly:
await axios.post('/api', uint8Array, {
  headers: { 'Content-Type': 'application/octet-stream' }
});

// Or accessing the underlying ArrayBuffer:
await axios.post('/api', uint8Array.buffer, {
  headers: { 'Content-Type': 'application/octet-stream' }
});

Request Transformations and Interceptors

By default, Axios includes built-in request transformers in axios.defaults.transformRequest. These default transformers check whether the data is an ArrayBuffer, Buffer, FormData, or Stream. When raw binary data is detected, Axios bypasses JSON stringification and leaves the payload untouched.

If you use custom Axios instances with modified transformRequest functions, ensure your custom transformations do not convert binary payloads into strings or objects before transmission:

const instance = axios.create({
  transformRequest: [
    (data, headers) => {
      if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
        return data; // Do not transform binary data
      }
      return JSON.stringify(data);
    }
  ]
});