疑問

honoでLINE message APIを受け取るWebhook APIを作るときに何をすればよいか?

回答

HonoでLINEのWebhookを作成するには、主に以下の3つのステップが必要です。

  1. リクエストの署名検証
  2. リクエストボディのパースとイベント処理
  3. LINEサーバーへの応答

手順

  1. Honoプロジェクトの準備 npm create hono@latest でプロジェクトを作成します。Node.js環境を想定します。

  2. 環境変数の設定 LINE Developersコンソールから取得した2つの値を設定します。

    • LINE_CHANNEL_SECRET: 署名検証に使うチャネルシークレット
    • LINE_CHANNEL_ACCESS_TOKEN: メッセージ返信に使うチャネルアクセストークン(長期)
  3. Webhookエンドポイントの実装

以下は、Node.jsランタイムで動作するHonoのサンプルコードです。

import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'
import type { MessageEvent, TextMessage } from '@line/bot-sdk'
import crypto from 'crypto'

const app = new Hono()

// 環境変数から設定を取得
const CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET!
const CHANNEL_ACCESS_TOKEN = process.env.LINE_CHANNEL_ACCESS_TOKEN!

// LINEからのリクエストの署名を検証する関数
const verifySignature = (signature: string, body: string): boolean => {
  const hash = crypto
    .createHmac('sha256', CHANNEL_SECRET)
    .update(body)
    .digest('base64');
  return signature === hash;
};

// Webhook用のPOSTエンドポイント
app.post('/webhook', async (c) => {
  // 1. 署名検証
  const signature = c.req.header('x-line-signature');
  const body = await c.req.text(); // JSONパース前に生テキストを取得

  if (!signature || !verifySignature(signature, body)) {
    throw new HTTPException(401, { message: 'Invalid signature' });
  }

  // 2. イベント処理
  const data = JSON.parse(body);
  const events: MessageEvent[] = data.events;

  for (const event of events) {
    if (event.type === 'message' && event.message.type === 'text') {
      const message: TextMessage = event.message;
      const replyToken = event.replyToken;

      // 3. メッセージを返信する (オウム返し)
      await fetch('https://api.line.me/v2/bot/message/reply', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${CHANNEL_ACCESS_TOKEN}`,
        },
        body: JSON.stringify({
          replyToken: replyToken,
          messages: [{
            type: 'text',
            text: message.text, // 受け取ったテキストをそのまま返す
          }],
        }),
      });
    }
  }

  // LINEサーバーに正常処理を通知
  return c.json({ status: 'ok' });
});

export default app

要点

  • 署名検証が最重要: x-line-signature ヘッダーとリクエストボディ、そしてチャネルシークレットを使って、リクエストがLINEから来た本物であることを確認します。
  • 生のボディテキストが必要: Honoの c.req.json() はボディを消費してしまうため、署名検証には c.req.text() で先に生のテキストを取得します。
  • 非同期で返信: Webhookのレスポンスとしてメッセージを返すのではなく、LINEのReply API (/v2/bot/message/reply) を別途 fetch などで呼び出します。
  • 200 OKを返す: イベント処理が終わったら、LINEサーバーに 200 OK を返す必要があります。Honoでは c.json({ status: 'ok' }) などで応答します。