Next.jsからSlackに通知を送る

@Panda_Program

Next.jsからSlackのチャンネルに通知を送る方法

**Next.jsとはVercelが作成しているReactのフレームワークです。**面倒な設定を書かなくてもすぐに使えるZero Configを標榜しており、実際にwebpackやTypeScriptと一緒にReactを書く際にも特別な準備は不要です。SSRにも対応しており、Reactで開発するならNext.jsかFacebook製のCreate React Appを使うのがスタンダードになっています。

関連記事: Google Apps ScriptからSlackとLINEを連携するbotを作る手順を紹介します

また、**Slackとは、主にIT企業で導入されている非同期コミュニケーションが可能なチャットツールです。**様々なツールと連携できるところに特徴があり、サーバーの監視ログやあらゆる通知をSlackのチャンネルに集約できます。

この記事ではNext.jsからSlackに通知を送る方法を紹介します。

SlackのWebhook URLを漏れないようにする

**Slackに通知を送るにはWebhook URLを取得します。**始めはWebhook URLをブラウザからPOSTすれば簡単に実現できると考えていました。

しかし、ブラウザでWebhook URLにPOSTする際にユーザーがWebhook URLを知ることができることに思い至り、フロントからSlackに通知する方法は避けました。

それでも実現方法を考えていると、Next.jsのAPIの機能を使ってサーバー側からコードを実行すればWebhook URLが漏れてしまう問題を解決できることに思い至りました。そこで、実際に本記事の方法で実装しました。

Webhook URLを.envに設定する

まずはSlackチャンネルのWebhook URLを環境変数に設定します。

.env
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXXXXXXXXXXX

SLACK_WEBHOOK_URLの値はprocess.env.SLACK_WEBHOOK_URLで取得します。なお.envに記述した環境変数は、Next.jsの中でどこからでも呼び出し可能です。しかし、配信されるJSにで値が公開されることはありません。

また、チャンネル毎のWebhook URLの取得方法は「slackのincoming-webhookに登録する」をご覧ください。

node-slack-sdkでWebhookにPOSTする

**Slackに通知を送るには、SlackのWebhook URLにPOSTリクエストをするということを書きました。**この処理はSlack公式のnode-slack-sdkというnodeモジュールを利用すると簡単に書くことができます。

lib/slack.tsに以下のようなコードを記述します。

lib/slack.ts
import { IncomingWebhook } from '@slack/webhook'

const slack = {
  sendToSlack: async (text: string) => {
    const url = process.env.SLACK_WEBHOOK_URL
    const webhook = new IncomingWebhook(url)
    await webhook.send({ text })
  },
}

export default slack

api/comments.tsにPOSTリクエストを送る

lib/slack.tsで作成したモジュールはNode.jsで動作します。Next.jsでは、apiを通じてサーバーサイドのコードを実行できます。

Next.jsでは、pages/api配下に作成したファイルがAPIのエンドポイントになります。/api/commentsというエンドポイントを作りたいときは、pages/api/comments.tsというファイルを置くことでパスとファイルを自動的にマッピングされます。

pages/api/comments.tsを作成する

まず、pages/api/comments.tsを作成します。ここで上記で作成したslackモジュールを実行します。

pages/api/comments.ts
import { NextApiRequest, NextApiResponse } from 'next'
import slack from 'lib/slack'

module.exports = async (req: NextApiRequest, res: NextApiResponse) => {
  const { message } = req.body

  if (typeof message === 'undefined') {
    res.writeHead(400).end('Invalid body: message')
  }

  if (req.method === 'POST') {
    // Slackのチャンネルにテキストを通知する
    await slack.sendToSlack(message)
    res.writeHead(201).end('Created')
  } else {
    // POST以外のメソッドは受け付けない
    res.writeHead(405).end('Method Not Allowed')
  }
}

これで/api/commentsにPOSTリクエストをするとSlackにメッセージを投稿できるようになりました。

エンドポイント/api/commentsにPOSTする

また、POSTリクエストにはfetchを使います。

const message = 'メッセージを投稿します'

fetch('/api/comments', {
  method: 'POST',
  mode: 'same-origin',
  credentials: 'same-origin',
  headers: { 'Content-Type': 'application/json; charset=utf-8' },
  body : JSON.stringify(message),
})

変数messageはSlackに送りたい文字列を格納してください。

[実践]ユーザーが投稿したコメントをSlackに通知する

では、実際にNext.jsからSlackに通知を送ります。ユースケースとして、「ユーザーが投稿したコメントをSlackに通知する」ことを想定します。

pages/index.tsx
import React, { useState, SyntheticEvent } from 'react'
import { NextPage } from 'next'

type Props = {
  comment: string
  handleChange: (e: SyntheticEvent) => void
  handleSubmit: (e: SyntheticEvent) => void
}

const Component: React.FC<Props> = (props) => (
  <form onSubmit={props.handleSubmit}>
    <input
      type="text"
      placeholder="コメントを入力..."
      value={props.comment}
      onChange={props.handleChange}
    />
    <button type="submit">投稿する</button>
  </form>
)

const Container: NextPage<{}> = () => {
  const [comment, setComment] = useState('')
  const handleChange = (e) => setComment(e.target.value)
  const handleSubmit = async (e) => {
    e.preventDefault()

    if (comment.trim() === '') {
      return
    }

    fetch('/api/comments', {
      method: 'POST',
      mode: 'same-origin',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json; charset=utf-8' },
      body : JSON.stringify({message: comment}),
    })
  }

  return <Component {...{comment, handleChange, handleSubmit}} />
}

Container.displayName = 'IndexPage'

export default Container

エンドポイント/api/commentsにPOSTでユーザーが投稿するコメントを送信するReactコンポーネントが作成できました。

inputとbuttonの表示

「投稿する」ボタンを押すと、入力したメッセージがSlackに通知されます。

まとめ

Next.jsからSlackに通知を送る方法を紹介しました。なお、以下に記述したコードはサーバーサイドだけで実行され、bundleするJSには含まれません。

  • pages/api配下に作成したファイル
  • getServerSidePropsgetStaticPropsといったReactコンポーネントにデータを注入するための関数

Next.jsはフロントエンドとサーバーサイドの境界をうまく区切っているフレームワークですね。さらにNext.jsを使いこなしていきましょう。

Happy Coding 🎉

パンダのイラスト
パンダ

記事が面白いと思ったらツイートやはてブをお願いします!皆さんの感想が執筆のモチベーションになります。最後まで読んでくれてありがとう。

  • Share on Hatena
  • Share on Twitter
  • Share on Line
  • Copy to clipboard