Xây Dựng API Đa Giao Thức Với REST, GraphQL và gRPC Như Thế Nào?

Ashley Innocent

Ashley Innocent

13 tháng 3 2026

Xây Dựng API Đa Giao Thức Với REST, GraphQL và gRPC Như Thế Nào?

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

TÓM TẮT

Xây dựng các API đa giao thức bằng cách tách biệt logic nghiệp vụ khỏi các lớp giao thức. Tạo một lớp miền dùng chung, sau đó thêm các bộ điều hợp REST, GraphQL và gRPC lên trên. Modern PetstoreAPI minh họa kiến trúc này với các mô hình dữ liệu nhất quán trên cả ba giao thức.

Giới thiệu

API của bạn phục vụ các ứng dụng web, ứng dụng di động và các dịch vụ vi mô nội bộ. Các ứng dụng web muốn REST vì sự đơn giản. Các ứng dụng di động muốn GraphQL để giảm truyền dữ liệu. Các dịch vụ vi mô muốn gRPC để đạt hiệu suất. Bạn có xây dựng ba API riêng biệt không?

Không. Bạn xây dựng một API với ba lớp giao thức. Logic nghiệp vụ vẫn giữ nguyên. Chỉ có các bộ điều hợp giao thức thay đổi. Đây là kiến trúc API đa giao thức.

Modern PetstoreAPI triển khai REST, GraphQL và gRPC từ một lõi dùng chung. Cùng một logic cửa hàng thú cưng phục vụ cả ba giao thức với hành vi nhất quán.

💡
Nếu bạn đang xây dựng hoặc kiểm thử các API đa giao thức, Apidog hỗ trợ kiểm thử REST, GraphQL và gRPC trong một công cụ duy nhất. Bạn có thể xác minh rằng tất cả các giao thức trả về dữ liệu nhất quán và kiểm thử các quy trình làm việc đa giao thức.
Tải ứng dụng

Trong hướng dẫn này, bạn sẽ tìm hiểu cách kiến trúc các API đa giao thức, triển khai các lớp giao thức và đảm bảo tính nhất quán giữa các giao thức bằng cách sử dụng Modern PetstoreAPI làm tài liệu tham khảo.

Kiến trúc đa giao thức

Các API đa giao thức tách biệt các mối quan tâm thành các lớp.

Kiến trúc phân lớp

┌─────────────────────────────────────────┐
│  Lớp Giao thức (REST/GraphQL/gRPC)     │
├─────────────────────────────────────────┤
│  Lớp Ứng dụng (Trường hợp sử dụng)          │
├─────────────────────────────────────────┤
│  Lớp Miền (Logic nghiệp vụ)           │
├─────────────────────────────────────────┤
│  Lớp Dữ liệu (Cơ sở dữ liệu, Bộ đệm)           │
└─────────────────────────────────────────┘

Lớp Giao thức: Xử lý các yêu cầu HTTP, truy vấn GraphQL, cuộc gọi gRPC
Lớp Ứng dụng: Điều phối các trường hợp sử dụng (tạo thú cưng, đặt hàng)
Lớp Miền: Các quy tắc nghiệp vụ (xác thực, tính toán)
Lớp Dữ liệu: Lưu trữ bền vững (cơ sở dữ liệu, bộ đệm)

Các nguyên tắc chính

1. Lõi không phụ thuộc giao thức

Logic nghiệp vụ không biết về HTTP, GraphQL hay gRPC. Nó hoạt động với các đối tượng miền.

2. Bộ điều hợp giao thức mỏng

Các lớp giao thức chuyển đổi giữa các định dạng giao thức và đối tượng miền. Chúng không chứa logic nghiệp vụ.

3. Các mô hình dữ liệu dùng chung

Tất cả các giao thức sử dụng cùng một mô hình miền nội bộ, đảm bảo tính nhất quán.

4. Triển khai độc lập

Mỗi giao thức có thể được triển khai riêng biệt nếu cần.

Lớp miền dùng chung

Lớp miền chứa logic nghiệp vụ được chia sẻ giữa tất cả các giao thức.

Mô hình miền

// Mô hình miền (không phụ thuộc giao thức)
class Pet {
  id: string;
  name: string;
  species: Species;
  status: PetStatus;
  price: number;

  constructor(data: PetData) {
    this.validate(data);
    Object.assign(this, data);
  }

  validate(data: PetData): void {
    if (!data.name || data.name.length < 2) {
      throw new ValidationError('Name must be at least 2 characters');
    }
    if (data.price < 0) {
      throw new ValidationError('Price cannot be negative');
    }
  }

  adopt(userId: string): Order {
    if (this.status !== PetStatus.AVAILABLE) {
      throw new BusinessError('Pet is not available for adoption');
    }
    this.status = PetStatus.ADOPTED;
    return new Order({
      petId: this.id,
      userId,
      total: this.price
    });
  }
}

Mô hình này hoạt động cho REST, GraphQL và gRPC. Các quy tắc xác thực và nghiệp vụ là như nhau.

Các trường hợp sử dụng

// Trường hợp sử dụng (không phụ thuộc giao thức)
class AdoptPetUseCase {
  constructor(
    private petRepository: PetRepository,
    private orderRepository: OrderRepository
  ) {}

  async execute(petId: string, userId: string): Promise<Order> {
    const pet = await this.petRepository.findById(petId);
    if (!pet) {
      throw new NotFoundError('Pet not found');
    }

    const order = pet.adopt(userId);
    await this.petRepository.save(pet);
    await this.orderRepository.save(order);

    return order;
  }
}

Trường hợp sử dụng này hoạt động bất kể nó được gọi từ REST, GraphQL hay gRPC.

Lớp giao thức REST

Lớp REST chuyển đổi các yêu cầu HTTP thành các hoạt động miền.

Bộ điều khiển REST

// Bộ điều hợp REST
class PetsController {
  constructor(private adoptPetUseCase: AdoptPetUseCase) {}

  async adoptPet(req: Request, res: Response): Promise<void> {
    try {
      const { petId } = req.params;
      const { userId } = req.body;

      const order = await this.adoptPetUseCase.execute(petId, userId);

      res.status(201).json({
        id: order.id,
        petId: order.petId,
        userId: order.userId,
        total: order.total,
        status: order.status
      });
    } catch (error) {
      this.handleError(error, res);
    }
  }

  private handleError(error: Error, res: Response): void {
    if (error instanceof NotFoundError) {
      res.status(404).json({
        type: 'https://petstoreapi.com/errors/not-found',
        title: 'Not Found',
        status: 404,
        detail: error.message
      });
    } else if (error instanceof ValidationError) {
      res.status(400).json({
        type: 'https://petstoreapi.com/errors/validation-error',
        title: 'Validation Error',
        status: 400,
        detail: error.message
      });
    } else {
      res.status(500).json({
        type: 'https://petstoreapi.com/errors/internal-error',
        title: 'Internal Server Error',
        status: 500
      });
    }
  }
}

Các tuyến đường REST

app.post('/v1/pets/:petId/adopt', (req, res) =>
  petsController.adoptPet(req, res)
);

Các điểm cuối REST của Modern PetstoreAPI

Lớp giao thức GraphQL

Lớp GraphQL chuyển đổi các truy vấn GraphQL thành các hoạt động miền.

Schema GraphQL

type Pet {
  id: ID!
  name: String!
  species: Species!
  status: PetStatus!
  price: Float!
}

type Order {
  id: ID!
  petId: ID!
  userId: ID!
  total: Float!
  status: OrderStatus!
}

type Mutation {
  adoptPet(petId: ID!, userId: ID!): Order!
}

Bộ phân giải GraphQL

// Bộ điều hợp GraphQL
const resolvers = {
  Mutation: {
    adoptPet: async (
      _parent: any,
      args: { petId: string; userId: string },
      context: Context
    ): Promise<Order> => {
      try {
        return await context.adoptPetUseCase.execute(
          args.petId,
          args.userId
        );
      } catch (error) {
        throw new GraphQLError(error.message, {
          extensions: {
            code: error instanceof NotFoundError ? 'NOT_FOUND' :
                  error instanceof ValidationError ? 'BAD_USER_INPUT' :
                  'INTERNAL_SERVER_ERROR'
          }
        });
      }
    }
  }
};

Yêu cầu GraphQL

mutation {
  adoptPet(
    petId: "019b4132-70aa-764f-b315-e2803d882a24"
    userId: "user-123"
  ) {
    id
    total
    status
  }
}

Schema GraphQL của Modern PetstoreAPI

Lớp giao thức gRPC

Lớp gRPC chuyển đổi các tin nhắn Protocol Buffer thành các hoạt động miền.

Định nghĩa Protocol Buffer

syntax = "proto3";

package petstore.v1;

service PetService {
  rpc AdoptPet(AdoptPetRequest) returns (AdoptPetResponse);
}

message AdoptPetRequest {
  string pet_id = 1;
  string user_id = 2;
}

message AdoptPetResponse {
  string order_id = 1;
  string pet_id = 2;
  string user_id = 3;
  double total = 4;
  string status = 5;
}

Triển khai dịch vụ gRPC

// Bộ điều hợp gRPC
class PetServiceImpl implements IPetService {
  constructor(private adoptPetUseCase: AdoptPetUseCase) {}

  async adoptPet(
    call: ServerUnaryCall<AdoptPetRequest, AdoptPetResponse>,
    callback: sendUnaryData<AdoptPetResponse>
  ): Promise<void> {
    try {
      const { petId, userId } = call.request;

      const order = await this.adoptPetUseCase.execute(petId, userId);

      callback(null, {
        orderId: order.id,
        petId: order.petId,
        userId: order.userId,
        total: order.total,
        status: order.status
      });
    } catch (error) {
      callback({
        code: error instanceof NotFoundError ? status.NOT_FOUND :
              error instanceof ValidationError ? status.INVALID_ARGUMENT :
              status.INTERNAL,
        message: error.message
      });
    }
  }
}

Dịch vụ gRPC của Modern PetstoreAPI

Cách Modern PetstoreAPI triển khai đa giao thức

Modern PetstoreAPI minh họa kiến trúc đa giao thức bằng các ví dụ thực tế.

Tổng quan kiến trúc

Modern PetstoreAPI
├── Lớp Miền
│   ├── Pet (thực thể)
│   ├── Order (thực thể)
│   └── Các trường hợp sử dụng
│       ├── TạoPet
│       ├── Nhận nuôiPet
│       └── ĐặtHàng
├── Lớp REST
│   ├── /v1/pets
│   ├── /v1/orders
│   └── Thông số kỹ thuật OpenAPI 3.2
├── Lớp GraphQL
│   ├── Bộ phân giải truy vấn
│   ├── Bộ phân giải thay đổi
│   └── Schema GraphQL
└── Lớp gRPC
    ├── Dịch vụ Pet
    ├── Dịch vụ Order
    └── Các định nghĩa .proto

Các mô hình dữ liệu nhất quán

Tất cả các giao thức trả về cùng một dữ liệu:

REST:

{
  "id": "019b4132-70aa-764f-b315-e2803d882a24",
  "name": "Fluffy",
  "species": "CAT"
}

GraphQL:

{
  "data": {
    "pet": {
      "id": "019b4132-70aa-764f-b315-e2803d882a24",
      "name": "Fluffy",
      "species": "CAT"
    }
  }
}

gRPC:

{
  pet_id: "019b4132-70aa-764f-b315-e2803d882a24"
  name: "Fluffy"
  species: CAT
}

Cùng dữ liệu, khác định dạng.

Xác thực dùng chung

Xác thực diễn ra trong lớp miền, vì vậy tất cả các giao thức đều áp dụng cùng một quy tắc:

// Xác thực miền (dùng chung)
if (name.length < 2) {
  throw new ValidationError('Name must be at least 2 characters');
}

REST trả về:

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "status": 400,
  "detail": "Name must be at least 2 characters"
}

GraphQL trả về:

{
  "errors": [{
    "message": "Name must be at least 2 characters",
    "extensions": {"code": "BAD_USER_INPUT"}
  }]
}

gRPC trả về:

code: INVALID_ARGUMENT
message: "Name must be at least 2 characters"

Cùng xác thực, định dạng lỗi đặc trưng cho từng giao thức.

Kiểm thử API đa giao thức với Apidog

Apidog hỗ trợ kiểm thử cả ba giao thức.

Kiểm thử điểm cuối REST

POST https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/adopt
Content-Type: application/json

{
  "userId": "user-123"
}

Kiểm thử thay đổi GraphQL

mutation {
  adoptPet(
    petId: "019b4132-70aa-764f-b315-e2803d882a24"
    userId: "user-123"
  ) {
    id
    total
  }
}

Kiểm thử dịch vụ gRPC

grpc.petstore.v1.PetService/AdoptPet
{
  "pet_id": "019b4132-70aa-764f-b315-e2803d882a24",
  "user_id": "user-123"
}

Xác minh tính nhất quán

Kiểm thử rằng cả ba giao thức trả về cùng một dữ liệu:

  1. Gọi điểm cuối REST
  2. Gọi thay đổi GraphQL
  3. Gọi dịch vụ gRPC
  4. So sánh kết quả

Apidog có thể tự động hóa việc kiểm thử đa giao thức này.

Các chiến lược triển khai

Chiến lược 1: Dịch vụ đơn lẻ

Triển khai tất cả các giao thức trong một dịch vụ:

┌─────────────────────────┐
│  Dịch vụ PetstoreAPI    │
│  ├── REST (cổng 8080)   │
│  ├── GraphQL (cổng 8081)│
│  └── gRPC (cổng 50051)  │
└─────────────────────────┘

Ưu điểm: Triển khai đơn giản, tài nguyên dùng chung
Nhược điểm: Tất cả các giao thức mở rộng cùng nhau

Chiến lược 2: Các dịch vụ riêng biệt

Triển khai mỗi giao thức riêng biệt:

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ Dịch vụ REST │  │ Dịch vụ GraphQL│  │ Dịch vụ gRPC │
│ (cổng 8080)  │  │ (cổng 8081)  │  │ (cổng 50051) │
└──────────────┘  └──────────────┘  └──────────────┘
       │                 │                  │
       └─────────────────┴──────────────────┘
                         │
                  ┌──────────────┐
                  │ Lõi dùng chung │
                  │   Thư viện   │
                  └──────────────┘

Ưu điểm: Mở rộng độc lập, cách ly giao thức
Nhược điểm: Triển khai phức tạp hơn

Chiến lược 3: Cổng API

Sử dụng cổng API để định tuyến đến các backend cụ thể theo giao thức:

                ┌─────────────┐
                │ Cổng API    │
                └─────────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Backend REST │ │ GraphQL  │ │ Backend gRPC│
└──────────────┘ └──────────┘ └─────────────┘

Ưu điểm: Định tuyến tập trung, giới hạn tốc độ, xác thực
Nhược điểm: Độ trễ bổ sung, sự phức tạp của cổng

Modern PetstoreAPI sử dụng Chiến lược 1 để đơn giản hóa.

Kết luận

Các API đa giao thức mang lại sự linh hoạt cho khách hàng mà không cần trùng lặp logic nghiệp vụ. Bằng cách tách biệt các lớp giao thức khỏi logic miền, bạn có thể hỗ trợ REST, GraphQL và gRPC từ một lõi dùng chung.

Modern PetstoreAPI minh họa kiến trúc này với các mô hình dữ liệu nhất quán, xác thực dùng chung và các bộ điều hợp đặc trưng cho từng giao thức. Cho dù khách hàng sử dụng REST để đơn giản, GraphQL để linh hoạt hay gRPC để đạt hiệu suất, họ đều truy cập cùng một cửa hàng thú cưng với cùng các quy tắc nghiệp vụ.

Kiểm thử các API đa giao thức của bạn với Apidog để đảm bảo tính nhất quán giữa các giao thức. Khám phá Modern PetstoreAPI để xem kiến trúc đa giao thức trong thực tế.

Tải ứng dụng

Câu hỏi thường gặp

Tôi có cần hỗ trợ cả ba giao thức không?

Không. Bắt đầu với REST cho các API công cộng. Thêm GraphQL nếu khách hàng cần tìm nạp dữ liệu linh hoạt. Thêm gRPC cho các dịch vụ vi mô nội bộ. Chỉ thêm các giao thức khi bạn có một trường hợp sử dụng rõ ràng.

Làm cách nào để giữ các giao thức nhất quán?

Chia sẻ logic nghiệp vụ trong lớp miền. Các bộ điều hợp giao thức chỉ nên chuyển đổi định dạng, không chứa các quy tắc nghiệp vụ. Kiểm thử tất cả các giao thức để xác minh chúng trả về cùng một dữ liệu.

Tôi có thể quản lý phiên bản các giao thức một cách độc lập không?

Có. REST có thể ở v2 trong khi GraphQL ở v1. Nhưng điều này tạo ra sự phức tạp. Cố gắng giữ các giao thức đồng bộ nếu có thể.

Làm cách nào để xử lý xác thực trên các giao thức?

Sử dụng cùng một cơ chế xác thực trong tất cả các giao thức. REST sử dụng mã thông báo Bearer trong tiêu đề. GraphQL sử dụng cùng mã thông báo. gRPC sử dụng siêu dữ liệu. Lớp miền xác thực mã thông báo theo cùng một cách.

Còn về WebSocket và SSE thì sao?

WebSocket và SSE là các giao thức truyền tải, không phải giao thức API. Bạn có thể thêm chúng cùng với REST/GraphQL/gRPC để cập nhật theo thời gian thực. Modern PetstoreAPI bao gồm cả hai.

Làm cách nào để tài liệu hóa các API đa giao thức?

Sử dụng OpenAPI cho REST, schema GraphQL cho GraphQL và các tệp .proto cho gRPC. Modern PetstoreAPI cung cấp cả ba tại https://docs.petstoreapi.com/

Tôi có thể sử dụng các cơ sở dữ liệu khác nhau cho các giao thức khác nhau không?

Có, nhưng nó tạo ra thách thức về tính nhất quán. Tốt hơn hết là sử dụng cùng một lớp dữ liệu cho tất cả các giao thức. Hãy để các bộ điều hợp giao thức xử lý sự khác biệt về định dạng.

Làm cách nào để kiểm thử các API đa giao thức?

Sử dụng Apidog để kiểm thử tất cả các giao thức trong một công cụ duy nhất. Tạo các bộ kiểm thử để xác minh tính nhất quán giữa các giao thức. Kiểm thử rằng REST, GraphQL và gRPC trả về cùng một dữ liệu cho cùng các hoạt động.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API