# Client vs Base Client in LINEJS: Understanding the Two-Layer Architecture

> Understand the difference between LINEJS client and base client modules. Discover how the high-level Client wraps the low-level BaseClient for easier API interaction and typed events.

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

---

**The `BaseClient` is the low-level engine that handles raw Thrift RPCs, authentication, and network protocols, while the `Client` is a high-level façade that wraps the base layer with convenient methods, typed events, and domain objects like `Chat` and `User`.**

LINEJS provides two distinct layers for interacting with the LINE messaging platform. Understanding the difference between client and base client modules in LINEJS is essential for choosing the right abstraction level for your application. The base layer (`@evex/linejs/base`) implements the core protocol engine, while the client layer (`@evex/linejs/client`) delivers developer-friendly APIs and event handling.

## What Is the Base Client?

The **BaseClient** (`@evex/linejs/base`) serves as the core engine of the library. Located in [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts), it manages low-level network handling, authentication flows, generated Thrift services, storage, push connections, polling, and E2EE encryption.

- It exposes a thin API that mirrors LINE’s internal RPCs directly through service objects like `base.talk`, `base.square`, and `base.auth`.
- It handles request sequencing, retries, and raw protocol details.
- It provides underlying push streams via `base.push.opStream` and `base.push.sqStream` but does **not** emit high-level events.

Use the BaseClient when you need direct access to specific services, want to build custom wrappers, or require fine-grained control over the login flow and network behavior.

## What Is the High-Level Client?

The **Client** (`@evex/linejs/client`) provides a convenient façade built on top of a `BaseClient`. Defined in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts), it wires the base client to high-level domain objects and emits strongly-typed events.

- It aggregates common helper methods like `fetchJoinedChats()`, `fetchJoinedSquares()`, and `getUser()`.
- It extends `TypedEventEmitter` to provide developer-friendly events such as `message`, `event`, and `square:message` with parsed objects like `TalkMessage` and `SquareMessage`.
- It stores the underlying `BaseClient` instance in a read-only `base` property, allowing access to low-level operations when needed.

This is the typical entry point for most applications, obtained through login helpers like `loginWithPassword`, `loginWithQR`, or `loginWithAuthToken`.

## Key Architectural Differences

### Construction and Initialization

The `BaseClient` is instantiated directly with low-level options. According to the source code in [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts) (lines 101-106), its constructor accepts device configuration, endpoints, optional custom `fetch` implementations, and storage handlers.

```typescript
import { BaseClient } from "https://deno.land/x/linejs/base/mod.ts";

const base = new BaseClient({
  device: "Android",
  version: "13.0",
});

```

In contrast, the `Client` constructor (lines 35-38 in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts)) receives an already-initialized `BaseClient` created by login helpers. The login utilities in [`packages/linejs/client/login.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/login.ts) perform authentication steps and return a ready-to-use `Client` instance.

### Responsibility Split

- **BaseClient**: Knows **how** to talk to LINE. It builds Thrift requests, manages connection persistence, handles retries, and provides raw service access.
- **Client**: Knows **what** developers typically want. It wraps raw services into semantic methods and translates protocol buffers into usable JavaScript objects.

### Event Model

The `BaseClient` only exposes raw push streams. The `Client` class implements the event-driven interface through its `listen()` method (lines 47-98 in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts)), which processes raw LINE operations and emits processed domain events.

## Code Examples

### Using the High-Level Client (Recommended)

The `loginWithPassword` helper returns a `Client` that wraps a `BaseClient` and provides high-level methods and events.

```typescript
import { loginWithPassword } from "https://deno.land/x/linejs/client/login.ts";

const client = await loginWithPassword(
  {
    email: "you@example.com",
    password: "superSecret",
    onPincodeRequest: (pin) => console.log("Enter PIN:", pin),
  },
  {
    device: "iOS",
    version: "15.0",
  },
);

// Listen to incoming chat messages
client.listen({ talk: true, square: true }).on("message", (msg) => {
  console.log(`New talk message from ${msg.senderMid}: ${msg.text}`);
});

// Fetch all joined chats
const chats = await client.fetchJoinedChats();
console.log(`You have ${chats.length} chats.`);

```

### Using the Low-Level BaseClient Directly

Interact with raw services when you need direct RPC access without event handling or object wrappers.

```typescript
import { BaseClient } from "https://deno.land/x/linejs/base/mod.ts";

const base = new BaseClient({
  device: "Android",
  version: "13.0",
});

// Manual authentication
base.authToken = Deno.env.get("LINE_AUTH_TOKEN")!;

// Direct RPC call
const chat = await base.talk.getChat({
  chatMid: "c1234567890abcdef1234567890abcd",
  withInvitees: true,
  withMembers: true,
});

console.log("Chat title:", chat.title);

```

### Mixing Both Layers

Access the underlying `BaseClient` through the `base` property when you need low-level operations alongside high-level convenience.

```typescript
import { loginWithQR } from "https://deno.land/x/linejs/client/login.ts";

const client = await loginWithQR(
  {
    onReceiveQRUrl: async (url) => console.log("Scan this QR:", url),
    onPincodeRequest: (pin) => console.log("Enter PIN:", pin),
  },
  { device: "iOS" },
);

// Use raw request via underlying BaseClient
const raw = await client.base.request.post("/v1/users/@me/profile", {
  method: "GET",
});
console.log("Raw profile JSON:", await raw.json());

```

## Summary

- **BaseClient** (`@evex/linejs/base`) is the protocol engine handling Thrift services, authentication, and raw network logic in [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts).
- **Client** (`@evex/linejs/client`) is the developer-friendly interface providing typed events, domain objects, and helper methods in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts).
- Login helpers in [`packages/linejs/client/login.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/login.ts) instantiate `BaseClient`, perform authentication, and return a configured `Client`.
- The `Client` stores the `BaseClient` in its `base` property, allowing hybrid usage patterns.
- Choose `BaseClient` for protocol-level control and `Client` for application development.

## Frequently Asked Questions

### Can I use BaseClient without the high-level Client?

Yes. You can instantiate `BaseClient` directly from [`packages/linejs/base/core/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/mod.ts) and manage authentication and service calls manually. This is useful when building custom frameworks or when you need to minimize bundle size by excluding the high-level event system.

### How do I access low-level methods from the high-level Client?

The `Client` class exposes the underlying `BaseClient` through its read-only `base` property. As shown in [`packages/linejs/client/client.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/client.ts) (lines 35-38), this allows you to call raw RPCs like `client.base.talk.getChat()` while still using high-level features for other operations.

### Does the BaseClient emit events?

No. The `BaseClient` only provides raw push streams (`base.push.opStream`, `base.push.sqStream`). High-level typed events are implemented exclusively in the `Client` class through its `listen()` method, which processes raw operations and emits parsed objects like `TalkMessage` and `SquareMessage`.

### Which module should I import for a typical chat bot?

Import from `@evex/linejs/client` using the login helpers (`loginWithPassword`, `loginWithQR`) defined in [`packages/linejs/client/login.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/client/login.ts). This provides the `Client` instance with event emitters and helper methods, offering the most productive API for building chat bots without managing raw Thrift protocols.