ExpressJS (TypeScript) – 建立 HTTP Server
簡介
在現代 Web 開發中,Express 仍是 Node.js 生態系最常被採用的輕量級框架。它提供了直觀的路由、middleware 機制以及與各種第三方套件的良好整合,使得建立 RESTful API 或簡易網站變得非常快速。
將 Express 與 TypeScript 結合,不僅能享受 JavaScript 的彈性,還能透過靜態型別檢查提前捕捉錯誤、提升 IDE 補完與程式碼可讀性。對於剛踏入後端開發的同學,掌握「如何用 TypeScript 建立一個最基本的 Express HTTP Server」是邁向完整全端開發的重要第一步。
本篇文章將從環境安裝、專案初始化、基本伺服器寫法,到路由、middleware、錯誤處理的實作,提供 3~5 個實用範例,並說明常見的陷阱與最佳實踐,最後列出實務上常見的應用情境,幫助你快速上手並在專案中落地。
核心概念
1️⃣ 安裝與專案初始化
在開始寫程式前,我們先建立一個乾淨的 TypeScript + Express 專案。
# 建立資料夾並進入
mkdir my-express-app && cd my-express-app
# 初始化 npm,產生 package.json
npm init -y
# 安裝核心套件
npm i express
npm i -D typescript ts-node @types/node @types/express
接著建立 tsconfig.json,讓編譯器知道我們的目標環境。
{
"compilerOptions": {
"target": "ES2022", // 使用最新的 JavaScript 標準
"module": "commonjs", // Node.js 預設模組系統
"strict": true, // 啟用嚴格模式,提升型別安全
"esModuleInterop": true, // 允許 import express from "express"
"outDir": "./dist", // 編譯後的檔案放置目錄
"rootDir": "./src"
},
"include": ["src"]
}
小技巧:在
package.json加入"start": "ts-node src/index.ts",即可直接npm start執行開發環境。
2️⃣ 建立最簡單的 Express Server
以下程式碼示範最基礎的 HTTP Server,只回傳「Hello, World!」給所有請求。
// src/index.ts
import express, { Request, Response } from "express";
const app = express(); // 建立 Express 應用實例
const PORT = process.env.PORT || 3000;
// 根路由 GET 請求
app.get("/", (req: Request, res: Response) => {
res.send("Hello, World! 🌏");
});
// 啟動伺服器
app.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
});
說明:
express()會回傳一個 Application 物件,我們可以在上面掛載路由、middleware。app.get()只處理 GET 方法,第一個參數是路徑,第二個是處理函式。res.send()會自動依內容類型設定Content-Type,此例回傳純文字。
3️⃣ 加入 TypeScript 型別與環境設定
在大型專案中,我們往往會把路由、設定、錯誤處理分離成不同檔案。以下示範如何使用 interface 為請求參數加上型別,讓 IDE 能提供自動補完。
// src/routes/user.ts
import { Router, Request, Response } from "express";
interface UserParams {
id: string; // 期待的路由參數型別為字串
}
const router = Router();
// 取得單一使用者資訊
router.get("/user/:id", (req: Request<UserParams>, res: Response) => {
const { id } = req.params; // id 已被正確推斷為 string
// 假設從資料庫取得使用者
const user = { id, name: "Alice", age: 30 };
res.json(user);
});
export default router;
在主程式中掛載此路由模組:
// src/index.ts
import express from "express";
import userRouter from "./routes/user";
const app = express();
app.use(express.json()); // 解析 JSON body
app.use("/api", userRouter); // 所有 /api 前綴的請求交給 userRouter
app.listen(3000, () => console.log("Server ready"));
重點:使用 Request<Params>、Response<ResBody> 讓 型別安全 成為開發的默認行為。
4️⃣ Middleware:請求前置處理
Middleware 是 Express 的核心概念,允許在請求到達路由前先執行共通邏輯,例如 日誌、驗證、CORS。
// src/middleware/logger.ts
import { Request, Response, NextFunction } from "express";
export function logger(req: Request, res: Response, next: NextFunction) {
const now = new Date().toISOString();
console.log(`[${now}] ${req.method} ${req.path}`);
next(); // 必須呼叫 next() 才會繼續往下走
}
在 index.ts 中全域掛載:
import { logger } from "./middleware/logger";
app.use(logger); // 所有路由皆會先走 logger
若要只針對特定路由套用,可在路由檔案內直接寫:
router.post("/login", logger, (req, res) => {
// 登入處理...
});
5️⃣ 錯誤處理與伺服器優雅關閉
Express 允許自訂錯誤處理函式,簽名必須有四個參數 (err, req, res, next)。
// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from "express";
export function errorHandler(
err: Error,
_req: Request,
res: Response,
_next: NextFunction
) {
console.error("❗️", err);
res.status(500).json({ message: "內部伺服器錯誤", error: err.message });
}
在所有路由之後掛載:
import { errorHandler } from "./middleware/errorHandler";
app.use(errorHandler);
優雅關閉:在開發或部署環境,最好在收到 SIGINT / SIGTERM 時先關閉伺服器,避免未完成的請求被截斷。
const server = app.listen(PORT, () => console.log(`Listening on ${PORT}`));
process.on("SIGINT", () => {
console.log("\nGracefully shutting down...");
server.close(() => {
console.log("Server closed");
process.exit(0);
});
});
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 建議的解法 |
|---|---|---|
忘記呼叫 next() 在 middleware 中 |
請求卡在中間層,客戶端永遠不會得到回應 | 每個非終結點的 middleware 必須 next(),或回傳 res.end() |
| 直接在路由裡寫大量業務邏輯 | 代碼難以維護、測試困難 | 把業務抽離成 Service / Controller,路由只負責參數解析與回傳 |
未使用 express.json() 解析 JSON body |
req.body 會是 undefined,POST/PUT 失效 |
在所有路由前 app.use(express.json()) |
| 錯誤拋出未被捕獲 | 程式直接 crash,伺服器宕機 | 使用 try/catch + next(err),或全域錯誤處理 middleware |
型別不一致 (例如 req.params.id 被當成 number) |
編譯通過但執行時出錯 | 利用 TypeScript 的 interface 為 req.params、req.body 明確指定型別 |
最佳實踐:
- 分層架構 –
router→controller→service→repository。 - 環境變數 – 使用
dotenv管理PORT、DB_URL等設定,避免硬編碼。 - 日誌 – 以
morgan或winston替代手寫 console,支援等級與檔案輸出。 - 安全 – 加入
helmet、cors,防止常見的 HTTP 攻擊。 - 測試 – 用
jest+supertest撰寫單元與整合測試,確保路由行為不會因重構而斷裂。
實際應用場景
| 場景 | 為何選擇 Express + TypeScript |
|---|---|
| RESTful API | 輕量、路由彈性、支援中介層,配合 TypeScript 可保障介面合約。 |
| 微服務 | 每個服務只需要一個簡單的 HTTP 入口,部署速度快,與 Docker/Kubernetes 配合無縫。 |
| Server‑Side Rendering (SSR) | 搭配 ejs、pug 或 react‑express,在同一個伺服器同時提供前端頁面與 API。 |
| Webhook 接收端 | 如 GitHub、Stripe 等外部服務推送事件,Express 的 middleware 能快速驗證簽名。 |
| 開發測試環境 | 使用 ts-node-dev 即可在本機即時編譯、重新載入,省去繁雜的 Build 步驟。 |
總結
本文從 環境建置 → 基本 Server → 型別化路由 → Middleware → 錯誤處理,一步步示範了如何用 TypeScript 實作一個可靠且易於維護的 Express HTTP Server。掌握這套基礎後,你即可:
- 以型別安全的方式撰寫路由與中介層,減少執行時錯誤。
- 在專案中導入 分層架構、環境變數、日誌與安全,提升可讀性與部署品質。
- 依照不同的實務需求(API、微服務、Webhook)快速擴展或調整。
只要持續將 最佳實踐 內化於日常開發,你的 Express 專案將會在可維護性、效能與安全性上都保持良好狀態。祝開發順利,期待在你的下一個專案裡看到更加完整與強大的 Express+TypeScript 應用!