Apidog

Nền tảng phát triển API hợp tác tất cả trong một

Thiết kế API

Tài liệu API

Gỡ lỗi API

Giả lập API

Kiểm thử API tự động

Cách Xây Dựng REST API với TypeScript (Có Ví Dụ)

中村 拓也

中村 拓也

Updated on tháng 4 2, 2025

Giới thiệu

TypeScript nâng cao trải nghiệm phát triển bằng cách thêm kiểu tĩnh vào JavaScript, giúp phát hiện lỗi tiềm ẩn trong quá trình phát triển thay vì vào thời gian chạy. Điều này đặc biệt có giá trị cho phát triển API, nơi việc duy trì tính toàn vẹn của dữ liệu và sự nhất quán của hợp đồng là rất quan trọng. Đến cuối hướng dẫn này, bạn sẽ có một API REST được tổ chức tốt và an toàn về kiểu дан để sẵn sàng cho sử dụng trong thế giới thực.

Giải thích Kiến trúc REST API

Trước khi đi vào thực hiện, hãy làm rõ những gì làm cho một API thực sự tuân thủ REST:

Nguyên tắc REST Cơ bản

  1. Không trạng thái: Mỗi yêu cầu từ client chứa tất cả thông tin cần thiết để xử lý nó. Máy chủ không lưu trữ ngữ cảnh của client giữa các yêu cầu.
  2. Dựa trên Tài nguyên: Tài nguyên được xác định bằng URL và các thao tác được thực hiện bằng cách sử dụng các phương thức HTTP chuẩn.
  3. Đại diện: Tài nguyên có thể có nhiều đại diện (JSON, XML, v.v.) mà client có thể yêu cầu.
  4. Các phương thức HTTP chuẩn: REST sử dụng các phương thức như GET, POST, PUT, DELETE và PATCH để thực hiện các thao tác CRUD:
  • GET: Lấy tài nguyên
  • POST: Tạo tài nguyên mới
  • PUT: Cập nhật tài nguyên hiện có (cập nhật toàn bộ)
  • PATCH: Cập nhật một phần tài nguyên
  • DELETE: Xóa tài nguyên

Giao diện đồng nhất: Nhận diện và thao tác tài nguyên một cách nhất quán thông qua URL và các phương thức HTTP.

REST API là gì?

Tại sao nên sử dụng TypeScript cho REST API?

TypeScript cung cấp một số lợi thế cho phát triển API:

  1. Kiểm tra kiểu tĩnh: Phát hiện lỗi liên quan đến kiểu trong quá trình phát triển
  2. Hỗ trợ IDE nâng cao: Cung cấp hoàn thành tự động và tài liệu tốt hơn
  3. Định nghĩa giao diện: Cho phép hợp đồng rõ ràng giữa client và server
  4. Quản lý mã: Tạo điều kiện cho kiến trúc tốt hơn thông qua các module và decorator
  5. Tái cấu trúc dễ dàng hơn: Đảm bảo an toàn kiểu giúp thay đổi quy mô lớn an toàn hơn
  6. Mã tự tài liệu: Các kiểu phục vụ như tài liệu cho cấu trúc dữ liệu

Dưới đây là một cách viết có ngữ cảnh hơn cho phần kiểm tra API mà chảy tốt hơn với hướng dẫn API REST TypeScript toàn diện của bạn:

Tại sao bạn nên kiểm tra REST API của mình với Apidog

Khi bạn phát triển REST API TypeScript của mình, Apidog cung cấp trải nghiệm kiểm tra vượt trội so với Postman bằng cách tích hợp sâu vào quy trình phát triển của bạn. Phương pháp đầu tiên của công cụ hoàn toàn bổ sung cho sự an toàn kiểu của TypeScript, cho phép bạn:

Xác thực các phản hồi với giao diện TypeScript của bạn
Apidog tự động kiểm tra rằng các phản hồi API khớp với các lược đồ mà bạn đã định nghĩa, phản ánh việc kiểm tra kiểu thời gian biên dịch của TypeScript ở cấp độ API. Điều này phát hiện các sự không khớp kiểu dữ liệu (như khi một ID số được trả về dưới dạng chuỗi) có thể gây ra lỗi thời gian chạy trong mã client TypeScript của bạn.

Tạo dữ liệu kiểm tra thực tế
Dựa trên các định nghĩa mô hình TypeScript của bạn (như giao diện Book), Apidog có thể tự động tạo các trường hợp kiểm tra với dữ liệu giả định được kiểu đúng cách, giúp tiết kiệm hàng giờ thiết lập kiểm tra thủ công trong khi đảm bảo các kiểm tra của bạn phản ánh các mẫu sử dụng thực tế.

Bảo trì sự nhất quán trên các môi trường khác nhau
Hệ thống quản lý môi trường cho phép bạn định nghĩa các biến được kiểu (như URL cơ sở API) hoạt động trên các môi trường phát triển, staging và sản xuất - tương tự như cách config.ts của TypeScript quản lý biến môi trường trong mã của bạn.

Tự động hóa các kịch bản kiểm tra phức tạp
Các kịch bản tiền xử lý của Apidog cho phép điều phối kiểm tra tinh vi, cho phép bạn:

  • Kết nối các cuộc gọi API trong khi duy trì an toàn kiểu
  • Biến đổi dữ liệu phản hồi giữa các yêu cầu
  • Thực hiện logic kiểm tra có điều kiện
    Tất cả trong khi tận dụng các gợi ý kiểu giống như TypeScript và hoàn thành tự động.

Tài liệu tích hợp
Tài liệu API tự động được tạo ra giữ nguyên đồng bộ với các kiểm tra của bạn, loại bỏ sự trôi dạt tài liệu mà thường gặp trong các dự án API. Điều này đặc biệt có giá trị khi làm việc với TypeScript, vì nó đảm bảo hành vi thời gian chạy của bạn khớp với các định nghĩa kiểu của bạn.

Đối với các nhóm xây dựng TypeScript APIs, sự tích hợp chặt chẽ của Apidog giữa thiết kế, kiểm tra và tài liệu tạo ra một quy trình làm việc hiệu quả hơn so với phương pháp phân mảnh của Postman. Khả năng xác thực các phản hồi với các giao diện TypeScript và tạo các trường hợp kiểm tra an toàn kiểu khiến nó trở thành bạn đồng hành lý tưởng cho quy trình phát triển dựa trên kiểu của bạn.

button

Các yêu cầu trước

Để theo dõi hướng dẫn này, bạn sẽ cần:

  • Node.js (v14+ được khuyến nghị) đã được cài đặt
  • Một trình soạn thảo văn bản hoặc IDE (VS Code được khuyến nghị vì hỗ trợ TypeScript)
  • Kiến thức cơ bản về JavaScript và Node.js
  • Quen thuộc với các khái niệm HTTP và REST
  • Quyền truy cập dòng lệnh/terminal

Bước 1: Thiết lập và Khởi tạo Dự án

Hãy bắt đầu bằng cách tạo một môi trường dự án có cấu trúc:

# Tạo thư mục dự án
mkdir typescript-rest-api
cd typescript-rest-api

# Khởi tạo package.json
npm init -y

Hiểu về package.json

Tệp package.json là trung tâm của bất kỳ dự án Node.js nào. Nó chứa siêu dữ liệu về dự án của bạn và quản lý các phụ thuộc. Cờ -y tạo ra một cấu hình mặc định mà chúng tôi sẽ tùy chỉnh ngay.

Bước 2: Cài đặt các phụ thuộc

Hãy cài đặt các gói cần thiết cho REST API TypeScript của chúng tôi:

# Cài đặt TypeScript và các công cụ phát triển
npm install typescript ts-node nodemon @types/node --save-dev

# Cài đặt Express và định nghĩa kiểu
npm install express
npm install @types/express --save-dev

# Cài đặt middleware cho ghi nhật ký và phân tích
npm install morgan cors helmet
npm install @types/morgan @types/cors --save-dev

# Cài đặt dotenv để quản lý biến môi trường
npm install dotenv

Phân tích các phụ thuộc:

  • typescript: Trình biên dịch TypeScript
  • ts-node: Cho phép chạy các tệp TypeScript trực tiếp
  • nodemon: Giám sát thay đổi tệp và khởi động lại máy chủ
  • express: Framework web để xử lý các yêu cầu HTTP
  • morgan: Middleware ghi nhật ký yêu cầu HTTP
  • cors: Middleware xử lý Chia sẻ Tài nguyên Gốc Chéo
  • helmet: Middleware bảo mật thiết lập nhiều tiêu đề HTTP khác nhau
  • dotenv: Tải các biến môi trường từ tệp .env

Các gói định nghĩa kiểu (@types/*) cung cấp thông tin kiểu TypeScript cho các thư viện được viết bằng JavaScript.

Bước 3: Cấu hình TypeScript

Khởi tạo cấu hình TypeScript:

npx tsc --init

Điều này tạo ra một tệp tsconfig.json với các thiết lập mặc định. Hãy tùy chỉnh nó cho REST API của chúng tôi:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.test.ts"]
}

Giải thích Cấu hình:

  • target: Xác định phiên bản ECMAScript mục tiêu (ES6 mang lại các tính năng hiện đại)
  • module: Định nghĩa hệ thống module (CommonJS hoạt động tốt với Node.js)
  • outDir: Nơi các tệp JavaScript đã biên dịch sẽ được đặt
  • rootDir: Địa điểm của các tệp TypeScript nguồn
  • strict: Kích hoạt tất cả các tùy chọn kiểm tra kiểu nghiêm ngặt
  • esModuleInterop: Cho phép nhập khẩu mặc định từ các module không có xuất khẩu mặc định
  • sourceMap: Tạo ra các tệp sơ đồ nguồn cho việc gỡ lỗi
  • resolveJsonModule: Cho phép nhập khẩu các tệp JSON như các module

Bước 4: Tạo Cấu hình Môi trường

Tạo một tệp .env trong thư mục gốc để lưu trữ các biến môi trường:

NODE_ENV=development
PORT=3000
API_VERSION=v1

Bây giờ hãy tạo một tệp .env.example với cùng cấu trúc nhưng không có các giá trị nhạy cảm. Điều này sẽ phục vụ như một mẫu cho các nhà phát triển khác:

NODE_ENV=development
PORT=3000
API_VERSION=v1

Thêm .env vào tệp .gitignore của bạn để ngăn cản việc cam kết thông tin nhạy cảm:

node_modules
dist
.env

Bước 5: Cập nhật Các tập lệnh package.json

Cải thiện package.json của bạn với các tập lệnh hữu ích:

"main": "dist/server.js",
"scripts": {
  "start": "node dist/server.js",
  "dev": "nodemon src/server.ts",
  "build": "tsc",
  "lint": "eslint . --ext .ts",
  "test": "jest",
  "clean": "rimraf dist"
}

Các tập lệnh này cung cấp các lệnh cho phát triển, xây dựng sản xuất, lint, kiểm tra và dọn dẹp các tệp đã biên dịch.

Bước 6: Tạo Cấu trúc Dự án

Tạo một cấu trúc thư mục được tổ chức tốt:

typescript-rest-api/
├── src/
│   ├── config/        # Tệp cấu hình
│   │   └── index.ts   # Xuất khẩu cấu hình trung tâm
│   ├── controllers/   # Bộ xử lý tuyến đường
│   ├── middleware/    # Middleware tùy chỉnh
│   ├── models/        # Mô hình/dữ liệu
│   ├── routes/        # Định nghĩa tuyến đường
│   │   └── v1/        # Tuyến đường API phiên bản 1
│   ├── services/      # Logic nghiệp vụ
│   ├── utils/         # Chức năng tiện ích
│   ├── types/         # Định nghĩa kiểu
│   ├── app.ts         # Cài đặt ứng dụng Express
│   └── server.ts      # Điểm vào của máy chủ
├── .env               # Biến môi trường (gitignored)
├── .env.example       # Biến môi trường mẫu
├── .gitignore         # Tệp bỏ qua của Git
├── package.json       # Siêu dữ liệu và các phụ thuộc của dự án
└── tsconfig.json      # Cấu hình TypeScript

Cấu trúc này theo nguyên tắc phân tách các mối quan tâm, làm cho mã nguồn trở nên dễ bảo trì khi nó phát triển.

Bước 7: Thiết lập Cấu hình

Tạo module cấu hình trong src/config/index.ts:

import dotenv from 'dotenv';
import path from 'path';

// Tải các biến môi trường từ tệp .env
dotenv.config({ path: path.resolve(__dirname, '../../.env') });

const config = {
  env: process.env.NODE_ENV || 'development',
  port: parseInt(process.env.PORT || '3000', 10),
  apiVersion: process.env.API_VERSION || 'v1',
  // Thêm các biến cấu hình khác nếu cần
};

export default config;

Bước 8: Tạo Ứng dụng Express

Đầu tiên, hãy tạo src/app.ts để thiết lập ứng dụng Express của chúng ta:

import express, { Express } from 'express';
import morgan from 'morgan';
import helmet from 'helmet';
import cors from 'cors';

import config from './config';
import routes from './routes';
import { errorHandler, notFoundHandler } from './middleware/error.middleware';

const app: Express = express();

// Middleware
app.use(helmet()); // Tiêu đề bảo mật
app.use(cors()); // Bật CORS
app.use(morgan(config.env === 'development' ? 'dev' : 'combined')); // Ghi nhật ký yêu cầu
app.use(express.json()); // Phân tích các thân JSON
app.use(express.urlencoded({ extended: true })); // Phân tích các thân mã hóa URL

// Tuyến API
app.use(`/api/${config.apiVersion}`, routes);

// Điểm kiểm tra sức khỏe
app.get('/health', (_req, res) => {
  res.status(200).json({ status: 'OK', timestamp: new Date() });
});

// Xử lý Lỗi
app.use(notFoundHandler);
app.use(errorHandler);

export default app;

Tiếp theo, tạo src/server.ts như là điểm vào:

import app from './app';
import config from './config';

const startServer = () => {
  try {
    app.listen(config.port, () => {
      console.log(`Máy chủ đang chạy trên cổng ${config.port} ở chế độ ${config.env}`);
      console.log(`API có sẵn tại <http://localhost>:${config.port}/api/${config.apiVersion}`);
      console.log(`Kiểm tra sức khỏe có sẵn tại <http://localhost>:${config.port}/health`);
    });
  } catch (error) {
    console.error('Không thể khởi động máy chủ:', error);
    process.exit(1);
  }
};

// Xử lý các từ chối lời hứa không được xử lý
process.on('unhandledRejection', (reason, promise) => {
  console.error('Từ chối không được xử lý tại:', promise, 'lý do:', reason);
});

startServer();

Bước 9: Định nghĩa Mô hình và Kiểu Dữ liệu

Tạo các giao diện cơ bản cho dữ liệu của bạn trong src/models/book.model.ts:

export interface Book {
  id: number;
  title: string;
  author: string;
  publishYear?: number;
  genre?: string;
  isbn?: string;
  createdAt: Date;
  updatedAt: Date;
}

// DTO (Đối tượng Chuyển giao Dữ liệu) để tạo sách mới
export interface CreateBookDTO {
  title: string;
  author: string;
  publishYear?: number;
  genre?: string;
  isbn?: string;
}

// DTO để cập nhật sách hiện có (tất cả các trường đều là tùy chọn)
export interface UpdateBookDTO {
  title?: string;
  author?: string;
  publishYear?: number;
  genre?: string;
  isbn?: string;
}

// Cấu trúc phản hồi cho sự nhất quán API
export interface BookResponse {
  data: Book | Book[] | null;
  message?: string;
  success: boolean;
}

Bước 10: Tạo Lớp Dịch vụ

Lớp dịch vụ chứa logic nghiệp vụ, tách biệt khỏi các bộ điều khiển. Tạo src/services/book.service.ts:

import { Book, CreateBookDTO, UpdateBookDTO } from '../models/book.model';

// Cơ sở dữ liệu trong bộ nhớ (trong một ứng dụng thực, bạn sẽ sử dụng cơ sở dữ liệu)
let books: Book[] = [
  { id: 1, title: '1984', author: 'George Orwell', publishYear: 1949 },
  { id: 2, title: 'Để giết một chú chim nhại', author: 'Harper Lee', publishYear: 1960 }
];

export class BookService {
  async getAllBooks(): Promise {
    return books;
  }

  async getBookById(id: number): Promise {
    const book = books.find(b => b.id === id);
    return book || null;
  }

  async createBook(bookData: CreateBookDTO): Promise {
    const newId = books.length > 0 ? Math.max(...books.map(b => b.id)) + 1 : 1;

    const newBook: Book = {
      id: newId,
      ...bookData
    };

    books.push(newBook);
    return newBook;
  }

  async updateBook(id: number, bookData: UpdateBookDTO): Promise {
    const index = books.findIndex(b => b.id === id);

    if (index === -1) {
      return null;
    }

    books[index] = { ...books[index], ...bookData };
    return books[index];
  }

  async deleteBook(id: number): Promise {
    const initialLength = books.length;
    books = books.filter(b => b.id !== id);

    return books.length !== initialLength;
  }
}

export default new BookService();

Bước 11: Tái cấu trúc Các bộ điều khiển để Sử dụng Lớp Dịch vụ

Cập nhật src/controllers/book.controller.ts để sử dụng lớp dịch vụ mới của chúng ta:

import { Request, Response } from 'express';
import bookService from '../services/book.service';

export const getBooks = async (_req: Request, res: Response) => {
  try {
    const books = await bookService.getAllBooks();
    return res.status(200).json(books);
  } catch (error) {
    return res.status(500).json({ message: 'Lỗi khi lấy sách', error });
  }
};

export const getBookById = async (req: Request, res: Response) => {
  try {
    const id: number = parseInt(req.params.id);
    const book = await bookService.getBookById(id);

    if (!book) {
      return res.status(404).json({ message: 'Sách không được tìm thấy' });
    }

    return res.status(200).json(book);
  } catch (error) {
    return res.status(500).json({ message: 'Lỗi khi lấy sách', error });
  }
};

export const createBook = async (req: Request, res: Response) => {
  try {
    const bookData = req.body;

    // Xác thực yêu cầu
    if (!bookData.title || !bookData.author) {
      return res.status(400).json({ message: 'Tiêu đề và tác giả là bắt buộc' });
    }

    const newBook = await bookService.createBook(bookData);
    return res.status(201).json(newBook);
  } catch (error) {
    return res.status(500).json({ message: 'Lỗi khi tạo sách', error });
  }
};

export const updateBook = async (req: Request, res: Response) => {
  try {
    const id: number = parseInt(req.params.id);
    const bookData = req.body;
    const updatedBook = await bookService.updateBook(id, bookData);

    if (!updatedBook) {
      return res.status(404).json({ message: 'Sách không được tìm thấy' });
    }

    return res.status(200).json(updatedBook);
  } catch (error) {
    return res.status(500).json({ message: 'Lỗi khi cập nhật sách', error });
  }
};

export const deleteBook = async (req: Request, res: Response) => {
  try {
    const id: number = parseInt(req.params.id);
    const success = await bookService.deleteBook(id);

    if (!success) {
      return res.status(404).json({ message: 'Sách không được tìm thấy' });
    }

    return res.status(204).send();
  } catch (error) {
    return res.status(500).json({ message: 'Lỗi khi xóa sách', error });
  }
};

Bước 12: Thêm Xác thực Yêu cầu

Tạo một middleware xác thực trong src/middleware/validation.middleware.ts:

import { Request, Response, NextFunction } from 'express';

export interface ValidationSchema {
  [key: string]: {
    required?: boolean;
    type?: 'string' | 'number' | 'boolean';
    minLength?: number;
    maxLength?: number;
    min?: number;
    max?: number;
    custom?: (value: any) => boolean;
    message?: string;
  };
}

export const validateRequest = (schema: ValidationSchema) => {
  return (req: Request, res: Response, next: NextFunction) => {
    const errors: string[] = [];

    Object.entries(schema).forEach(([field, rules]) => {
      const value = req.body[field];

      // Kiểm tra các trường bắt buộc
      if (rules.required && (value === undefined || value === null || value === '')) {
        errors.push(rules.message || `Trường '${field}' là bắt buộc`);
        return;
      }

      // Bỏ qua xác thực thêm nếu trường không có và không bắt buộc
      if ((value === undefined || value === null) && !rules.required) {
        return;
      }

      // Xác thực kiểu
      if (rules.type && typeof value !== rules.type) {
        errors.push(rules.message || `Trường '${field}' phải là một ${rules.type}`);
      }

      // Xác thực chuỗi
      if (rules.type === 'string') {
        if (rules.minLength !== undefined && value.length < rules.minLength) {
          errors.push(rules.message || `Trường '${field}' phải có ít nhất ${rules.minLength} ký tự`);
        }

        if (rules.maxLength !== undefined && value.length > rules.maxLength) {
          errors.push(rules.message || `Trường '${field}' phải không vượt quá ${rules.maxLength} ký tự`);
        }
      }

      // Xác thực số
      if (rules.type === 'number') {
        if (rules.min !== undefined && value < rules.min) {
          errors.push(rules.message || `Trường '${field}' phải lớn hơn hoặc bằng ${rules.min}`);
        }

        if (rules.max !== undefined && value > rules.max) {
          errors.push(rules.message || `Trường '${field}' phải nhỏ hơn hoặc bằng ${rules.max}`);
        }
      }

      // Xác thực tùy chỉnh
      if (rules.custom && !rules.custom(value)) {
        errors.push(rules.message || `Trường '${field}' không vượt qua xác thực`);
      }
    });

    if (errors.length > 0) {
      return res.status(400).json({ errors });
    }

    next();
  };
};

Rồi tạo các lược đồ xác thực trong src/validators/book.validator.ts:

import { ValidationSchema } from '../middleware/validation.middleware';

export const createBookSchema: ValidationSchema = {
  title: {
    required: true,
    type: 'string',
    minLength: 1,
    maxLength: 100,
    message: 'Tiêu đề là bắt buộc và phải từ 1 đến 100 ký tự'
  },
  author: {
    required: true,
    type: 'string',
    minLength: 1,
    maxLength: 100,
    message: 'Tác giả là bắt buộc và phải từ 1 đến 100 ký tự'
  },
  publishYear: {
    type: 'number',
    min: 0,
    max: new Date().getFullYear(),
    message: `Năm xuất bản phải từ 0 đến ${new Date().getFullYear()}`
  }
};

export const updateBookSchema: ValidationSchema = {
  title: {
    type: 'string',
    minLength: 1,
    maxLength: 100,
    message: 'Tiêu đề phải từ 1 đến 100 ký tự'
  },
  author: {
    type: 'string',
    minLength: 1,
    maxLength: 100,
    message: 'Tác giả phải từ 1 đến 100 ký tự'
  },
  publishYear: {
    type: 'number',
    min: 0,
    max: new Date().getFullYear(),
    message: `Năm xuất bản phải từ 0 đến ${new Date().getFullYear()}`
  }
};

Bước 13: Cập nhật Tuyến đường để Bao gồm Xác thực

Hãy cập nhật src/routes/book.routes.ts để bao gồm middleware xác thực của chúng ta:

import { Router } from 'express';
import { getBooks, getBookById, createBook, updateBook, deleteBook } from '../controllers/book.controller';
import { validateRequest } from '../middleware/validation.middleware';
import { createBookSchema, updateBookSchema } from '../validators/book.validator';

const router = Router();

// GET /api/books - Lấy tất cả sách
router.get('/', getBooks);

// GET /api/books/:id - Lấy một sách cụ thể
router.get('/:id', getBookById);

// POST /api/books - Tạo một sách mới
router.post('/', validateRequest(createBookSchema), createBook);

// PUT /api/books/:id - Cập nhật một sách
router.put('/:id', validateRequest(updateBookSchema), updateBook);

// DELETE /api/books/:id - Xóa một sách
router.delete('/:id', deleteBook);

export default router;

Bước 14: Thêm Tài liệu API

Cài đặt Swagger cho tài liệu API:

npm install swagger-ui-express @types/swagger-ui-express swagger-jsdoc @types/swagger-jsdoc --save-dev

Tạo src/utils/swagger.ts:

import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'API REST TypeScript',
      version: '1.0.0',
      description: 'Một API REST đơn giản được xây dựng với Express và TypeScript',
    },
    servers: [
      {
        url: '<http://localhost:3000/api>',
      },
    ],
  },
  apis: ['./src/routes/*.ts'],
};

const specs = swaggerJsdoc(options);

export { specs, swaggerUi };

Thêm các chú thích JSDoc vào tệp tuyến đường của bạn để tạo tài liệu Swagger:

/**
 * @swagger
 * components:
 *   schemas:
 *     Book:
 *       type: object
 *       required:
 *         - title
 *         - author
 *       properties:
 *         id:
 *           type: number
 *           description: ID tự động được tạo ra của sách
 *         title:
 *           type: string
 *           description: Tiêu đề của sách
 *         author:
 *           type: string
 *           description: Tác giả của sách
 *         publishYear:
 *           type: number
 *           description: Năm sách được xuất bản
 */

/**
 * @swagger
 * tags:
 *   name: Books
 *   description: API quản lý sách
 */

/**
 * @swagger
 * /books:
 *   get:
 *     summary: Trả về danh sách tất cả các sách
 *     tags: [Books]
 *     responses:
 *       200:
 *         description: Danh sách các sách
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/Book'
 */

// Thêm các chú thích JSDoc tương tự cho các điểm cuối khác

Cập nhật server.ts để bao gồm Swagger:

import { specs, swaggerUi } from './utils/swagger';

// Thêm trước các tuyến đường của bạn
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));

Bước 15: Thêm Hỗ trợ CORS

Thêm middleware CORS để cho phép các yêu cầu gốc chéo:

npm install cors @types/cors --save-dev

Cập nhật server.ts:

import cors from 'cors';

// Thêm điều này trước các middleware khác
app.use(cors());

// Để kiểm soát nhiều hơn các tùy chọn CORS:
app.use(cors({
  origin: ['<http://localhost:3000>', '<https://yourdomain.com>'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

Bước 16: Thêm Cấu hình Môi trường

Tạo một tệp .env cho các biến môi trường:

PORT=3000
NODE_ENV=development
# Thêm các biến cấu hình khác ở đây, như chuỗi kết nối cơ sở dữ liệu

Cài đặt dotenv để tải các biến môi trường:

npm install dotenv --save

Tạo src/config/env.config.ts:

import dotenv from 'dotenv';
import path from 'path';

// Tải các biến môi trường từ tệp .env
dotenv.config({ path: path.resolve(__dirname, '../../.env') });

export default {
  port: parseInt(process.env.PORT || '3000'),
  nodeEnv: process.env.NODE_ENV || 'development',
  // Thêm các biến môi trường khác ở đây
};

Cập nhật server.ts để sử dụng cấu hình này:

import config from './config/env.config';

const PORT: number = config.port;

Bước 17: Tích hợp Cuối cùng và Kiểm tra

Với tất cả các thành phần đã được lắp đặt, cấu trúc tệp của bạn sẽ trông như sau:

typescript-rest-api/
├── src/
│   ├── config/
│   │   └── env.config.ts
│   ├── controllers/
│   │   └── book.controller.ts
│   ├── middleware/
│   │   ├── error.middleware.ts
│   │   └── validation.middleware.ts
│   ├── models/
│   │   └── book.model.ts
│   ├── routes/
│   │   ├── book.routes.ts
│   │   └── index.ts
│   ├── services/
│   │   └── book.service.ts
│   ├── utils/
│   │   └── swagger.ts
│   ├── validators/
│   │   └── book.validator.ts
│   └── server.ts
├── .env
├── tsconfig.json
└── package.json

Chạy API của bạn:

npm run dev

Kiểm tra các điểm cuối bằng cách sử dụng một công cụ như Postman hoặc truy cập tài liệu Swagger tại http://localhost:3000/api-docs.

Kết luận

Giờ đây, bạn đã hoàn thành việc xây dựng một API REST vững chắc với TypeScript. API này thể hiện các thực tiễn tốt nhất bao gồm:

  1. An toàn kiểu thông qua các giao diện và mô hình TypeScript
  2. Kiến trúc sạch với sự phân tách các mối quan tâm (bộ điều khiển, dịch vụ, mô hình)
  3. Xác thực đầu vào để đảm bảo tính toàn vẹn của dữ liệu
  4. Xử lý lỗi để có phản hồi API vững chắc
  5. Tài liệu API với Swagger để cải thiện trải nghiệm phát triển
  6. Hỗ trợ CORS cho các yêu cầu gốc chéo
  7. Cấu hình môi trường cho các tình huống triển khai khác nhau

Nền tảng này cung cấp một điểm khởi đầu tuyệt vời cho các ứng dụng phức tạp hơn. Khi API của bạn phát triển, hãy xem xét việc triển khai:

  • Tích hợp cơ sở dữ liệu với TypeORM, Prisma hoặc Mongoose
  • Middleware xác thực với JWT hoặc OAuth
  • Giới hạn tốc độ để ngăn chặn lạm dụng
  • Ghi nhật ký với Winston hoặc các thư viện ghi nhật ký khác
  • Kiểm tra với Jest, Mocha hoặc SuperTest
  • Container hóa với Docker để triển khai nhất quán

Sự kết hợp giữa kiểu tĩnh của TypeScript và kiến trúc linh hoạt của Express dẫn đến một API có thể duy trì và mở rộng theo nhu cầu của ứng dụng của bạn.