# [Node.js] 用 Nodemailer 寄信 - 筆記
###### tags: `Node.js` `Express.js` `npm`
:::warning
:bulb: 本站筆記已同步更新到我的[個人網站](https://simplydevs.netlify.app/)囉! 歡迎參觀與閱讀,體驗不同的視覺感受!
:::
[TOC]
## :memo: Nodemailer安裝與設定
首先參閱[Nodemailer的文件](https://nodemailer.com/about/)。
### 安裝
`npm install --save nodemailer`
安裝完成後到package.json確認,沒問題就來進行接下來的設定,看一下文件的說明:

其實就是3個重點:
1. 先做一個transporter,可以使用[SMTP](https://www.geeksforgeeks.org/simple-mail-transfer-protocol-smtp/)或[其他傳輸機制](https://nodemailer.com/transports/)
2. 設定寄信的內容,包含寄件人、收件人、信件內容、主旨、附件等
3. 使用Nodemailer的`sendMail()`方法,透過先前產生的transporter將信件寄出
### 引入nodemailer
`const nodemailer = require('nodemailer')`
### 製作transporter
官方文件範例:
`let transporter = nodemailer.createTransport(options[, defaults])`
因為預設使用SMTP,所以如果使用SMTP機制的話,以下兩種寫法皆可:
`let transporter = nodemailer.createTransport("SMTP", {smtpOptions});`
`let transporter = nodemailer.createTransport({smtpOptions});`
完整範例如下,**帳號密碼可以用環境變數的方式**引入:
```nodejs=2
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email", // 寄信的host,
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: testAccount.user, // 代為寄信的帳號
pass: testAccount.pass, // 該帳戶的密碼
},
});
```
:::success
#### 如果使用Gmail做為transporter
nodemailer已經知道知名的信件服務提供者的SMTP連線資訊,所以若使用[這些信件服務](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/)做為transporter,不需提供host、port等資訊,以下以gmail為例:
```nodejs=2
let transporter = nodemailer.createTransport({
service: 'gmail', // no need to set host or port etc.
auth: {...}
})
```
但是由於gmail對安全性的限制,這裡無法直接使用gmail帳號密碼做為驗證,必須設定**App password**:
1. 首先進入Google account頁面,點選左側**Security**

2. 找到 How you sign in to Google的區塊,確認 2-Step Verification已經開啟

3. 進入 2-Step Verification頁面,拉到最下面,找到App passwords

4. 點進App passwords,選擇app:Mail、device:(你的裝置),按Generate。
5. 這時會產生一組password,這組密碼可以做為transporter中auth的密碼,**記得複製下來貼到.env裡面,視窗關了就看不到了**,必須重新產生一組。
#### 如果不想使用密碼
如果不想使用App password,又想利用gmail做為transporter,可以使用**Gmail API + OAuth2**的方式驗證,詳細作法參考:
* [Node.js - SEND Emails Using Nodemailer | Gmail | OAuth2](https://youtu.be/18qA61bpfUs)
* [How to Use Nodemailer to Send Emails from Your Node.js Server](https://www.freecodecamp.org/news/use-nodemailer-to-send-emails-from-your-node-js-server/)
:::
### 設定mail options
接著設定寄件內容,直接看範例:
```nodejs=16
let mailOptions = {
from: "sender@server.com", //寄件人email
to: "receiver@sender.com", //收件人email
subject: "Message title", //主旨
text: "Plaintext version of the message", //信件內容,純文字
html: "<p>HTML version of the message</p>" //信件內容,html
};
```
除了以上基本內容,還有更多進階內容可以設定,例如:
```nodejs=16
let mailOptions = {
...,
cc: 'user1@example.com', //cc的對象
bcc: 'user2@example.com', //bcc的對象
attachments: [{
filename: 'image1.jpg',
path: 'filepath/image1.jpg'
}], //附件
headers: {
'My-Custom-Header': 'header value'
}, // headers內容
date: new Date('2000-01-01 00:00:00') //日期
};
```
更多進階設定,參考文件[Message Configuration](https://nodemailer.com/message/)。
### 寄出信件
transporter和mail options都設定完之後,就可以用nodemailer提供的`sendMail()`方法寄出信件:
`transporter.sendMail(mailOptions)`
也可以加上callback回傳error和result,更多選項詳見[文件](https://nodemailer.com/usage/):
```
transport.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log("Message sent: %s", info.messageId);
// Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
})
```
## :memo: 實際操作 - 聯絡表單 Contact form
### 想法
製作一個聯絡表單,類似客服信箱的概念,使用者留言後按送出,表單內容會寄到客服信箱。
### 表單HTML
```[HTML]
<form class="form" method="POST" action="/contact" onsubmit="return validation();">
<div class="row">
<div class="form-group col-md-6 my-3">
<input type="text" name="name" class="form-control" placeholder="Name" required>
</div>
<div class="form-group col-md-6 my-3">
<input type="email" name="email" class="form-control" placeholder="Email" required>
</div>
<div class="form-group col-md-12 my-3">
<input type="text" name="subject" class="form-control" placeholder="Subject" required>
</div>
<div class="form-group col-md-12 my-3">
<textarea rows="6" name="message" class="form-control" placeholder="Your Message" required></textarea>
</div>
<div class="col-md-12 text-center my-3">
<button type="submit" value="Send message" name="submit" class="btn btn-contact fs-5" title="Submit Your Message!">Send Your Message!</button>
</div>
</div>
</form>
```
### 把寄信功能封裝成一個helper
```nodejs=1
# ./helpers/email-helpers.js
const nodemailer = require('nodemailer')
const contactFormSend = async (name, email, subject, message) => {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL,
pass: process.env.APP_PASSWORD
}
})
const mailOptions = {
from: process.env.EMAIL,
to: process.env.EMAIL,
subject,
html: `<h3>Name: ${name}</h3><br/><h3>Email: ${email}</h3><br/><h3>Message:</h3><br/><p>${message}</p>`
}
const sendMail = await transporter.sendMail(mailOptions)
return sendMail
}
module.exports = contactFormSend
```
```nodejs=1
# .env
EMAIL=XXXX@gmail.com
APP_PASSWORD=your_password
```
### Route - POST /contact
* 首先利用Express架一個伺服器,專案入口設定為`app.js`(詳細步驟[點我](https://hackmd.io/@yy933/BJR_v_K_j)),接著到`app.js`引入email-helpers
`const contactFormSend = require('./helpers/email-helpers')`
* 傳送表單資料的路由
```nodejs=1
# app.js
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config()
}
const express = require('express')
const app = express()
const port = 3000
const contactFormSend = require('./helpers/email-helpers')
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
// Send email from contact form
app.post('/contact', (req, res) => {
try {
//從req.body取得表單資料
const { name, email, subject, message } = req.body
// 傳入表單資料,利用email-helper寄信
contactFormSend(name, email, subject, message)
return res.redirect('/contact')
} catch (error) {
console.log(error)
res.redirect('/contact')
}
})
app.listen(port, () => {
console.log(`App is listening at http://localhost:${port}`)
})
```
### 寄信功能試用
都設定完畢後,來試用看看:
1. 填寫表單並送出

2. 到信箱收信

成功寄信啦!! :tada::tada::tada:
---
## 參考資料
* [Nodemailer文件](https://nodemailer.com/about/)
* [Node.js + Nodemailer : How to send Emails via SMTP with Nodemailer](https://devdotcode.com/node-js-nodemailer-how-to-send-emails-via-smtp-with-nodemailer/)
* [Node.js 系列學習日誌 #21 - 使用 nodemailer 套件透過 gmail 發送電子信箱](https://ithelp.ithome.com.tw/articles/10160766)
::: success
:crescent_moon: 本站內容僅為個人學習記錄,如有錯誤歡迎留言告知、交流討論!
:::