Express + TypeScript 專案從零開始的步驟

前置說明
本文將教你如何使用 pnpm
來從零開始建立一個 Express + TypeScript 的專案。使用 pnpm
的好處在於它提供了快速、節省磁碟空間的套件管理方式,並且非常適合大型專案的依賴管理。接下來,我們會一步步完成專案的初始化、基本架構的搭建,以及一些 Express 與 TypeScript 的最佳實踐。
1. 建立專案目錄
首先,進入到你想要創建專案的目錄並初始化專案:
1 2 3
| mkdir express_ts_proto cd express_ts_proto pnpm init
|
pnpm init
會初始化專案並生成 package.json
文件,這將作為專案的基礎設定檔。
還有要新增 .gitignore 檔案,把常見的檔案都 ignore 掉。
2. 安裝 Express 和相關依賴
使用 pnpm
安裝 Express 和相關的依賴:
1
| pnpm add express cookie-parser morgan
|
這個指令會安裝 Express 框架以及常用的中介軟體,例如處理 cookie 的 cookie-parser
和記錄 HTTP 請求的 morgan
。
3. 安裝 TypeScript 和相關開發依賴
接著,安裝 TypeScript 及相關的型別定義檔案,讓專案支援 TypeScript:
1
| pnpm add -D typescript @types/node @types/express @types/cookie-parser @types/morgan ts-node @types/debug
|
這些開發依賴會幫助專案在開發階段支援 TypeScript 型別檢查。
4. 初始化 TypeScript 配置
創建一個 tsconfig.json
文件來設置 TypeScript 編譯器的配置:
修改 tsconfig.json
文件如下,來配置輸出和根目錄:
1 2 3 4 5 6 7 8 9 10 11
| { "compilerOptions": { "target": "ES2020", "module": "commonjs", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true }, "include": ["src/**/*"] }
|
這樣的配置可以確保 TypeScript 會將編譯後的 JavaScript 文件輸出到 dist
目錄,並設置專案的源文件路徑為 src
。
5. 建立 Express 應用模板
使用 express-generator
來生成基本的 Express 應用結構,並指定不使用視圖引擎:
1 2 3
| pnpm add express-generator --save-dev
pnpm exec express --no-view
|
這將會建立一個預設的應用結構,隨後我們會將其轉換為 TypeScript 格式。
6. 安裝 dotenv
用來管理環境變數
安裝 dotenv
來處理 .env
環境變數文件:
7. 建立 .env
文件
在專案根目錄創建 .env
文件來定義環境變數:
這裡設定了應用的預設埠和調試模式。
8. 建立應用文件結構
新增 src/
目錄並且在 src/
下建立以下文件結構:
1 2 3 4 5 6 7
| /src /bin www.ts /routes index.ts users.ts app.ts
|
9. 編寫 src/app.ts
請將原本的 app.js
重新命名修改為 app.ts
,並且確認搬移到 src/
資料夾裡面。
app.ts
是應用的核心,負責設置中介軟體(Middleware)和路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import express from 'express'; import { join } from 'path'; import cookieParser from 'cookie-parser'; import logger from 'morgan'; import routes from './routes';
const app = express();
app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser());
app.use(routes);
export default app;
|
10. 編寫 src/bin/www.ts
請講原本的 bin/www
移動並且修改副檔案名 src/bin/www.ts
。
www.ts
負責啟動伺服器並處理錯誤。
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
| #!/usr/bin/env node
import 'dotenv/config'; import app from '../app'; import http from 'http'; import debug from 'debug';
const normalizePort = (val: string) => { const port = parseInt(val, 10); if (isNaN(port)) return val; if (port >= 0) return port; return false; };
const onError = (error: any) => { if (error.syscall !== 'listen') { throw error; }
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } };
const onListening = () => { const addr = server.address(); if (addr !== null && typeof addr !== 'string') { const bind = 'port ' + addr.port; debug('Listening on ' + bind); } else if (addr !== null && typeof addr === 'string') { const bind = 'pipe ' + addr; debug('Listening on ' + bind); } else { console.error('Server address is null.'); } };
const port = normalizePort(process.env.PORT || '3000'); app.set('port', port);
const server = http.createServer(app); server.listen(port); server.on('error', onError); server.on('listening', onListening);
|
11. 建立 src/routes/index.ts
這個文件管理首頁的路由邏輯。
1 2 3 4 5 6 7 8 9 10 11
| import { Router, Request, Response } from 'express'; import usersRouter from './users';
const router = Router();
router.get('/', (req: Request, res: Response) => { res.send('Welcome to the Home Page') }); router.use('/users', usersRouter);
export default router;
|
12. 建立 src/routes/users.ts
users.ts
負責管理 /users
路由邏輯。
1 2 3 4 5 6 7 8 9 10
| import { Router, Request, Response } from 'express';
const router = Router();
router.get('/', (req: Request, res: Response) => { res.send('respond with a resource'); });
export default router;
|
13. 更新 package.json
在 package.json
中添加 start
腳本來啟動應用:
1 2 3
| "scripts": { "start": "ts-node ./src/bin/www" }
|
14. 運行專案
現在,應用可以通過 pnpm start
命令啟動:
如果一切順利,你應該會看到伺服器成功啟動並開始監聽指定的端口。
15. 最佳實踐與未來發展
- 環境變數管理:通過
.env
文件靈活管理專案的環境設置。 - TypeScript 錯誤處理:確保每個文件中的參數、返回值都有正確的型別註解。
- 中介軟體與路由的拆分:未來可以將中介軟體和路由拆分成更多模組以便
16. 參考連結
參考連結