Handling gRPC-Web vs REST in Axios HTTP Client

This article provides a technical comparison of executing gRPC-Web requests versus standard REST calls using the Axios HTTP client. While standard REST calls rely on standard JSON serialization and HTTP semantics, gRPC-Web introduces binary Protocol Buffers (Protobuf) encoding, custom framing headers, and trailer parsing. The following sections break down the differences in message formatting, transport configuration, and payload decoding required to handle gRPC-Web over Axios.

Core Protocol Differences

Standard REST requests over Axios use JSON payloads, typical HTTP methods (GET, POST, PUT, DELETE), and standard HTTP status codes for error handling.

gRPC-Web operates differently:

Configuring Axios for REST vs. gRPC-Web

1. Standard REST Request

Standard REST requests require minimal configuration. Axios handles JSON serialization and deserialization automatically.

import axios from 'axios';

async function fetchUserREST(userId) {
  const response = await axios.get(`https://api.example.com/users/${userId}`, {
    headers: {
      'Accept': 'application/json',
    },
  });
  return response.data; // Parsed JSON object
}

2. gRPC-Web Request via Axios

To handle gRPC-Web manually in Axios, the request must use binary buffers for both sending and receiving, along with explicit framing and Protobuf serialization.

Request Construction:

  1. Serialize the message using compiled Protobuf definitions into a Uint8Array.
  2. Prepend the 5-byte gRPC frame header.
  3. Set responseType to 'arraybuffer' to receive raw binary data.
import axios from 'axios';
import { UserRequest, UserResponse } from './generated/user_pb';

function createGrpcFrame(payload) {
  const frame = new Uint8Array(5 + payload.length);
  // Byte 0: compression flag (0 = uncompressed)
  frame[0] = 0;
  // Bytes 1-4: big-endian message length
  const view = new DataView(frame.buffer);
  view.setUint32(1, payload.length, false);
  // Remaining bytes: serialized protobuf message
  frame.set(payload, 5);
  return frame;
}

async function fetchUserGrpc(userId) {
  const req = new UserRequest();
  req.setId(userId);
  const serialized = req.serializeBinary();
  const framedPayload = createGrpcFrame(serialized);

  const response = await axios.post('https://api.example.com/UserService/GetUser', framedPayload, {
    headers: {
      'Content-Type': 'application/grpc-web+proto',
      'X-Grpc-Web': '1',
    },
    responseType: 'arraybuffer',
  });

  return parseGrpcResponse(new Uint8Array(response.data));
}

Parsing gRPC-Web Responses:

The response data contains one or more 5-byte framed messages followed by a trailer frame (indicated by a 0x80 flag in byte 0).

function parseGrpcResponse(buffer) {
  let offset = 0;
  let responseMessage = null;

  while (offset < buffer.length) {
    const flag = buffer[offset];
    const view = new DataView(buffer.buffer, buffer.byteOffset + offset);
    const length = view.getUint32(1, false);
    offset += 5;

    const chunk = buffer.subarray(offset, offset + length);
    offset += length;

    if (flag === 0x00) {
      // Data frame
      responseMessage = UserResponse.deserializeBinary(chunk);
    } else if (flag === 0x80) {
      // Trailer frame (contains status headers as raw text)
      const trailerText = new TextDecoder().decode(chunk);
      // Process grpc-status and grpc-message from trailerText
    }
  }

  return responseMessage;
}

Key Comparison Summary

Feature Axios REST Call Axios gRPC-Web Call
HTTP Method GET, POST, PUT, DELETE, etc. POST only
Content-Type application/json application/grpc-web+proto or application/grpc-web-text
Payload Format Plain JSON or Text Binary Protobuf with 5-byte frame prefix
Response Type json (default) arraybuffer or text
Error Handling HTTP status codes (4xx, 5xx) grpc-status headers/trailers
Streaming Limited (chunked transfer) Supported for Server Streaming over single connection

Practical Recommendation

While Axios can send and receive gRPC-Web frames using custom buffer manipulation, direct implementation requires writing boilerplate for framing, trailer parsing, and metadata handling. For production environments, using dedicated clients such as @grpc/grpc-web or leveraging custom Axios transport adapters built for gRPC is standard practice.