[express][01]Express + TypeScript 專案從零開始的步驟

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

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 編譯器的配置:

1
pnpm exec tsc --init

修改 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 環境變數文件:

1
pnpm add dotenv

7. 建立 .env 文件

在專案根目錄創建 .env 文件來定義環境變數:

1
2
PORT=3000
DEBUG=*

這裡設定了應用的預設埠和調試模式。

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();

// Middleware
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

// Uncomment if needed for serving static files
// app.use(express.static(join(__dirname, 'public')));

// Use centralized routes
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'; // Load .env variables
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;

// handle specific listen errors with friendly messages
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();

/* GET users listing. */
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 命令啟動:

1
pnpm start

如果一切順利,你應該會看到伺服器成功啟動並開始監聽指定的端口。


15. 最佳實踐與未來發展

  • 環境變數管理:通過 .env 文件靈活管理專案的環境設置。
  • TypeScript 錯誤處理:確保每個文件中的參數、返回值都有正確的型別註解。
  • 中介軟體與路由的拆分:未來可以將中介軟體和路由拆分成更多模組以便

16. 參考連結

參考連結


[express][01]Express + TypeScript 專案從零開始的步驟
https://terryyaowork.github.io/express/web-develop/20241017/368853435/
作者
Terry Yao
發布於
2024年10月17日
許可協議