LoginSignup
1
1

LINE BotサーバーをNestJSで開発する その2

Last updated at Posted at 2023-11-19

前回の続きです。立ち上げたエコーサーバーのコードの要点をざっと見ていきます。

ソースコードはこちらです。
https://github.com/bathtimefish/line-botserver-example-nestjs/tree/v1

main.ts

src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as fs from 'fs';
import { config } from 'dotenv';

function getOptions() {
  return {
    httpsOptions: {
      key: fs.readFileSync(process.env.SSL_KEY_PATH),
      cert: fs.readFileSync(process.env.SSL_CERT_PATH),
    },
  };
}

async function bootstrap() {
  let options = {};
  if (process.env.SSL_KEY_PATH && process.env.SSL_CERT_PATH) {
    options = getOptions();
  }
  const app = await NestFactory.create(AppModule, options);
  await app.listen(3000);
}
config();
bootstrap();

ここではデフォルトで生成されたコードにhttpsサーバー化の設定を追加しています。LINE PlatformからのWebhookを受けるためにはhttpsサーバーである必要があるからです。

NestFactory.create(AppModule, options);でサーバーのインスタンスを作成し、app.listen(3000);でポート3000でBotサーバーを起動しています。AppModuleはBotサーバーを構成するモジュールで後述します。第二引数のoptionsにはgetOptions()で取得したLet's Encryptの証明書ファイルのパス設定が含まれています。NestJSではhttpsOptionsに証明書のパスを設定することでWebサーバーをhttpsサーバーとして作成することができます。

app.module.ts

src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MessagingModule } from './messaging/messaging.module';

@Module({
  imports: [MessagingModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

@Module()デコレーターで注釈されるNestJSのModuleこのModuleによってサーバーアプリケーションの構造が簡潔に定義できるようになっています。ここではimportsにMessagingAPIを扱うModuleをインポートしています。importsは他のModuleをimportして本Moduleに統合することができるので複数のModuleを構造的に開発することができます。

controllersにはこのModuleで動作させるControllerのリストを設定します。Controllerは@Controller()で注釈されるリクエスト/レスポンスを制御する機能を定義します。providersにはController等で共通的に使われる機能を@Injectable()によって注釈されるクラスにより提供します。

上記コードのcontrollersprovidersはNestJSの初期プロジェクト設定時のままで変更しておらず、imports: [MessagingModule],を追加したのみです。つまり今回カスタマイズしたLIME Messaging APIのエコーサーバーの全機能はMessagingModuleに入っていることになります。では、MessagingModuleを見ていきましょう。

messaging.module.ts

src/messaging/messaging.module.ts
import { Module } from '@nestjs/common';
import { MessagingController } from './messaging.controller';
import { MessagingService } from './messaging.service';

@Module({
  controllers: [MessagingController],
  providers: [MessagingService],
  exports: [MessagingService],
})
export class MessagingModule {}

今回カスタマイズした部分はほぼsrc/messagingに収まっています。それ以外は上記のmain.tsをhttpsの設定を加えただけです。

messaging.module.tsはNestJSのModuleなのでapp.module.tsの構造と全く同じです。異なる点は、このコードではimportsを使用しておらずexportsで読み込んだプロバイダを公開しています。

messaging.controller.ts

src/messaging/messaging.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { LineMessagingApiDto } from './messaging.dto';
import { MessagingService } from './messaging.service';

@Controller('messaging')
export class MessagingController {
  constructor(private readonly messagingService: MessagingService) {}
  @Get()
  getMessages() {
    return this.messagingService.getMessage();
  }
  @Post()
  postMessages(@Body() body: LineMessagingApiDto) {
    return this.messagingService.dispachMessage(body);
  }
}

MessagingAPIのロジックを定義するControllerです。@Controller('messaging')によって、APIのパスが/messagingとなります。リクエストメソッドごとに定義されるクラスメソッドは@Get()@Post()といったアノテーションによって非常に簡潔に定義できます。各メソッド内の機能は後述するmessaging.service.ts内のProviderで提供されています。ProviderはControllerのクラスに注入されているためthis.messagingService.getMessage();のように自クラスの機能のように利用できます。

messaging.service.ts

src/messaging/messaging.service.ts
import { Injectable } from '@nestjs/common';
import type { ILineMessagingApi, ISimpleMessage } from './messaging.interface';
import { messagingApi } from '@line/bot-sdk';
import type { ClientConfig } from '@line/bot-sdk';

const getLineBotClient = () => {
  const clientConfig: ClientConfig = {
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
    channelSecret: process.env.CHANNEL_SECRET,
  };
  return new messagingApi.MessagingApiClient(clientConfig);
};

@Injectable()
export class MessagingService {
  getMessage(): ISimpleMessage {
    return { message: 'This is a LINE Bot Server powered by NestJS' };
  }
  async dispachMessage(body: ILineMessagingApi): Promise<void> {
    const client = getLineBotClient();
    const event = body.events[0];
    let textMessage: { type: string; text: string };
    switch (event.message.type) {
      case 'text':
        textMessage = {
          type: 'text',
          text: event.message.text,
        };
        break;
      default:
        textMessage = {
          type: 'text',
          text: 'Server receinved an invalid message type',
        };
    }
    await client.replyMessage({
      replyToken: event.replyToken,
      messages: [textMessage],
    });
  }
}

messaging.service.tsにはMessaging APIを利用するエコーサーバーのロジックを含むメソッドが記述されています。ここに定義されているgetMessage()dispatchMessage()をController側に記述することもできますが、上記のようにProviderとして提供することで、messaging.controller.ts以外のControllerでこれらの機能を利用したくなった場合にすぐ提供することができます。

dispachMessage()がLINE PlatformからWebhookを受信したときに動作するメソッドです。ここでは簡単のためTextEventMessageを受信したときのみ、テキストの内容をそのまま返信するようにしています。

src/messaging/messaging.service.ts
await client.replyMessage({
  replyToken: event.replyToken,
  messages: [textMessage],
});

で、Message Eventに含まれるreplyTokenとテキストメッセージを設定してメッセージを送信したクライアントに対してメッセージを返信しています。

おわりに

NestJSで作成したシンプルなLINE Botサーバーのコードをざっと見ていきました。NestJSは大規模なWebサーバーを効率よく開発/メンテナンスするために大変優れた構造を持っています。LINE Botを開発保守している上で、NestJSは良い選択肢の一つになるんじゃないでしょうか。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1