# Architecture of BaseClient and Its Services in LINEJS: Core Design Explained

> Explore the core architecture of BaseClient in LINEJS. Understand how this central class connects utilities and exposes typed services for LINE API endpoints.

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

---

**LINEJS is built around a single `BaseClient` class that wires together low-level utilities and exposes typed service objects to implement the LINE API endpoints.**

The LINEJS library (evex-dev/linejs) provides a TypeScript SDK for the LINE platform. Understanding the **BaseClient and its services architecture** is essential for extending the client or debugging request flows, as it separates cross-cutting concerns like encryption and polling from domain-specific API implementations.

## BaseClient as the Core Orchestrator

### Entry Point and Event System

The nucleus of the SDK is defined in [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts) at line 101. Here, the `BaseClient` class extends `TypedEventEmitter` to emit high-level events such as `log`, `update:authtoken`, and various LINE event streams. Because services hold a reference to the client, any service can raise events that bubble up to the user-facing `Client` wrapper (see `Client.listen` in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts)).

### Constructor Responsibilities

The constructor (lines 152-160) initializes `deviceDetails` and `endpoint` configurations. Between lines 176-186, it instantiates eight typed services: `auth`, `call`, `channel`, `liff`, `livetalk`, `relation`, `square`, and `talk`. It also initializes utility modules including `request`, `e2ee` (end-to-end encryption), `obs`, `timeline`, `poll`, `push`, and `storage`.

For stateful request tracking, the `getReqseq` method (lines 204-218) generates per-service sequence numbers and persists them via the storage abstraction. The client supports pluggable HTTP stacks through a private `#customFetch` field; the public `fetch` wrapper (lines 220-232) forwards to either the injected fetch or `globalThis.fetch`.

## Service Abstraction Layer

### The BaseService Contract

All services share a minimal contract defined in [`packages/linejs/base/service/types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/types.ts) (lines 1-9):

```ts
export interface BaseService {
    client: BaseClient;
    protocolType: ProtocolKey;
    requestPath: string;
    errorName: string;
}

```

Each concrete service stores a reference to the owning `BaseClient` and provides typed methods that wrap the low-level `RequestClient.request` call.

### AuthService Implementation

`AuthService` in [`packages/linejs/base/service/auth/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/auth/mod.ts) (line 8) implements the contract with `protocolType` set to `4`, `requestPath` set to `"/AS4"`, and `errorName` set to `"AuthServiceError"`.

Key methods include:

- **`tryRefreshToken`** (lines 20-33): Loads the stored refresh token, calls the low-level `refresh` method, updates `authToken`, and emits `update:authtoken`.
- **`refresh`** (lines 36-45): Makes the actual request to `"/EXT/auth/tokenrefresh/v1"` using `LINEStruct.refresh_args`.
- **`openSession`** (lines 84-94): Opens a new LINE session with the authentication servers.

### TalkService for Messaging

`TalkService` at [`packages/linejs/base/service/talk/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/talk/mod.ts) (line 10) handles messaging operations. The `sync` method (lines 30-62) fetches event batches from the `"/SYNC4"` endpoint. The `sendMessage` method (lines 80-71) builds a `LINEStruct.sendMessage_args` payload, transparently handles E2EE encryption, and retries automatically on encryption errors.

## Request Flow and Event Handling

The interaction flow follows four stages:

1. **Construction**: `new BaseClient({device, version, ...})` builds core utilities and all service objects.
2. **Login**: `client.loginProcess.login(...)` (handled in [`packages/linejs/base/login/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/login/mod.ts)) populates `client.authToken` and initializes the push connection.
3. **API Call**: When a consumer calls `client.talk.sendMessage(...)`, the call delegates to the `TalkService` instance. The service builds a Thrift request and calls `client.request.request`, which uses the fetch wrapper and injects required headers (auth token, device info, etc.).
4. **Event Listening**: `client.listen({talk:true, square:true})` creates a `Polling` object via `client.createPolling()`. The `Polling` class (in [`packages/linejs/base/polling/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/polling/mod.ts)) internally calls `client.talk.sync` and `client.square.fetchMyEvents`, emitting high-level events that the client re-dispatches to user code.

## Extensibility Points

### Pluggable Storage and Fetch

Developers can inject a custom storage implementation (defaulting to `MemoryStorage`) to persist auth tokens and request sequences. A custom `fetch` implementation can also be provided for request signing, proxy support, or testing environments.

### Adding New Services

To extend the API surface, implement the `BaseService` interface, inject the new service in `BaseClient`’s constructor alongside the existing services, and expose it via the public client wrapper.

## Code Examples

### Basic Client Setup and Messaging

```ts
import { BaseClient } from "https://deno.land/x/linejs@latest/packages/linejs/base/core/mod.ts";

const client = new BaseClient({
  device: "iOS",
  version: "15.0",
});

await client.loginProcess.login({
  // login credentials (access token, etc.)
});

await client.talk.sendMessage({
  to: "u1234567890abcdef1234567890abcdef",
  text: "Hello from LINEJS!",
});

```

### Manual Token Refresh

```ts
try {
  await client.auth.tryRefreshToken();
  console.log("Token refreshed, new token:", client.authToken);
} catch (e) {
  console.error("Refresh failed:", e);
}

```

### Listening for Events

```ts
const abort = new AbortController();

client.listen({
  talk: true,
  square: true,
  signal: abort.signal,
});

client.on("message", (msg) => {
  console.log("New talk message:", msg.raw.text);
});

client.on("square:message", (msg) => {
  console.log("New square message:", msg.raw.text);
});

// Stop listening later:
// abort.abort();

```

## Summary

- **`BaseClient`** in [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts) acts as the nucleus, extending `TypedEventEmitter` for event propagation.
- **Services** implement the `BaseService` interface from [`packages/linejs/base/service/types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/types.ts), encapsulating protocol types and request paths.
- **AuthService** and **TalkService** demonstrate the pattern: store client reference, define protocol metadata, and wrap Thrift calls.
- **Request flow** moves from service methods → `RequestClient` → pluggable `fetch` with automatic header injection.
- **Extensibility** is supported via custom storage, custom fetch, and the ability to add new services following the established interface.

## Frequently Asked Questions

### How does BaseClient handle authentication token refreshes?

According to the evex-dev/linejs source code, `BaseClient` delegates token management to `AuthService` in [`packages/linejs/base/service/auth/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/auth/mod.ts). The `tryRefreshToken` method (lines 20-33) retrieves the stored refresh token, calls the `refresh` endpoint, updates the client's `authToken` property, and emits an `update:authtoken` event that other services can observe.

### What is the relationship between BaseClient and the user-facing Client class?

`BaseClient` provides the core infrastructure and service objects, while `Client` in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts) wraps it with high-level conveniences. `Client` exposes methods like `listen()` that internally use `BaseClient.createPolling()` and re-dispatch events from `BaseClient`'s event emitter to user code with richer context.

### Can I use a custom HTTP client or storage backend with LINEJS?

Yes. The `BaseClient` constructor accepts a custom `fetch` implementation via the `#customFetch` private field, which the public `fetch` wrapper (lines 220-232) prioritizes over `globalThis.fetch`. Similarly, you can inject any storage implementation matching the interface used by `getReqseq` (lines 204-218) to replace the default `MemoryStorage`.

### How are real-time events fetched in LINEJS?

Real-time events use a polling architecture. When you call `client.listen()`, the client creates a `Polling` instance (in [`packages/linejs/base/polling/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/polling/mod.ts)) that periodically calls `client.talk.sync` for personal chats and `client.square.fetchMyEvents` for OpenChat rooms. These methods return batches of events that the SDK emits as high-level JavaScript events.