課程從頭進行到現在,我們已經學習了 Node.js 模組、Express 路由、middleware、JWT、bcrypt、PostgreSQL 與容器化等。這一週的最終任務,就是把這些所學串聯、整合起來,實際打造出一個能運作的專案。
具體來說,你會拿到一個完整的健身房網站前端(會員、教練、課程、購買方案等頁面都完成了),以及一份 API 規格書(Swagger)。不過目前這個專案的後端不存在,所以你的任務就是根據現有情境跟規格,來嘗試將後端撰寫、建置出來,讓這個專案能正確運作。
關於後端程式的規劃跟撰寫,像是資料庫設計、程式如何拆分、要使用 TypeORM 或 pg,同學都可自行決定。驗收會從外部呼叫你的 API,然後檢查回應結果是否正確。
-
安裝並啟動 Docker Desktop,並確認 Node.js 版本 >= 20
-
Fork 作業專案 https://github.com/hexschool/node-js-final-2026/,再 clone 同學自己的 fork,進到專案後執行
npm install- 這次需使用到 Fork,因為最終驗收是要跑自己 Repo 的 GitHub Actions
- Fork 專案之後進到自己 Repo 的 Actions 分頁,按下「I understand my workflows, go ahead and enable them」按鈕,因為 GitHub 對於 fork 過來的專案預設會關閉 Actions,所以我們需手動啟用。
-
在此專案根目錄的終端機輸入:
docker compose up -d,首次啟動要 build 前端,所以會需要多一些時間(約 2~5 分鐘),完成後會啟動三個服務,分別對應下面三個網址:http://localhost:3000:健身房前端頁,初始每一頁會報 API 錯誤,這些還不能正常運作的頁面與功能,就是你的待辦清單http://localhost:8081:Swagger API 文件。這是作業唯一的 API 規格來源,每個 endpoint 點進去都能看到完整的 request/response 範例與錯誤訊息localhost:5432:PostgreSQL 資料庫(目前是空的),裡頭的資料表要從你的後端建立
(在 Docker Desktop 也會看到 frontend(3000)、swagger(8081)、postgres(5432) 成功啟動)
- 接著在專案中建立一個後端資料夾,名稱請使用
backend(作業後續的容器化階段,docker-compose.yml也是使用這個資料夾名稱)。建立後的專案結構如下:
node-js-final-2026/
├── backend/ # 目前要撰寫的後端,跑在 localhost:8080
├── frontend/
├── docs/
├── test/
└── docker-compose.yml- 承上,接著進入
backend/,初始化並安裝需要的後端套件,最後將專案根目錄的環境變數範本(.env.example)複製到backend/.env。需注意:- 記得不可把
.env推上 GitHub - 後端相關套件需安裝在
backend/,不可安裝在專案根目錄 ⚠️ .env裡的PORT=8080不可更動,這個後端需運作在 8080,因為前端寫死http://127.0.0.1:8080/api/路徑,以及 Swagger 的 Try it out 也是使用這個 port;如果更動 8080 port,前端跟 Swagger 就會運作失敗
- 記得不可把
# 進入後端資料夾
cd backend
# 初始化
npm init -y
# 安裝所需要的套件。例如:
npm install express cors dotenv pg
# 建議安裝 nodemon(存檔會自動重啟,方便開發)
npm install -D nodemon- 承上,初始化並安裝套件完成後,接著開啟
backend/package.json,在scripts中設定後端的啟動指令。以下範例假設實際負責啟動伺服器的入口檔案為backend/bin/www.js,在scripts設置:
"dev": "nodemon ./bin/www.js",
"start": "node ./bin/www.js"
npm run dev:開發期使用,透過 nodemon 啟動後端,當程式碼變更時,會自動重新啟動npm start:直接透過 Node.js 啟動後端,不會自動重新啟動。(GitHub Actions 驗收時會使用npm start,所以記得要加上這個設定。)
server.js、app.js 或其他路徑,請依照實際的專案結構調整指令。
整體環境架構說明: 這份作業分成開發期與容器化兩個階段。
開發期:
- 整體開發環境包含前端、Swagger、PostgreSQL、測試,以及同學正在撰寫的
backend/ - 前端、Swagger 與 PostgreSQL 透過 Docker Compose 啟動
- 開發期雖然已經將
backend/建立在作業專案裡,但目前的docker compose up -d還不會啟動後端 - 後端需另外進入
backend/,透過npm run dev啟動
node-js-final-2026/
├── backend/ # npm run dev,跑在 localhost:8080
├── frontend/ # Docker Compose,跑在 localhost:3000
├── docs/ # Docker Compose,跑在 localhost:8081
├── test/
└── docker-compose.yml
PostgreSQL # Docker Compose,跑在 localhost:5432容器化階段:
- 待開發期功能做完、正式繳交作業之前,再替
backend/撰寫 Dockerfile,並將 backend 服務加入docker-compose.yml - 完成後,前端、Swagger、PostgreSQL 與後端都能透過 Docker Compose 啟動
本週有 M0(確認後端服務正常啟動)、M1~M6 主軸任務,以及壓軸的容器化任務;另外有一個選做的 upload 加分題(圖片上傳功能,不列入驗收)。建議依據 Swagger 的分組順序,從 M0、M1 開始依序開發到 M6,最後再完成容器化任務。
任務項目:
-
M0|確認後端服務啟動(需先完成)
- 內容:實作一支 GET /healthcheck 端點,回報服務狀態
- 驗證:沒有獨立測試,但這支 API 服務要能正常運作
- 前端頁面:屬於確認服務,無對應畫面
-
M1|種資料
- 內容:技能、方案的新增/查詢/刪除
- 驗證:
npm run test:m1 - 前端頁面:健身方案列表
-
M2|會員系統
- 內容:註冊、登入(簽發 JWT)、查看與修改個人資料(暱稱、密碼)
- 驗證:
npm run test:m2 - 前端頁面:註冊、登入、會員個人資料
-
M3|教練後台
- 內容:升級為教練、維護教練個人檔案(含技能)、課程的開設/修改/查詢
- 驗證:
npm run test:m3 - 前端頁面:教練個人後台、課程管理
-
M4|公開瀏覽
- 內容:教練列表、教練詳情、教練的課程,以及全站課程列表(皆免登入)
- 驗證:
npm run test:m4 - 前端頁面:教練列表、教練詳情
-
M5|購買與報名
- 內容:購買方案、報名/取消課程,以及查看本人的購買紀錄與課表(含剩餘堂數)
- 驗證:
npm run test:m5 - 前端頁面:購買方案、報名課程、我的課表、我的訂單
- 注意事項:
- 四句固定錯誤訊息:報名課程失敗時,前端會用錯誤訊息文字判斷要跳哪個提示視窗,下面四句必須一字不差(連標點符號都需一致):
已經報名過此課程/已無可使用堂數/已達最大參加人數,無法參加/請先登入 - 取消報名是「軟刪除」:取消時系統不會刪掉這筆報名紀錄,只是標註一個取消時間(紀錄留著);也因為這樣,取消過的課不能再報名。另外剩餘堂數沒有對應欄位,它是根據「買的總堂數 − 未取消的報名數」計算出來
- 四句固定錯誤訊息:報名課程失敗時,前端會用錯誤訊息文字判斷要跳哪個提示視窗,下面四句必須一字不差(連標點符號都需一致):
-
M6|月營收統計
- 內容:教練查詢自己指定月份的營收統計(需跨方案與報名做聚合計算)
- 驗證:
npm run test:m6 - 前端頁面:教練營收報表
- 注意事項:
- 時間:一筆報名算進哪個月,看的是「報名建立的時間」,不是上課時間;年份固定為當年
- 單堂均價 = 全部方案的總價 ÷ 全部方案的總堂數
- 月營收 = 當月未取消的報名數 × 單堂均價(先乘再無條件捨去 floor)
- 完整公式與計算範例,可參考 Swagger 的 M6 章節
-
壓軸|容器化
- 內容:替
backend/撰寫 Dockerfile,並將 backend 服務加入docker-compose.yml - 驗證:執行
docker compose up -d --build backend postgres後,再執行npm run test:smoke - 前端頁面:無
- 內容:替
開發時的終端機配置:
進行 M0~M6 開發時,建議同時開啟兩個終端機:
- 終端機 A|啟動後端
- 說明:進入作業專案裡的
backend/,執行npm run dev,讓後端持續監聽localhost:8080 - 路徑:node-js-final-2026/backend/
- 說明:進入作業專案裡的
- 終端機 B|執行測試:
- 說明:停留在專案根目錄,依照目前的任務進度執行
npm run test:m1~npm run test:m6 - 路徑:node-js-final-2026/
- 說明:停留在專案根目錄,依照目前的任務進度執行
M1~M6 主軸開發流程:
- 先到 Swagger(
localhost:8081)看相關 API 的規格,了解 request/response 的欄位跟範例 - 在專案裡的
backend/撰寫程式(如果使用 nodemon,會在存檔後自動重啟) - 用 Swagger 的 Try it out,測試撰寫是否正確;需要登入的 API,先按右上角 Authorize 貼上登入後拿到的 token
- 等這個任務(例如 M1)的幾支 API 都完成後,可在專案根目錄的終端機執行對應的測試(M1 就執行
npm run test:m1),如果沒通過就根據錯誤訊息調整 - 若這個任務有對應的前端頁面(見上方「前端頁面」欄位),也可以重新整理
localhost:3000,確認相關頁面、功能是否正常運作;若是沒有對應畫面的 API(例如 M1 技能種資料),主要以上方第 3、4 點確認即可
共通規則:
- 回傳格式與狀態碼:驗收只看大方向,請求成功就回 2xx(200、201 都算對),失敗就回 4xx,再搭配回傳的
status欄位。格式大致是:成功{ "status": "success", "data": ... }、失敗{ "status": "failed", "message": "..." }。錯誤訊息除了 M5 那四句要一模一樣,其他情況的文字可以自訂,細節以 Swagger 文件為主 - 環境變數:資料庫連線和
JWT_SECRET都從環境變數讀取(key 名對照作業專案.env.example),不可寫死在程式裡 - 自動建表:後端初始連到的是一個空資料庫(有 database、但沒有任何資料表),所以啟動時就得把需要的資料表建好
- port 與後端服務啟動:後端的 port 固定用 8080(參照環境準備第 5 點),而且
GET /healthcheck要等資料庫就緒後才回應 200(M0)
壓軸任務:容器化
從 M0、M1 一路到 M6 都完成後,接著要進行最後的容器化任務。
在專案的 backend/ 裡寫好 Dockerfile、.dockerignore(至少排除 node_modules 與 .env,別讓它們被打包到 image)。再照專案根目錄 docker-compose.yml 裡下方預留的 W10 容器化挑戰,根據註解寫好的規則、提示來將 backend 服務補上。
在進到運行容器化的步驟前,要記得把原先在 backend/ 執行的 npm run dev 關閉,將 8080 port 空出,避免後續容器化的 backend 無法使用 8080 port。確認後在專案根目錄的終端機執行:
docker compose up -d --build backend postgres
npm run test:smoke # 會測幾支基本 API,確認容器裡的後端與資料庫能連線跟運作以上步驟完成後,之後只要 docker compose up -d 指令就能將整體健身房網站運行起來。
在開發或容器化階段,都是在專案根目錄輸入 npm test 來確認各個測試項目;
如果是開發階段,則可使用 npm run test:m1 ~ npm run test:m6 分任務完成的階段來測試。
測試結果判別:
- ✓ 表示測試通過
- ✕ 表示測試失敗
作業繳交前必須通過 npm test 整體測試。
最後有看到 Tests: 68 passed, 68 total 即代表全數通過。
在容器化階段的步驟完成,並 push 上 GitHub Repo,這時 Repo 的 Actions 會自動跑同一套驗收,測試 M1~M6、容器化部分(共 7 個 job)。在 Actions 跑完後可點進此次的 workflow run 頁面,內容會有逐條 ✅/❌ 的結果,讓我們得知項目的正確與否。若有看到錯誤,可再根據錯誤訊息來做調整。
繳交前提醒不需更動部分:
作業專案的 frontend/、docs/、test/、.github/、根目錄 package.json/package-lock.json。
你主要撰寫並新增的是 backend/,相關套件需安裝在 backend/package.json。
排查問題前,可先確認目前的階段:
- 開發期:後端位於作業專案的
backend/,使用npm run dev啟動。 - 容器化後:後端位置不變,但改由 Docker Compose 啟動。
Q:後端連不上資料庫,或 docker compose up 啟動失敗?
先確認 Docker Desktop 已開啟,再於專案根目錄執行:docker compose ps。如果 postgres 沒有正常啟動,可以執行 docker compose logs postgres 來查看 log。
常見原因包含 Docker 尚未啟動完成、Port 被占用,或環境變數設定錯誤。
Q:後端出現 EADDRINUSE,顯示 8080 已被占用?
代表已有其他程式使用 8080,常見情況是:
- 重複執行了兩次
npm run dev - Docker backend 與本機執行的 backend 同時啟動
如果是 Docker backend 占用,可先在專案根目錄執行:docker compose stop backend,再進入 backend/ 啟動本機後端。
(記得不可修改 8080,因為前端與 Swagger 都固定使用這個 Port。)
Q:執行 npm test 時出現連線錯誤(像是:ECONNREFUSED)?
ECONNREFUSED 通常代表測試連不到 localhost:8080,也就是後端尚未啟動或啟動失敗。
可先開啟:http://localhost:8080/healthcheck,這支 API 用來確認後端與資料庫是否已準備完成。若無法正常回應,請先查看後端終端機或 Docker log。
- 開發期請進入
backend/執行:npm run dev - 容器化後請在專案根目錄執行:
docker compose up -d --build backend postgres
確認 /healthcheck 可正常回應後,再執行測試。
另外,這份作業不會自動建立後端的啟動指令,請自行在 backend/package.json 設定好 dev、start,並指向正確的入口檔案。
Q:前端呼叫 API 時出現 CORS 錯誤?
前端、Swagger 與後端使用不同 Port,因此後端需要開放跨來源請求。
記得加上 CORS middleware(app.use(cors()))。
Q:頁面只剩選單和頁尾,內容一片空白?
先查看瀏覽器 Console。如果同時出現 401、無效的 token 或未授權訊息,可能是瀏覽器仍保留舊的登入 token。通常發生情境:
- 執行過
db:reset - 修改過
JWT_SECRET - 重新建立過資料庫
解法:
可到 DevTools 的 Application → Cookies,刪除 localhost:3000 下的 token,再重新整理並登入。
若沒有 token 相關錯誤,請再從 Console 或 Network 查看是哪支 API 失敗。
Q:一定要使用 TypeORM 嗎?可以只使用 pg 嗎?
都是可行的。這份作業不限制資料庫操作方式,驗收主要確認 API 路徑、回傳格式、狀態碼與實際行為是否符合規格。
不過資料必須能存進 PostgreSQL,且後端啟動時要能在空資料庫中建立所需資料表。
Q:M5 報名課程一直卡在錯誤訊息?
以下四句必須完全一致,包含文字、標點與空格:
已經報名過此課程
已無可使用堂數
已達最大參加人數,無法參加
請先登入可再檢查這些文字是否完全一致。
Q:容器重啟後資料不見了?
驗收會重啟一次容器,確認先前建立的資料是否仍然存在。因此,資料必須確實寫入 PostgreSQL,不能只暫存在後端程式的變數或陣列中,否則後端重啟後資料就會消失。
只要每一筆資料都有正確寫入資料庫,容器重啟後資料就會保留,也能通過這項驗收。
Q:本機測試與 GitHub Actions 結果不同?
常見原因是本機與 CI 的資料庫或環境不同。
可以先執行:npm run db:reset,清空本機資料庫後重新測試。
Q:GitHub Actions 紅燈,要怎麼找問題?
進入失敗的 workflow,依序查看:
- 哪一個 job 失敗。
- 第一個出現紅色叉叉的 step。
- 該 step 中最早出現的錯誤訊息。
常見方向:
- 找不到
backend/:確認後端是否建立於專案根目錄,並已 commit、push 到 GitHub。 - backend 無法啟動:檢查啟動指令、環境變數與資料庫連線。
- 測試失敗:依測試名稱回本機執行對應的
npm run test:m{N}。
建議先在本機重現並修正,再重新 push。
Q:哪些檔案不能修改?
以下內容屬於作業專案原先環境(前端、文件、驗收檔案),請勿修改:
frontend/
docs/
test/
.github/
根目錄 package.json
根目錄 package-lock.json主要需要新增與修改的是:backend/。
後端套件也應安裝在 backend/,因此 backend/package.json 與 backend/package-lock.json 可以正常修改。
docker-compose.yml 是例外,容器化階段需要依照註解加入 backend 服務。