開発日誌

開発した時に躓いたこととかの記録

Next.jsのAPIでnodemailerを使ってメールを送る

  • メール通知を作ってくれ。Next.jsのAPI Routesで。と言われて『できるんだ……』ってなったのでやってみるなど。
  • Routing: API Routes | Next.js

ざっくり仕様

  • メールはGmailを使う
  • ライブラリはnodemailerというやつを使う
  • FWはもちろんNext.js

結果

import type { NextApiRequest, NextApiResponse } from 'next'
import { createTransport } from 'nodemailer'

const transporter = createTransport({
  service: process.env.MAIL_SERVICE,
  secure: true,
  auth: {
    user: process.env.MAIL_AUTH_USER,
    pass: process.env.MAIL_AUTH_PASS,
  },
})

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const method = req.method
  switch (method) {
    case 'POST': {
      const { title, body } = req.body
      try {
        await transporter.sendMail({
          from: process.env.MAIL_FORM_USER,
          to: process.env.MAIL_TO_DEFAULT,
          subject: title,
          text: body,
        })
        res.status(200).end()
      } catch (error) {
        res.status(500).end()
      }
      res.status(500).end()
      break
    }
    default: {
      res.status(404).end()
    }
  }
}
  • src/pages/index.tsx
import Head from 'next/head'
import styles from '@/styles/Home.module.scss'
import { useState } from 'react'

export default function Home() {
  const [text, setText] = useState('')
  const [result, setResult] = useState<string>()

  const onClick = () => {
    setResult('メールを送ってるよ……。')
    fetch('/api/mail', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        title: 'テストメール',
        body: text,
      }),
    })
      .then((res) => {
        setResult('メールを送ったよ')
      })
      .catch((err) => {
        setResult('メールを送るのに失敗したよ……。')
      })
  }

  const onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setText(e.target.value)
  }

  return (
    <>
      <Head>
        <title>Mail Test App</title>
        <meta name='description' content='Generated by create next app' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <main>
        <div className={`${styles.send}`}>
          {result === undefined ? (
            <>
              <textarea onChange={(e) => onChange(e)} />
              <button onClick={() => onClick()}>めーるおくるよー</button>
            </>
          ) : (
            <p>{result}</p>
          )}
        </div>
      </main>
    </>
  )
}

ひっかかったこと

Googleのログインにひっかかる

メモ

  • ts環境だとnodemailerがエラーになるのは @types/nodemailerをinstallしておけばOK
  • 同一オリジンポリシーがあるので、同じリポジトリのサイトから呼ぶだけならプラスで何かやる必要はなさそう

参考にしたサイト

おわり

  • 意外と簡単にできる!そして意外とメール送る処理って実装したことない。
  • メール送りまくったのでGoogleに不審に思われてないかちょっと不安
  • デプロイして動作確認もやったが、BASIC認証つけたとはいえインターネットに残しておくのは不安なので削除した。みんなもきをつけよう。
  • おわり。