# How the Thrift Protocol Handles LINE API Requests in LINEJS: A Complete Technical Guide

> Discover how LINEJS employs the Thrift protocol for LINE API requests. Learn about serialization, HTTP transmission, and deserialization within this technical guide.

- Repository: [Evex  Developers/linejs](https://github.com/evex-dev/linejs)
- Tags: deep-dive
- Published: 2026-03-01

---

**LINEJS uses Apache Thrift to serialize request arguments into binary payloads, transmits them via HTTP to LINE's private API endpoints, and deserializes responses using custom `writeThrift` and `readThrift` utilities managed by the `RequestClient` class.**

The open-source **LINEJS** library (available at `evex-dev/linejs`) implements a complete Thrift-based communication layer to interact with LINE's undocumented HTTP API. Unlike typical REST clients that exchange JSON, LINEJS constructs binary Thrift structs, sends them with `application/x-thrift` content types, and parses the binary responses back into JavaScript objects. This article examines the exact mechanism—from argument serialization to error handling—based on the library's TypeScript implementation.

## Building the Thrift Request

The process begins when the client prepares a method call. LINEJS does not use IDL-generated classes; instead, it builds requests dynamically using a **NestedArray** structure that mirrors Thrift's field encoding.

### The NestedArray Structure

Each argument becomes a tuple containing the Thrift type, field ID, and value:

```typescript
// [ fieldType, fieldId, value ]
const args: NestedArray = [
  [Thrift.Type.I64, 1, 123456789n],          // long userId
  [Thrift.Type.STRING, 2, "Hello world"],   // message text
];

```

This array format allows the client to describe arbitrary Thrift structs without compile-time code generation.

### Serializing with writeThrift

Inside `RequestClient.requestCore`, the library calls `writeThrift` to convert the NestedArray into a binary payload:

```typescript
const Trequest = this.client.thrift.writeThrift(value, methodName, protocol);

```

The implementation in **[`packages/linejs/base/thrift/readwrite/write.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/write.ts)** (lines 14-45) performs three critical steps:

1. Opens a `TBufferedTransport` stream
2. Writes a protocol-specific header via `genHeader`
3. Recursively walks the NestedArray, emitting binary field markers through `writeValue` and `_writeStruct`

The resulting `Uint8Array` contains the complete Thrift message ready for transmission.

## Transmitting the Binary Payload

Once serialized, the binary payload travels via standard HTTP POST requests. The `RequestClient` manages all network communication in **[`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts)**.

### HTTP Headers and Endpoints

The client assembles mandatory headers including authentication tokens and protocol identifiers:

```typescript
const response = await this.client.fetch(
  `https://${this.endpoint}${path}`,
  {
    method: overrideMethod,
    headers: {
      "content-type": "application/x-thrift",
      "accept": "application/x-thrift",
      "x-line-access": authToken, // if authenticated
    },
    signal: AbortSignal.timeout(timeout),
    body: Trequest,
  },
);

```

All requests target LINE's `legy.line-apps.com` domain (or regional equivalents) with paths like `/S3` for Talk services. The `getHeader` method (lines 72-84 in [`request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/request/mod.ts)) handles the precise header composition required by LINE's servers.

## Parsing the Thrift Response

After receiving the HTTP response, LINEJS converts the ArrayBuffer back into structured data through the Thrift reader pipeline.

### Deserializing with readThrift

The `requestCore` method passes the raw bytes to `readThrift`:

```typescript
const parsedBody = new Uint8Array(body);
const res = this.client.thrift.readThrift(parsedBody, protocol);

```

Located in **[`packages/linejs/base/thrift/readwrite/read.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/read.ts)** (lines 22-30), this function:

- Wraps the binary data in a `TFramedTransport`
- Uses `TCompactProtocol` or `TBinaryProtocol` (specified by the `protocol` parameter) to read the message header
- Recursively reconstructs JavaScript objects via `readStruct` and `readValue`, handling nested structs, lists, maps, and sets

## Field Mapping and Error Handling

Raw Thrift responses contain numeric field IDs rather than human-readable names. LINEJS post-processes these through a renaming layer and implements LINE-specific error recovery.

### Renaming Thrift Field IDs

The `rename_data` function in **[`packages/linejs/base/thrift/rename/parser.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/rename/parser.ts)** (lines 71-133) maps numeric field IDs back to their original names:

```typescript
if (parse === true) {
  this.client.thrift.rename_data(res, square.includes(path));
}

```

This step also normalizes LINE-specific error structures like `TalkException` and `SquareException` into friendly JavaScript error objects, making the API responses intelligible to developers.

### Token Refresh and Exception Handling

The `RequestClient` inspects every response for error structs (specifically `res.data.e`). When the error code equals `MUST_REFRESH_V3_TOKEN`, the client automatically refreshes the authentication token and retries the request recursively with `isReRequest = true`. For fatal errors like `NOT_AUTHORIZED_DEVICE`, it clears stored credentials and emits an `"end"` event. This logic resides in the error-handling block of `requestCore` (lines 30-50 in [`request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/request/mod.ts)).

## Practical Implementation Examples

### Sending a Talk Message

This example demonstrates the high-level API that encapsulates the entire Thrift flow:

```typescript
import { LineClient } from "@evex/linejs";

const client = new LineClient({
  // device details and auth token configuration
});

await client.login(); // ensures auth token is set

// Build Thrift arguments for TalkService.sendMessage
const args = [
  [client.thrift.Type.I64, 1, 123456789n],          // target userId
  [client.thrift.Type.STRING, 2, "Hello from LINEJS!"], // text
];

// Use the high-level request helper
const result = await client.request.request(
  args,
  "sendMessage",
  3,            // ProtocolKey (3 = TBinaryProtocol)
  true,         // parse response
  "/S3",        // Talk endpoint
);
console.log("Message sent, server response:", result);

```

### Manual Thrift Read/Write

For advanced use cases, you can bypass `RequestClient` and use the thrift utilities directly:

```typescript
import { Thrift, Protocols } from "@evex/linejs/base/thrift/mod.ts";
import { writeThrift, readThrift } from "@evex/linejs/base/thrift/readwrite/mod.ts";

const binary = writeThrift(
  [
    [Thrift.Type.STRING, 1, "ping"],
  ],
  "pingMethod",
  Protocols[3], // Binary protocol
);

const response = await fetch("https://legy.line-apps.com/S3", {
  method: "POST",
  headers: {
    "content-type": "application/x-thrift",
    "accept": "application/x-thrift",
    "x-line-application": "...your system type...",
    "user-agent": "Line/10.13.0",
  },
  body: binary,
});

const raw = new Uint8Array(await response.arrayBuffer());
const parsed = readThrift(raw, Protocols[3]);
console.log("Parsed response:", parsed);

```

## Summary

- **Binary Serialization**: LINEJS uses `writeThrift` in [`packages/linejs/base/thrift/readwrite/write.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/write.ts) to convert NestedArray arguments into Thrift binary format.
- **HTTP Transport**: The `RequestClient` class transmits payloads with `Content-Type: application/x-thrift` to LINE's `legy.line-apps.com` endpoints.
- **Response Parsing**: `readThrift` in [`packages/linejs/base/thrift/readwrite/read.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/read.ts) deserializes binary responses back into JavaScript objects.
- **Field Mapping**: The `rename_data` function translates numeric Thrift field IDs to human-readable names and extracts error structs.
- **Automatic Recovery**: The client handles `MUST_REFRESH_V3_TOKEN` errors by refreshing tokens and retrying requests automatically.

## Frequently Asked Questions

### What protocol does LINEJS use to communicate with LINE servers?

LINEJS uses **Apache Thrift** over HTTP. According to the `evex-dev/linejs` source code, it serializes requests into binary Thrift payloads using either `TBinaryProtocol` (ProtocolKey 3) or `TCompactProtocol`, transmitted with the `application/x-thrift` content type.

### Where is the Thrift serialization logic implemented in LINEJS?

The Thrift serialization logic resides in [`packages/linejs/base/thrift/readwrite/write.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/write.ts) for encoding and [`packages/linejs/base/thrift/readwrite/read.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/read.ts) for decoding. The `writeThrift` function (lines 14-45) handles binary output, while `readThrift` (lines 22-30) handles parsing responses.

### How does LINEJS handle authentication token expiration?

When the Thrift response contains a `TalkException` with code `MUST_REFRESH_V3_TOKEN`, the `RequestClient` in [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts) automatically refreshes the access token and re-issues the request. For `NOT_AUTHORIZED_DEVICE` errors, it clears the token and emits an `"end"` event.

### Can I use LINEJS Thrift utilities without the full client?

Yes. The library exposes low-level Thrift functions in [`packages/linejs/base/thrift/readwrite/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/thrift/readwrite/mod.ts). You can import `writeThrift` and `readThrift` directly to manually serialize NestedArray structures and parse binary responses without instantiating the full `LineClient`.