在next/react裡發送email,最常看到的有兩個package,下載次數最多的是nodemalier,其次是emailjs。很多同學會問:「為何要使用email服務?」,當我們的系統透過gmail發送信件時,因為gmail常被拿來當作攻擊的跳板,當大量發信時,通常會被判斷為垃圾信。而這些email服務就是讓我們可以發大量信件而不會被判斷為垃圾信。另外,也提供一些管理功能,例如,可以讓我們轉換郵件服務提供者的時候,不必改寫程式。

API

以下就以nodemailer以及gmail為例,進行說明。首先,先設定好gmail帳號,透過產生app密碼,產生另一組專門用來透過應用程式發信的密碼 (跟平常登入不一樣的密碼)。

Untitled

設定好應用程式密碼後,透過npm或yarn安裝package,因為範例是使用typescript,還需要@types/nodemailer,如果使用javascript,就不需要:

npm install nodemailer
npm install @types/nodemailer

通常,為了保護我們的設定,會將設定放在.env.local (如果佈署到vercel,請記得也要在vercel上設定變數)

SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_USER=XXXX
SMTP_PASSWORD=XXXX
SMTP_FROM_EMAIL=XXXX

接下來,我們利用next的api寫一個很簡單的發信服務

/pages/api/email_test.ts

import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
import nodemailer from "nodemailer";

const handler: NextApiHandler = async (
  req: NextApiRequest,
  res: NextApiResponse
) => {
  const smtpOptions = {
    host: process.env.SMTP_HOST || "smtp.gmail.com",
    port: parseInt(process.env.SMTP_PORT || "465"),
    //secure: true,
    auth: {
      user: process.env.SMTP_USER || "user",
      pass: process.env.SMTP_PASSWORD || "password",
    },
  };
  try {
    const transporter = nodemailer.createTransport(smtpOptions);
    console.log("user:", process.env.SMTP_USER);
    await transporter.sendMail({
      from: process.env.SMTP_USER || "[email protected]",
      to: "[email protected]",
      subject: "測試",
      html: "測試 gmail",
    });
    return res.status(200).json({ message: "Email sent successfully" });
  } catch (error) {
    console.error(error);
  }
};

export default handler;

當我們執行這個api時,就能收到email了

Untitled

Untitled

透過API發出電子郵件

要從一般的頁面透過API發送電子郵件,首先,先新增一個測試頁面,讓使用者輸入收件人電子郵件:

import { Button, TextField } from "@mui/material";
import Head from "next/head";
import { useState } from "react";

export default function TestEmail() {
  const [email, setEmail] = useState<string>();
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(e.target.value);
  }

  return (
    <div>
      <Head>
        <title>email測試</title>
        <meta name="description" content="email test" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <TextField
        type="text"
        name="email"
        value={email}
        placeholder="請輸入信箱..."
        onChange={handleChange}
        autoComplete="email"
      />

      <Button >
        送出
      </Button>
    </div>)
}