How to Extend LINEJS with Custom Services: A Complete Developer Guide

Extend LINEJS by implementing the BaseService interface to create modular service classes that automatically inherit HTTP request handling, Thrift serialization, and end-to-end encryption capabilities from the shared BaseClient.

LINEJS is built around a modular service architecture where each LINE API feature (Talk, Square, Auth) lives in its own service class. To extend LINEJS with custom services, you create a class implementing the BaseService interface and instantiate it within a BaseClient subclass, giving your custom code immediate access to the library's core infrastructure without modifying the upstream source.

Understanding the Service Architecture

The LINEJS codebase separates concerns through a strict service layer. Every built-in service follows the same pattern you will use for custom extensions.

The BaseService Interface

All services must satisfy the contract defined in packages/linejs/base/service/types.ts. The interface requires four properties:

  • client: The BaseClient instance providing shared utilities
  • protocolType: Thrift protocol identifier (e.g., 4 for TalkService-compatible binary protocol)
  • requestPath: API endpoint path (e.g., /S4)
  • errorName: String identifier for error reporting

As implemented in evex-dev/linejs, this interface ensures every service can access the same low-level request infrastructure.

BaseClient Shared Utilities

When you create a custom service, the BaseClient instance passed to your constructor provides enterprise-grade features out of the box:

  • HTTP request handling via client.request.request (defined in packages/linejs/base/core/mod.ts lines 68-71)
  • Thrift definitions via client.thrift.def (line 70)
  • Device and endpoint configuration (lines 60-64)
  • Continuable pagination via client.continueRequest (lines 13-14)
  • End-to-end encryption via client.e2ee (lines 72-73)
  • Event emitter via TypedEventEmitter (lines 1-2)

The BaseClient constructor wires built-in services at lines 78-86 of packages/linejs/base/core/mod.ts, creating the pattern you will replicate for custom services.

Step-by-Step Implementation

Follow these four steps to add a production-ready custom service to your LINEJS application.

1. Create the Service Class

Create a new folder under packages/linejs/base/service/ (e.g., myservice/) containing a mod.ts file. Implement BaseService and define your API methods using the inherited request handler.

// packages/linejs/base/service/myservice/mod.ts
import type { BaseClient } from "../../core/mod.ts";
import type { BaseService } from "../types.ts";
import type * as LINETypes from "@evex/linejs-types";

export class MyService implements BaseService {
  client: BaseClient;
  protocolType: number = 4;  // Matches TalkService protocol
  requestPath = "/S4";       // Adjust based on your API endpoint
  errorName = "MyServiceError";

  constructor(client: BaseClient) {
    this.client = client;
  }

  /** Example API call using LINEJS request infrastructure */
  async getFoo(id: string): Promise<LINETypes.Foo> {
    return await this.client.request.request(
      [["fooId", 1, id]],           // Thrift payload
      "getFoo",                     // Method name
      this.protocolType,             // Protocol identifier
      true,                          // Requires auth
      this.requestPath,              // Endpoint path
    );
  }
}

2. Export from the Service Barrel

To make your service importable alongside built-in services, add it to the barrel export at packages/linejs/base/service/mod.ts. This step is optional if you intend to keep the service private to your application.

// packages/linejs/base/service/mod.ts
export { MyService } from "./myservice/mod.ts";   // Add this line

The existing barrel exports built-in services from lines 1-8 of this file.

3. Extend BaseClient

Instead of modifying the core library, subclass BaseClient to expose your service as a strongly-typed property. This approach preserves your changes across LINEJS updates.

// custom-client.ts
import { BaseClient } from "./base/core/mod.ts";
import { MyService } from "./base/service/mod.ts";

export class ExtendedClient extends BaseClient {
  readonly myService: MyService;

  constructor(init: ConstructorParameters<typeof BaseClient>[0]) {
    super(init);
    this.myService = new MyService(this);
  }
}

This pattern mirrors the internal implementation where BaseClient instantiates built-in services like TalkService and SquareService within its constructor.

4. Use Your Custom Service

Instantiate your extended client and call custom methods with full TypeScript support and automatic error handling.

// usage.ts
import { ExtendedClient } from "./custom-client.ts";

const client = new ExtendedClient({
  device: "Android",
  version: "12.0",
});

await client.loginProcess.loginWithEmail("you@example.com", "password");

// Access your custom service
const result = await client.myService.getFoo("abc123");
console.log("Foo:", result);

Why Subclass Instead of Modify?

Subclassing BaseClient offers three critical advantages for extending LINEJS with custom services:

  • Update isolation: Core library updates from evex-dev/linejs will not overwrite your custom service implementations
  • Type safety: You add strongly-typed properties without altering the generated typings of the original package
  • Future compatibility: New built-in services added to BaseClient automatically propagate to your subclass

If you must patch the core library directly—such as when maintaining a public fork—add your service instantiation within the BaseClient constructor following the pattern at lines 78-86 of packages/linejs/base/core/mod.ts.

Summary

  • Service contract: Implement BaseService from packages/linejs/base/service/types.ts to ensure compatibility with LINEJS request handling
  • Infrastructure access: Your service receives a BaseClient instance providing HTTP requests, Thrift serialization, E2EE, and pagination via this.client
  • Implementation path: Create the class → export from mod.ts → subclass BaseClient → instantiate with new MyService(this)
  • Best practice: Use client subclassing rather than editing BaseClient directly to maintain update compatibility
  • Code locations: Reference packages/linejs/base/core/mod.ts for the core client and packages/linejs/base/service/mod.ts for service exports

Frequently Asked Questions

Do I need to implement my own HTTP client or encryption layer?

No. When you implement BaseService and receive the BaseClient instance in your constructor, you inherit the existing infrastructure. According to the LINEJS source code, BaseClient provides this.client.request.request for HTTP handling, this.client.e2ee for end-to-end encryption, and this.client.thrift.def for serialization. Your service class focuses solely on defining the Thrift payload structure and method names.

Can I use custom services without modifying the LINEJS source code?

Yes. You can keep your service implementation entirely within your application codebase. Simply import BaseClient and BaseService from the LINEJS package, implement your service class in your own files, and subclass BaseClient to wire it together. You only need to edit packages/linejs/base/service/mod.ts if you want your service exported from the library's public API surface.

What protocol type should I use for custom LINE API endpoints?

Use protocolType = 4 for standard LINE API services, which matches the binary protocol used by TalkService and other core services in the LINE ecosystem. The requestPath should match your specific endpoint (e.g., /S4 for certain service types). If you are implementing a completely custom microservice unrelated to LINE's Thrift infrastructure, you may use the raw HTTP utilities from BaseClient instead of the Thrift request method.

How do I handle pagination in custom service methods?

Use the client.continueRequest utility defined in packages/linejs/base/core/mod.ts at lines 13-14. Wrap your async fetch method with continueRequest to automatically handle continuation tokens and paginated responses. This ensures your custom service behaves identically to built-in services like getNextMessages, providing a consistent interface for consuming large result sets.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →