Chào các bạn, sau đây mình xin giới thiệu cho các bạn loạt bài viết về việc Sử dụng TypeScript để viết API bảo mật với Node.js và Express. Trong khuôn khổ 5 bài có thể sẽ không đầy đủ và sai sót nhiều. Mong các bạn comment góp ý. Mình xin cảm ơn.
Tổng quan các bài viết
- Giới thiệu và cài đặt ban đầu
- Tạo Modeling Data và Services
- Tạo Endpoints
- Bảo mật API
- Quản lý quyền
Mình sẽ ra lần lượt các bài.
Trong bài viết đầu tiên Giới thiệu và cài đặt ban đầu, mình sẽ hướng dẫn cách cài đặt TypeScript trong Node.js và Framework Express sử dụng Hot-Module Replacement của webpack.
Tôi sẽ lấy một ví dụ cụ thể về việc làm một trang web mà liên quan đến menu của nhà hàng. Qua đó sẽ tìm hiểu cách xây dựng API đầy đủ tính năng bằng Node.js, Express và TypeScript, cho phép client thực hiện các thao tác với data trên các resources [ở đây là menu của nhà hàng]. Sử dụng TypeScript với Node.js cho phép bạn truy cập vào việc kiểm tra kiểu tĩnh tùy chọn cùng với công cụ mạnh mẽ cho các ứng dụng lớn và các tính năng ECMAScript mới nhất.
Ngoài ra, chúng ta sẽ tìm hiểu cách xác định data models, tạo data service và xây dựng các modular endpoints. Chúng ta cũng có thể sử dụng Auth0 để bảo mật API.
Chúng ta sẽ sử dụng rule trong business để hạn chế quyền truy cập API:
- Bất cứ ai cũng có thể đọc các item trong menu.
- Chỉ admin mới được phép CRUD các item trong menu đó.
Để đơn giản, tôi sẽ lưu data ngay trong RAM.
Nội dung
- Cài đặt project với Node.js và Express
- Cài đặt các dependeccy
- Khởi tạo TypeScript với Node.js
- Sử dụng biến môi trường env
- Tạo Node App với Express sử dụng TypeScript
- Set Up Hot-Module Replacement [HMR] for TypeScript with Webpack
Các bạn tạo thư mục mới tùy ý để chứa project. Mình đặt tên là typescript-secure-api
và cd vào đó
1
2
3
mkdir typescript-secure-api
cd typescript-secure-api
Tiếp theo, khởi tạo một project Node.js mới với file package.json
mặc định
1
2
npm init -y
2. Cài đặt các dependeccy
Chúng ta cần require một số các dependency để tạo server Express bảo mật với TypeScript.
1
2
npm i express dotenv cors helmet
- Express: Cũng không cần trình bày nhiều. Một framework nhanh, gọn, nhẹ cho Node.js.
- Dotenv: Dotenv là một module Zero-dependency, nó load các biến môi trường từ file .env vào process.env.
- CORS: Một middleware Express. Các bạn có thể tham khảo ở đây.
- Helmet: Một middleware Express, bảo mật các ứng dụng bằng cách setting các HTTP headers khác nhau, giúp giảm thiểu các attack vectors phổ biến.
Cài đặt typescript như các dependency
1
2
npm i -D typescript
Để sử dụng TypeScript hiệu quả, cần cài đặt định nghĩa kiểu cho các packages trên
1
2
npm i -D @types/node @types/express @types/dotenv @types/cors @types/helmet
Khi một package không có các loại build-in, bạn có thể cài đặt định nghĩa kiểu thông qua @types npm namespace – nơi lưu trữ các định nghĩa kiểu của TypeScript trong project DefiniteTyped. Khi các package được cài đặt, các types sẽ tự động được compiler của TypeScript include vào.
3. Khởi tạo TypeScript với Node.jsĐể giúp compiler của TypeScript hiểu cấu trúc project, cần tạo file tsconfig.json
trong folder project bằng câu lệnh sau
1
2
npx tsc --init
Đó là tất cả những gì bạn cần để định configure project này với các giá trị mặc định.
4. Sử dụng biến môi trường envThay vì sử dụng các hard-coded configuration variables trong các file, bạn có thể xác định tất cả các biến đó ở một file và import chúng vào các module nếu cần. File này thương có tên là .env
:
1
2
touch .env
Trong file, mình định nghĩa PORT của server:
1
2
PORT=7000
Bằng cách sử dụng package dotenv, trong bất cứ module nào mình cũng có thể sử dụng biến local được định nghĩa trong file .env
5. Tạo Node App với Express sử dụng TypeScriptChú ý : Trong file .env có thể chứa các thông tin nhạy cảm VD như API keys,… Vì vậy nên thêm file này vào .gitignore.
Để cho project có cấu trúc tốt, mình sẽ thêm một folder src
1
2
mkdir src
Trong folder src
mình tạo một file tên là endtrypoint.ts
1
2
touch src/endtrypoint.ts
Dưới đây sẽ là nội dung của file endtrypoint.ts
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Required External Modules
*/
/**
* App Variables
*/
/**
* App Configuration
*/
/**
* Server Activation
*/
/**
* Webpack HMR Activation
*/
Tại mỗi phần comment, mình sẽ thêm nội dung như sau:
Phần Required External Modules
, thêm các dependency đã cài đặt trước đó và load các biến môi trường trong file .env
bằng cách sử dụng method package.json
0
1
2
3
4
5
6
7
8
9
10
11
/**
* Required External Modules
*/
import * as dotenv from "dotenv";
import express from "express";
import cors from "cors";
import helmet from "helmet";
dotenv.config[];
Phần package.json
1, kiểm tra biến môi trường PORT đã được load vào process.env chưa. Nếu ok, sẽ truyền value của nó [dạng number] vào và tạo một instance của Express. Nếu không thì sẽ exit.
1
2
3
4
5
6
7
8
9
10
11
12
/**
* App Variables
*/
if [!process.env.PORT] {
process.exit[1];
}
const PORT: number = parseInt[process.env.PORT as string, 10];
const app = express[];
Phần package.json
2, liên kết các function middleware trong packages mà đã import vào module entry point.
1
2
3
4
5
6
7
8
/**
* App Configuration
*/
app.use[helmet[]];
app.use[cors[]];
app.use[express.json[]];
package.json
3 là một collection của 14 middleware function nhỏ mà nó thiết đặt các HTTP Response Headers. Liên kết package.json
4 không gồm tất cả các middleware function này, nhưng cung cấp các giá trị mặc định như DNS Prefetch Control, Frameguard, Hide Powered-By, HSTS, IE No Open, Don’t Sniff Mimetype và XSS Filter.
Chúng ta cho phép các CORS request bằng method package.json
5. Sau đó, parse các request truyền vào dưới dạng JSON bằng method package.json
6, cái sẽ xác định các package.json
7 object với package.json
8 object gồm các data đã được parse.
Phần package.json
9, tạo Express server
1
2
3
4
5
6
7
8
/**
* Server Activation
*/
const server = app.listen[PORT, [] => {
console.log[`Listening on port ${PORT}`];
}];
6. Set Up Hot-Module Replacement [HMR] for TypeScript with Webpack
Quá trình compile TypeScript có thể làm tăng thời gian khởi động. Tuy nhiên, nếu có bất cứ thay đổi nào trong sourcecode, bạn không cần recompile toàn bộ project. Chúng ta có thể thiết lập webpack Hot-Module Replacement [HMR] để giảm đáng kể thời gian khởi động lại khi thực hiện thay đổi.
Cài đặt Webpack Dependencies cho TypeScript
1
2
npm i -D ts-loader webpack webpack-cli webpack-node-externals
tsconfig.json
0: Giúp cho việc tiền xử lý các file TypeScript để bundle ra các file Javascript.tsconfig.json
1: Một module bundle, có khả năng transforming, bundling, or packaging bất kỳ tresource hoặc asset nào.tsconfig.json
2: module cung cấp một bộ các lệnh để tăng tốc độ khi cài đặt một webpack project.tsconfig.json
3: dễ dàng exclude Node.js module từ webpack bundle.
Trong thư mục project tạo file tsconfig.json
4
1
2
touch webpack.config.ts
Với nội dung dưới đây
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
const webpack = require["webpack"];
const path = require["path"];
const nodeExternals = require["webpack-node-externals"];
module.exports = {
entry: ["webpack/hot/poll?100", "./src/endtrypoint.ts"],
watch: true,
target: "node",
externals: [
nodeExternals[{
whitelist: ["webpack/hot/poll?100"]
}]
],
module: {
rules: [
{
test: /.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
}
]
},
mode: "development",
resolve: {
extensions: [".tsx", ".ts", ".js"]
},
plugins: [new webpack.HotModuleReplacementPlugin[]],
output: {
path: path.join[__dirname, "dist"],
filename: "endtrypoint.js"
}
};
Có nhiều điều cần nói trong file config webpack này, nhưng tôi chỉ đề cập đến những task ở high-level cần hiểu:
- Xác định file entry trong project:
tsconfig.json
5 . - Định nghĩa loader mà webpack cần để compile file source:
tsconfig.json
0 cho các file có đuôi làtsconfig.json
7 vàtsconfig.json
8 - Định nghĩa directory, nơi mà webpack sẽ lưu trữ các file source đã compiled: đó là directory
tsconfig.json
9
Enable Hot-Module Replacement trong ứng dụng Express
Để Enable Hot-Module Replacement [HMR] trong Express server, cần update lại file endtrypoint.ts
.
Trong fileendtrypoint.ts
, tại phần .env
2
1
2
3
4
5
6
7
8
9
/**
* Webpack HMR Activation
*/
if [module.hot] {
module.hot.accept[];
module.hot.dispose[[] => server.close[]];
}
Bình tĩnh, hãy đọc tiếp phần dưới khi thấy lỗi báo đỏ .env
3.
Ở đây, chúng ta nhận thấy rằng Hot-Module Replacement đã enable thông qua HotModuleReplocationPlugin cho module được xác định trong endtrypoint.ts
. Nếu HMR đã enable, trong module sẽ có interface .env
5, cái mà có thể sử dụng để thực hiện các thao tác trên module.
Sử dụng interface method accept[] để accept các module và các dependency của nó cập nhật. Sau đó, interface method dispose[] được thực hiện khi module cuối cùng được thay thế. Các hành động xóa hay thoát sẽ xóa mọi resources đã tạo, VD kết thúc ứng dụng Node.js và thoát tất cả các kết nối hiện có.
Do .env
6 không được định nghĩa bất kì đâu trong file, nên TypeScript compiler sẽ quăng ra một error: Thuộc tính .env
3 chưa được định nghĩa. Để fix, chúng ta sẽ sử dụng .env
8 – một thư viện quản lý state của Angular
.env
8 cung cấp một src
0, cái mà được định nghĩa cho webpack hot module. Hãy update lại phần .env
2 như sau
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
/**
* Webpack HMR Activation
*/
type ModuleId = string | number;
interface WebpackHotModule {
hot?: {
data: any;
accept[
dependencies: string[],
callback?: [updatedDependencies: ModuleId[]] => void,
]: void;
accept[dependency: string, callback?: [] => void]: void;
accept[errHandler?: [err: Error] => void]: void;
dispose[callback: [data: any] => void]: void;
};
}
declare const module: WebpackHotModule;
if [module.hot] {
module.hot.accept[];
module.hot.dispose[[] => server.close[]];
}
Run server Node-Express và TypeScript
Với việc cài đặt HMR, cần cài đặt thêm 2 script trong file package.json
, đó là src
3 và tsconfig.json
1 [để ý tên file endtrypoint.js nhé]
1
2
3
4
5
6
"main": "endtrypoint.js",
"scripts": {
"start": "node dist/endtrypoint",
"webpack": "webpack --config webpack.config.ts"
},
tsconfig.json
1 script sẽ chạy và cấu hình tsconfig.json
4và bundle ra file src
7 trong directory tsconfig.json
9.
Để chạy, chúng ta sẽ mở 2 cửa sổ terminal, một cái để gọi tới tsconfig.json
1
1
2
npm run webpack
Một cái để gọi tới src
3
1
2
npm start
Trong terminal chạy src
3, hiển thị message “Listening on port 7000” -> OK.
Như vậy, mình đã xong phần một hướng dẫn Giới thiệu và cài đặt ban đầu cho loạt seri về việc Sử dụng TypeScript để viết API bảo mật với Node.js và Express.