六角專題班學習組電商 API 之完整教學

前言

這一篇將會介紹如何使用六角學院所提供的後端程式碼啟動 API 伺服器,並透過 Postman 來測試 API 的各種功能。

事前準備

準備開始往下之前,請你先準備好以下的環境:

以上環境都準備好後,我們就可以開始進入下一步囉!

Note
這一篇不會特別介紹該如何安裝這些軟體,如果你對這些軟體不熟悉,建議你可以先自行查詢相關資料哩。

Firebase 專案設定

由於六角學院所提供的專案會使用 Firebase Storage 來儲存圖片,所以你需要先設定好 Firebase 專案。

Note
Firebase 新的規定部分功能是需要綁定信用卡才能使用,但是不用擔心,Google 本身會提供一定程度的免費額度使用。
除此之外,Firebase 的畫面可能會隨著使用者帳號差異有一點點不一樣,但這一篇會盡可能描述從零開始唷~

建立 Firebase 專案

首先開啟 Firebase,並且點選右上角的「轉到控制台

轉到控制台

接著點「新增專案」or 「建立專案

新增專案

當你點了「建立專案」後,就會跳出一個滿版的視窗,這邊就是你要輸入專案名稱的地方,基本上你可以隨便取,但是我這邊就取「Firebase-Express」,接著按下「繼續

專案名稱

第二步驟是 Firebase 問你要不要開啟 Google Analytics(分析) 之類,但這邊我們都不需要,所以就「關閉」並按下「建立專案

Google Analytics(分析)

建立過程會稍微等一下,你去上個廁所就好了

建立中

準備好後,就按一下「繼續

新專案已準備就緒

這時候你應該會直接進入到你的專案控制台,到這邊為止你的 Firebase 專案就準備完成了

完成

設定 Firebase Storage

Note
這個步驟很重要,如果沒有先設定的話,你將無法取得 Storage Bucket 的 URL,這對我們後面的程式碼會有影響。

接下來你必須設置 Firebase Storage 的儲存區域,所以請你點一下左側功能列表找到「Storage」,並點擊「升級專案

升級專案

接著 Firebase 會跳出要求你升級帳號,因為 Firebase 為了避免資源被濫用,所以未來開始一率使用 Firebase Storage 服務的人,都要綁定信用卡,但這邊不用擔心,我們先繼續往下,點擊「建立 Cloud Billing 帳戶

繼續

這時候畫面會跳出 Google Cloud 視窗,點一下「確認

確認

接著需要填寫一些稅務資訊,填寫完畢後,最下方要綁定一個信用卡,而這邊我是綁定一張自己沒有在使用的信用卡,沒問題後,就按下「確認購買

確認購買

Note
如果你是新的使用者 Google 會提供 300 美金讓你可以免費使用。

這時候會跑一下「正在設定帳單」,就稍微等待一下….

正在設定帳單

理論上設定完就可以回到 Firebase 上。

Note
如果你的帳戶有問題,如:跨區、卡號有問題,Google 會要求你再次驗證一次唷。

接著你可能會跳出一個預算設定,這邊我們先按下「略過」,後面如果你有需要可以再自己去設定

略過

接著我們要把 Firebase 與我們在 Google Cloud 綁定的信用卡與我們的 Firebase 專案連結,這邊我們可以直接點一下「連結 Cloud Billing 帳戶

連結 Cloud Billing 帳戶

當你看到以下畫面時,就代表你設定完成囉!

設定完成

如果你想知道目前用量,你可以點上方齒輪 => 「用量與帳單」查看哩

用量與帳單

接下來我們回到 Firebase Storage 的部分,你應該已經會看到以下畫面,這時候你可以點一下「開始使用

開始使用

接著會需要選取 Storage 的地區,通常預設是 US-CENTRAL1,而這邊我們沒有必要去調整,所以你可以直接按下「繼續

Storage 地區

接著會需要設定安全性規則,這邊也不用動

安全性規則

當你看到這個畫面,就代表你的 Storage 已經設定完成了

設定完成

Note
為什麼不直接使用 Google Cloud 就好呢?因為 Firebase 本身就是基於 Google Cloud 的服務,而 Firebase 有額外提供許多方便的功能,如:Authentication、Firestore、Realtime Database、Storage、Hosting 等等,在整體 UI 介面上也比較親民,所以我們這邊就使用 Firebase 來進行操作。

取得 Firebase Admin SDK

接著由於我們需要上傳圖片,因此會使用到 Firebase Admin SDK,所以接著就來取得這個 SDK 吧!

首先點一下上方「齒輪」 => 「專案設定

專案設定

接著再點一下「服務帳號」,你就可以看到 Firebase Admin SDK 的頁面

Firebase Admin SDK

最後你只要按一下「產生新的私密金鑰」(你可以依照你的程式語言生成範例程式碼),就可以取得你的 Firebase Admin SDK 金鑰,接著請把這份 JSON 檔案保存好,後面我們會使用。

Danger
請不要隨意將金鑰提供給別人
請不要將這個金鑰放到你的 GitHub 上(如果你真這樣幹,請你把這個儲存庫刪除並重新生成一個新的金鑰吧。)

到目前為止,我們已經準備好了 Firebase 的事前準備,接下來我們就準備往下進行囉!

電商專案

首先我們要先來準備下載一下六角學院所提供的專案,這邊我們會使用到 Git 來進行下載。

請你點擊這個連結「六角專題班學習組電商 API」,接著找到「Download ZIP」取得專案壓縮檔案

Download ZIP

解壓縮後,請你使用 Visual Studio Code 開啟這個專案,你會看到以下這個檔案架構圖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NODEJS-STUDY-EXAMPLE/
├── .github/ # GitHub Actions 設定檔
├── bin/ # 伺服器啟動設定檔
├── config/ # 專案設定檔(如:環境變數、資料庫連線等)
├── controllers/ # 路由控制器,處理業務邏輯
├── db/ # 資料庫設定,連線設定
├── entities/ # 資料庫實體模型
├── middlewares/ # 中介層,處理請求的中間件
├── routes/ # 路由設定
├── test/ # 單元測試
├── utils/ # 共用工具函式
├── .dockerignore # Docker 忽略設定
├── .env.example # 環境變數範例檔案
├── .eslintrc.json # ESLint 設定檔
├── .gitignore # Git 忽略設定
├── .nvmrc # Node.js 版本管理檔案
├── app.js # 專案進入點
├── docker-compose.yml # Docker Compose 設定檔
├── Dockerfile # Docker 設定檔
├── jest.config.js # Jest 測試框架設定檔
├── package-lock.json # npm 鎖定檔
├── package.json # 專案資訊與依賴設定
└── README.md # 專案說明文件

了解基本架構後,我們也要了解一下這個專案所提供的指令,在這個專案中,有幫我們準備好一些指令:

1
2
3
4
5
6
7
8
9
10
11
12
{
"scripts": {
"start": "docker compose --env-file .env up -d --build",
"restart": "docker compose --env-file .env up --force-recreate --build -d",
"stop": "docker compose --env-file .env stop",
"clean": "docker compose --env-file .env down -v",
"dev": "node ./bin/www.js",
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --config ./jest.config.js --coverage --roots ./test/unit",
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testSequencer=./test/sequencer -i --config ./jest.config.js --coverage --roots ./test/integration",
"init:schema": "typeorm schema:sync -d ./db/data-source.js"
}
}

這邊我們來看一下這些指令的用途:

  • start:啟動並建立 Docker 環境
  • restart:重新啟動 Docker 環境
  • stop:停止 Docker 環境
  • clean:清除 Docker 環境
  • dev:啟動開發模式
  • test:unit:執行 Jest 單元測試
  • test:integration:執行 Jest 整合測試
  • init:schema:初始化資料庫

而這邊實際上我們常用的指令僅有 startrestartstopcleantest:integration,其他指令你可以先不用理會。

設定專案環境變數

接下來我們要將 .env.example 複製一份並命名為 .env

1
cp .env.example .env

接著打開 .env 檔案,你會看到以下這些環境變數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# PostgreSQL 設定
POSTGRES_USER=testHexschool
POSTGRES_PASSWORD=pgStartkit4test
POSTGRES_DB=test

# 資料庫連接設定

## Docker 環境請使用 postgres
DB_HOST=postgres
## 本地開發環境請改成 localhost
# DB_HOST=localhost

DB_PORT=5432
DB_USERNAME=testHexschool
DB_PASSWORD=pgStartkit4test
DB_DATABASE=test
DB_SYNCHRONIZE=true
DB_ENABLE_SSL=false

# 伺服器設定
PORT=8080

# LOG 設定
LOG_LEVEL=debug

# JWT 設定
JWT_EXPIRES_DAY=30d
JWT_SECRET=hexschool666

# Firebase Storage 設定
FIREBASE_SERVICE_ACCOUNT=
FIREBASE_STORAGE_BUCKET=

這邊要請你將 Firebase Admin SDK 的金鑰內容複製到 FIREBASE_SERVICE_ACCOUNT 中,此時你畫面會長這樣:

1
2
3
4
5
6
7
8
# Firebase Storage 設定
FIREBASE_SERVICE_ACCOUNT={
"type": "service_account",
"project_id": "firebase-express",
"private
...
}
FIREBASE_STORAGE_BUCKET=

Firebase Admin SDK

接下來有一個很重要的行為要做,也就是將 FIREBASE_SERVICE_ACCOUNT 改成一行,這樣才能正確讀取到金鑰,將整段選取起來,並按下 command + shift + p(Windows:ctrl + shift + p)輸入 Join Lines,這樣就會變成一行

1
FIREBASE_SERVICE_ACCOUNT={"type":"service_account","project_id":"firebase-express","private_key_id":"..."}

Git Join Lines

Note
請務必把整個 JSON 選取起來後再進行 Join Lines,不然會導致 JSON 格式錯誤唷!

接著我們要去取得 Firebase Storage Bucket,請你回到 Firebase 控制台,並點選左側功能列表的「Storage」,進入到 Storage 頁面後,請你找到「gs://....」這一段就是你的 Storage Bucket 名稱,請將這個名稱複製到 FIREBASE_STORAGE_BUCKET

Firebase Storage Bucket

到這邊為止,我們才終於把 Environment Variables 設定完成了,接下來我們就可以開始來準備還原專案囉!

安裝專案套件

接下來我們要安裝專案所需的套件,在輸入指令之前,請先確認以下事項:

  • Node.js 版本是 v20 以上
  • Docker 已經安裝完成並且運作正常

請在終端機輸入以下指令:

1
2
3
docker -v
node -v # 確認 Node.js 版本
npm ci

Note
請不要使用 npm install,因為這個指令會將 package-lock.json 的版本更新,這樣會導致專案的套件版本不一致,所以為了避免這個問題,請使用 npm ci 來安裝套件。
如果你不知道 npm cinpm install 的差異,請參考這篇文章:npm ci 與 npm install 差異

容器化(Docker)環境

接下來我們要使用 Docker 來建立我們的環境,請在終端機輸入以下指令:

1
npm start

運作過程中會稍微等一下

docker compose

執行完畢後,你會看到以下這個畫面:

docker compose

這就代表 npm start 已經把 Docker 環境準備好了,接著請你打開 Docker Desktop,確認畫面是否像這樣兩個綠點:

Docker Desktop

如果你發現沒有正常顯示兩個綠點,那就代表可能沒有正常運作,最基本排除錯誤方式就是輸入 npm run restart 來重新建立 Docker 環境。

測試 API

由於 Docker 是將我們當前整個後端程式碼封裝成一個環境,所以我們可以直接使用 Postman 來測試 API,而當初我們針對後端設定的 Port 是 8080,所以我們可以直接在 Postman 輸入 http://localhost:8080/healthcheck 來測試 API 是否正常運作,如果你看到以下這個畫面,就代表你的 API 已經正常運作了!

ok

到這邊為止,我們已經完成了整個專案的 Docker 建立囉!

該如何開發?

前面我們只是學習該如何把專案給建立起來並且開始 Run 起來,接下來我將會介紹該如何用這個專案來開發。

本地開發前準備

在前面我們所設定的 Env 環境是用來建立 Docker 環境的,並不是拿來開發用的,所以如果你準備開始開發的話,那你就要先將 Env 底下屬性做調整:

1
2
3
4
5
6
7
8
9
10
# 資料庫連接設定

## Docker 環境請使用 postgres
# DB_HOST=postgres # 將這一行註解起來
## 本地開發環境請改成 localhost
DB_HOST=localhost # 取消註解

...
# 伺服器設定
PORT=5500 # 這個 Port 從 8080 改成 5500

我們這邊主要修改的是…

  • DB_HOST:將 postgres 註解起來,並將 localhost 取消註解
  • PORT:將 8080 改成 5500

會這樣做是因為,我們要從 Docker 環境切換到本地開發環境,所以我們要將 DB_HOST 改成 localhost,而 PORT 是因為我們要避免與 Docker 環境的 Port 衝突,所以我們將 Port 改成 5500

接下來你還要注意一件事情,請務必確認你的 Docker PostgreSQL 有正常運作,也就是正常來講,你會看到以下這個畫面:

Docker Desktop

兩個點都應該是要綠色的,如果沒有你是無法啟動專案的唷!

為了確保你可以正常開發,所以我這邊提供 Todo 給你自我檢查:

  • 有跑過 npm ci 安裝套件
  • 有將 .env.example 複製一份並命名為 .env
  • 有將 Firebase Admin SDK 的金鑰複製到 FIREBASE_SERVICE_ACCOUNT
  • 有將 Firebase Storage Bucket 名稱複製到 FIREBASE_STORAGE_BUCKET
  • 有確認 Docker Desktop 是否有兩個綠點
  • 有確認 Postman 是否可以正常測試 API
  • 有將 DB_HOST 改成 localhost,並將 PORT 改成 5500
  • 有確認 Docker PostgreSQL 是否正常運作

如果以上都檢查完畢了,那麼你就可以試著輸入以下指令來啟動專案:

1
npm run dev

專案啟動成功後,你會看到以下這個畫面:

npm run dev

如果有看到的話,恭喜你,你已經可以開始本地開發囉~

Note
中斷執行的終端機,請按下 ctrl + c 即可退出。

開發流程

本專案有加入測試檔案,測試檔案目的是確保你寫出來的程式碼有符合預期結果,該怎麼做呢?

首先請你打開 「六角專題班學習組電商 API」,接著切換到 answer 分支,這個分支就是已經寫好的答案,你可以將 answer 分支下載到你的本地端,這樣你就可以參考答案來開發。

那麼我們的開發流程會是如何呢?這邊提供流程給予參考:

  1. 參考課程提供的 DB 規劃表以及 API 文件來開發功能
  2. 開發完畢後,將位於 answer 分支的測試檔案複製到 main 分支
  3. 輸入 npm run test:integration 來執行整合測試

Note
測試檔案是基於課程提供的 API 文件來撰寫,所以你可以參考 API 文件來撰寫程式碼。

這時候就會看到測試結果,如果有通過的話,那就代表你的程式碼是正確的,如果沒有通過,那就代表你的程式碼有問題,請你再次檢查程式碼。

接下來,就讓我來快速的示範一次整個開發流程唷!

開發範例(使用者註冊)

首先一開始我們會先開發使用者註冊的功能,因此當你打開 routes/users.js 檔案後,你會看到以下這段程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require('express')

const usersController = require('../controllers/users')
const router = express.Router()
// const config = require('../config/index')
// const { dataSource } = require('../db/data-source')
// const logger = require('../utils/logger')('Users')
// const auth = require('../middlewares/auth')({
// secret: config.get('secret').jwtSecret,
// userRepository: dataSource.getRepository('users'),
// logger
// })

router.post('/signup', usersController.postSignup)

module.exports = router

這邊是幫各位大家準備好的起手式,讓你知道基本的寫法是這樣,而註解那一段可以暫時不理會,那部分是針對 Middleware 的開發,接著我們點擊右鍵「移至定義」

移至定義

接著 VSCode 就會幫你打開位於 controllers/users.js 檔案,這邊你會看到以下這段程式碼:

1
2
3
4
5
6
class UsersController {
static async postSignup (req, res, next) {
}
}

module.exports = UsersController

這邊就是讓你開始寫使用者註冊的功能。

那麼這邊就讓我偷懶一下打開 answer 分支的 controllers/users.js 檔案,把 postSignup 的解答貼上來:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const bcrypt = require('bcrypt')

const config = require('../config/index')
const { dataSource } = require('../db/data-source')
const logger = require('../utils/logger')('UsersController')
const generateJWT = require('../utils/generateJWT')

const passwordPattern = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,32}/
const emailPattern = /^[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\.[A-Za-z]{2,}$/

function isUndefined (value) {
return value === undefined
}

function isNotValidSting (value) {
return typeof value !== 'string' || value.trim().length === 0 || value === ''
}

class UsersController {
static async postSignup (req, res, next) {
try {
const { name, email, password } = req.body
if (isUndefined(name) || isNotValidSting(name) || name.trim().length > 10 || name.trim().length < 2 ||
isUndefined(email) || isNotValidSting(email) || !emailPattern.test(email) ||
isUndefined(password) || isNotValidSting(password)) {
logger.warn('欄位未填寫正確')
res.status(400).json({
message: '欄位未填寫正確'
})
return
}
if (!passwordPattern.test(password)) {
logger.warn('建立使用者錯誤: 密碼不符合規則,需要包含英文數字大小寫,最短8個字,最長32個字')
res.status(400).json({
message: '密碼不符合規則,需要包含英文數字大小寫,最短8個字,最長32個字'
})
return
}
const userRepository = dataSource.getRepository('users')
const existingUser = await userRepository.findOne({
where: { email }
})

if (existingUser) {
logger.warn('註冊失敗,Email 已被使用')
res.status(409).json({
message: '註冊失敗,Email 已被使用'
})
return
}
const salt = await bcrypt.genSalt(10)
const hashPassword = await bcrypt.hash(password, salt)
const newUser = userRepository.create({
name,
email,
role: 'USER',
password: hashPassword
})
const savedUser = await userRepository.save(newUser)
logger.info('新建立的使用者ID:', savedUser.id)
res.status(201).json({
message: '註冊成功'
})
} catch (error) {
logger.error('建立使用者錯誤:', error)
next(error)
}
}
}

module.exports = UsersController

貼上來後,我們再把 answer 分支的 test/integration/0-users 資料夾複製到你的專案內

複製資料夾

這邊要注意,複製資料夾時,會包含其他的測試檔案,所以要麻煩你將 0-postSignup.test.js 之外的檔案刪除,這樣才能正確執行測試,畢竟你還沒有寫完其他的功能。

接下來你就可以嘗試輸入以下指令來執行測試:

1
npm run test:integration

如果你看到以下這個畫面,那就代表你的程式碼是正確的,並且通過測試了!(而底下這張圖也是六角的教練們要看的結果圖)

測試結果

Note
你寫出來的程式碼必須要與我們提供的答案一樣,才能通過測試,如果不一樣,那就代表你的程式碼有問題,請再次檢查程式碼唷!

所以每當我們開發完畢一個功能,你就可以把測試解答複製到你的專案內,並且執行測試哩~

透過 GitHub Actions 自動化測試

除了透過本地端來執行測試外,我們也可以透過 GitHub Actions 來自動化測試,這樣就可以讓我們的程式碼在每次 Push 到 GitHub 上時,都能自動執行測試,這樣就能確保我們的程式碼是正確的。

申請 GitHub Token

首先我們要先申請 GitHub Token,這個 Token 是用來讓 GitHub Actions 可以正常運作,請你進入到 GitHub 的 Settings 頁面(點自己頭像)

Setting

並點選左側功能列表的「Developer settings」

Developer settings

接著點選左側功能列表的「Personal access tokens」,進入到「Tokens」頁面,接著點選「Generate new token(classic)」

Generate new token

接著你會看到以下這個畫面,請你填寫以下欄位:

  • Note:填寫 Token 名稱
  • Expiration:選擇 Token 有效期限
  • Select scopes:勾選 repoworkflow,將這兩個權限勾選起來

Generate token

其他欄位都不用理會,接著點選「Generate token」,接著你會看到以下這個畫面:

Token generated

請你將這個 Token 複製起來,這個 Token 只會出現一次,所以請務必複製起來,並且妥善保管,後面我們會用到這個 Token。

設定 GitHub Repository Secret

所以請你打開 GitHub 並建立一個新的儲存庫

建立儲存庫

接著我們要來替這個 GitHub 儲存庫設置 Secrets and variables,請你進入儲存庫的 Settings 頁面,並點選左側功能列表的「Secrets and variables」,接著點選「Actions」

Secrets and variables

接著點選「New repository secret」,進入到新增 Secrets 頁面,這邊我們要新增以下這些 Secrets:

  • FIREBASE_SERVICE_ACCOUNT:Firebase Admin SDK 金鑰
  • FIREBASE_STORAGE_BUCKET:Firebase Storage Bucket 名稱
  • DEPLOY_GITHUB_TOKEN:GitHub Token

也就是我們前面 .env 設定的環境變數,這邊我們要把這些環境變數設定到 GitHub 上

Secrets

Note
由於我們測試有使用到 Firebase Admin SDK 金鑰,所以才會需要特別設定這一塊,而其他方面則不需要,因為預設 .env.example 已經設定好了,當我們執行 GitHub Actions 時,會自動跑 cp .env.example .env 這個指令,所以不需要特別設定。

設定完這些後,你就可以把程式碼 Push 到這個儲存庫囉!理論上,你只要每次 push 到該儲存庫,GitHub Actions 就會自動執行測試囉

GitHub Actions

那麼以上就是完整的流程囉!

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ