Day 28 | 部署到 Render
今天把網站放上雲端,結果第一次啟動就當掉。
今天做的事
在Render連接GitHub的repository,Build Command填npm install,Start Command填npm start,並設定環境變數JWT_SECRET。
卡關一:JWT_SECRET 的值貼錯了
我在值的欄位貼的是「產生密鑰的那行指令」,而不是指令執行後印出的結果。這樣密鑰會變成一句公開的文字,等於沒有密鑰。正確做法是先在本機執行指令,把輸出的64字元貼進去,而且線上的密鑰要跟本機不同。
卡關二:建置成功,但啟動就崩潰
Logs 顯示:
Error: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.38' not found
(required by .../node_modules/sqlite3/build/Release/node_sqlite3.node)
sqlite3是第九天就遇過的原生模組,npm install預設會下載別人預先編譯好的版本,而那個版本需要的系統函式庫,比Render主機上的新。解法是讓主機自己從原始碼編譯,把Build Command改成:
npm install && npm rebuild sqlite3 --build-from-source
因為程式碼沒有改動、沒有新的commit,不會自動重新部署,要手動按Manual Deploy。重新部署後,/health回傳{"status":"ok"},服務上線了。
觀察
心得
在我的電腦上能跑不代表在別的環境也能跑。同樣的程式碼、同樣的套件,換了作業系統版本就不一樣。部署的價值就在於逼你面對這些平常看不見的差異。
Day 29 | 線上版測試:伺服器重啟的證據
網站上線了,今天要驗證它在線上的行為,特別是Render文件提到的資料重置。
今天做的事
要證明伺服器真的重啟過,需要有證據。我把/health改成同時回傳啟動時間和運行秒數:
const startedAt = new Date().toISOString();
app.get('/health', (req, res) => {
res.json({
status: 'ok',
startedAt,
uptimeSeconds: Math.round(process.uptime())
});
});
本機測試時,startedAt維持2026-10-19T12:02:37Z沒變,uptimeSeconds從174增加到300。線上版 todo-app-3f13.onrender.com/health 則顯示另一個啟動時間(12:12:53Z)和51秒的運行時間。
觀察
啟動時間不變、運行秒數往上加,代表伺服器沒有重啟;啟動時間變了,才代表真的重啟過。之後比對重新部署前後的startedAt,就能分辨我測到的是新的伺服器還是舊的。
資料重整測試


心得
要驗證一個說法,不能只做動作,還要有證據證明動作真的發生了。如果沒有startedAt,我根本分不出自己測到的是哪一台伺服器。
Day 30 | 30 天總結:從 Hello World 到線上網站
30 天前,我只會用Node.js印出一行Hello World。今天,我有了一個可以註冊登入、管理待辦事項、部署在雲端的網站。
這 30 天做了什麼
Day 1 到 5:HTTP、Request、Express、Middleware、靜態頁面
Day 6 到 8:RESTful API與CRUD
Day 9 到 13:SQLite、前後端驗證、分類、截止日期
Day 14 到 18:篩選、編輯、統計、重構、搜尋
Day 19 到 23:註冊登入、JWT、資料隔離
Day 24 到 29:安全強化、環境變數、部署與線上測試
學到最有感的幾件事
最難的卡關
路由重複(第3、26天)、舊的伺服器占著port導致啟動後沒反應(第十四天)、限流沒有生效(第二十四天)、部署時原生模組版本不符(第二十八天)。這些問題有一個共通點:錯誤訊息往往不在我以為的地方。
連結
GitHub:https://github.com/yyyehh/todo-app
線上版:https://todo-app-3f13.onrender.com
最後
這30天最大的收穫,不是做出了一個待辦清單,而是學會遇到問題時怎麼找原因:看Network的狀態碼、看終端機的紀錄、看回應是HTML還是JSON。這些方法會比任何單一套件的用法,更長久地跟著我。