วิธีสร้าง API แบบ Multi-Protocol ด้วย REST, GraphQL และ gRPC

Ashley Innocent

Ashley Innocent

13 March 2026

วิธีสร้าง API แบบ Multi-Protocol ด้วย REST, GraphQL และ gRPC

Apidog สำหรับองค์กร

ติดตั้งภายในองค์กร

SSO & RBAC

รองรับ SOC 2

สำรวจ Apidog Enterprise

สรุปสาระสำคัญ

สร้าง API แบบหลายโปรโตคอลโดยการแยกตรรกะทางธุรกิจออกจากชั้นโปรโตคอล สร้างชั้นโดเมนที่ใช้ร่วมกัน จากนั้นเพิ่มอะแดปเตอร์ REST, GraphQL และ gRPC เข้าไปด้านบน Modern PetstoreAPI แสดงให้เห็นสถาปัตยกรรมนี้ด้วยโมเดลข้อมูลที่สอดคล้องกันในทั้งสามโปรโตคอล

บทนำ

API ของคุณให้บริการแก่ไคลเอนต์เว็บ แอปบนมือถือ และไมโครเซอร์วิสภายใน ไคลเอนต์เว็บต้องการ REST เพื่อความเรียบง่าย แอปบนมือถือต้องการ GraphQL เพื่อลดการถ่ายโอนข้อมูล ไมโครเซอร์วิสต้องการ gRPC เพื่อประสิทธิภาพ คุณจะสร้าง API แยกกันสามตัวหรือไม่?

ไม่ คุณสร้าง API เดียวที่มีสามชั้นโปรโตคอล ตรรกะทางธุรกิจยังคงเหมือนเดิม มีเพียงอะแดปเตอร์โปรโตคอลเท่านั้นที่เปลี่ยนไป นี่คือสถาปัตยกรรม API แบบหลายโปรโตคอล

Modern PetstoreAPI ใช้ REST, GraphQL และ gRPC จากแกนหลักที่ใช้ร่วมกัน ตรรกะของร้านขายสัตว์เลี้ยงแบบเดียวกันนี้ให้บริการแก่ทั้งสามโปรโตคอลด้วยพฤติกรรมที่สอดคล้องกัน

💡
หากคุณกำลังสร้างหรือทดสอบ API แบบหลายโปรโตคอล Apidog รองรับการทดสอบ REST, GraphQL และ gRPC ในเครื่องมือเดียว คุณสามารถยืนยันได้ว่าโปรโตคอลทั้งหมดส่งคืนข้อมูลที่สอดคล้องกัน และทดสอบเวิร์กโฟลว์ข้ามโปรโตคอล
ปุ่ม

ในคู่มือนี้ คุณจะได้เรียนรู้วิธีการออกแบบสถาปัตยกรรม API แบบหลายโปรโตคอล การนำชั้นโปรโตคอลไปใช้ และการรับรองความสอดคล้องกันในโปรโตคอลต่างๆ โดยใช้ Modern PetstoreAPI เป็นข้อมูลอ้างอิง

สถาปัตยกรรมหลายโปรโตคอล

API แบบหลายโปรโตคอลแยกความรับผิดชอบออกเป็นชั้นๆ

สถาปัตยกรรมแบบแบ่งชั้น

┌─────────────────────────────────────────┐
│  Protocol Layer (REST/GraphQL/gRPC)     │
├─────────────────────────────────────────┤
│  Application Layer (Use Cases)          │
├─────────────────────────────────────────┤
│  Domain Layer (Business Logic)          │
├─────────────────────────────────────────┤
│  Data Layer (Database, Cache)           │
└─────────────────────────────────────────┘

ชั้นโปรโตคอล: จัดการการเรียกใช้ HTTP, GraphQL queries, gRPC ชั้นแอปพลิเคชัน: จัดการกรณีการใช้งาน (สร้างสัตว์เลี้ยง, สั่งซื้อ) ชั้นโดเมน: กฎทางธุรกิจ (การตรวจสอบความถูกต้อง, การคำนวณ) ชั้นข้อมูล: การคงอยู่ของข้อมูล (ฐานข้อมูล, แคช)

หลักการสำคัญ

1. แกนหลักที่ไม่ขึ้นกับโปรโตคอล

ตรรกะทางธุรกิจไม่ทราบเกี่ยวกับ HTTP, GraphQL หรือ gRPC แต่ทำงานร่วมกับออบเจกต์โดเมน

2. อะแดปเตอร์โปรโตคอลแบบบาง

ชั้นโปรโตคอลจะแปลระหว่างรูปแบบโปรโตคอลและออบเจกต์โดเมน โดยไม่มีตรรกะทางธุรกิจ

3. โมเดลข้อมูลที่ใช้ร่วมกัน

โปรโตคอลทั้งหมดใช้โมเดลโดเมนเดียวกันภายใน เพื่อให้มั่นใจถึงความสอดคล้องกัน

4. การปรับใช้ที่เป็นอิสระ

แต่ละโปรโตคอลสามารถปรับใช้แยกกันได้หากจำเป็น

ชั้นโดเมนที่ใช้ร่วมกัน

ชั้นโดเมนประกอบด้วยตรรกะทางธุรกิจที่ใช้ร่วมกันในทุกโปรโตคอล

โมเดลโดเมน

// โมเดลโดเมน (ไม่ขึ้นกับโปรโตคอล)
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
    });
  }
}

โมเดลนี้ใช้ได้กับ REST, GraphQL และ gRPC กฎการตรวจสอบความถูกต้องและกฎทางธุรกิจจะเหมือนกัน

กรณีการใช้งาน

// กรณีการใช้งาน (ไม่ขึ้นกับโปรโตคอล)
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;
  }
}

กรณีการใช้งานนี้ทำงานได้ไม่ว่าจะเป็นการเรียกจาก REST, GraphQL หรือ gRPC

ชั้นโปรโตคอล REST

ชั้น REST จะแปลคำขอ HTTP ไปเป็นการดำเนินการของโดเมน

REST Controller

// อะแดปเตอร์ 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
      });
    }
  }
}

เส้นทาง REST

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

จุดเชื่อมต่อ REST ของ Modern PetstoreAPI

ชั้นโปรโตคอล GraphQL

ชั้น GraphQL จะแปล GraphQL queries ไปเป็นการดำเนินการของโดเมน

GraphQL Schema

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!
}

GraphQL Resolver

// อะแดปเตอร์ 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'
          }
        });
      }
    }
  }
};

คำขอ GraphQL

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

GraphQL schema ของ Modern PetstoreAPI

ชั้นโปรโตคอล gRPC

ชั้น gRPC จะแปลข้อความ Protocol Buffer ไปเป็นการดำเนินการของโดเมน

คำจำกัดความ 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;
}

การนำบริการ gRPC ไปใช้งาน

// อะแดปเตอร์ 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
      });
    }
  }
}

บริการ gRPC ของ Modern PetstoreAPI

Modern PetstoreAPI ใช้หลายโปรโตคอลอย่างไร

Modern PetstoreAPI แสดงให้เห็นสถาปัตยกรรมแบบหลายโปรโตคอลพร้อมตัวอย่างจริง

ภาพรวมสถาปัตยกรรม

Modern PetstoreAPI
├── ชั้นโดเมน
│   ├── Pet (เอนทิตี)
│   ├── Order (เอนทิตี)
│   └── กรณีการใช้งาน
│       ├── สร้างสัตว์เลี้ยง
│       ├── รับเลี้ยงสัตว์เลี้ยง
│       └── สั่งซื้อ
├── ชั้น REST
│   ├── /v1/pets
│   ├── /v1/orders
│   └── ข้อมูลจำเพาะ OpenAPI 3.2
├── ชั้น GraphQL
│   ├── ตัวแก้ไขการสืบค้น
│   ├── ตัวแก้ไขการเปลี่ยนแปลง
│   └── GraphQL schema
└── ชั้น gRPC
    ├── PetService
    ├── OrderService
    └── คำจำกัดความ .proto

โมเดลข้อมูลที่สอดคล้องกัน

โปรโตคอลทั้งหมดส่งคืนข้อมูลเดียวกัน:

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
}

ข้อมูลเดียวกัน รูปแบบต่างกัน

การตรวจสอบความถูกต้องที่ใช้ร่วมกัน

การตรวจสอบความถูกต้องเกิดขึ้นในชั้นโดเมน ดังนั้นโปรโตคอลทั้งหมดจึงบังคับใช้กฎเดียวกัน:

// การตรวจสอบความถูกต้องของโดเมน (ใช้ร่วมกัน)
if (name.length < 2) {
  throw new ValidationError('Name must be at least 2 characters');
}

REST ส่งคืน:

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

GraphQL ส่งคืน:

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

gRPC ส่งคืน:

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

การตรวจสอบความถูกต้องเดียวกัน รูปแบบข้อผิดพลาดเฉพาะโปรโตคอล

การทดสอบ API แบบหลายโปรโตคอลด้วย Apidog

Apidog รองรับการทดสอบทั้งสามโปรโตคอล

ทดสอบจุดเชื่อมต่อ REST

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

{
  "userId": "user-123"
}

ทดสอบ GraphQL Mutation

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

ทดสอบบริการ gRPC

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

ตรวจสอบความสอดคล้องกัน

ทดสอบว่าทั้งสามโปรโตคอลส่งคืนข้อมูลเดียวกัน:

  1. เรียกใช้จุดเชื่อมต่อ REST
  2. เรียกใช้ GraphQL mutation
  3. เรียกใช้บริการ gRPC
  4. เปรียบเทียบผลลัพธ์

Apidog สามารถทำการทดสอบข้ามโปรโตคอลนี้ได้โดยอัตโนมัติ

กลยุทธ์การปรับใช้

กลยุทธ์ที่ 1: บริการเดียว

ปรับใช้โปรโตคอลทั้งหมดในบริการเดียว:

┌─────────────────────────┐
│  บริการ PetstoreAPI    │
│  ├── REST (พอร์ต 8080)   │
│  ├── GraphQL (พอร์ต 8081)│
│  └── gRPC (พอร์ต 50051)  │
└─────────────────────────┘

ข้อดี: การปรับใช้ง่าย, ทรัพยากรที่ใช้ร่วมกัน ข้อเสีย: ทุกโปรโตคอลปรับขนาดพร้อมกัน

กลยุทธ์ที่ 2: บริการแยกกัน

ปรับใช้แต่ละโปรโตคอลแยกกัน:

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ บริการ REST  │  │ บริการ GraphQL│  │ บริการ gRPC  │
│ (พอร์ต 8080)  │  │ (พอร์ต 8081)  │  │ (พอร์ต 50051) │
└──────────────┘  └──────────────┘  └──────────────┘
       │                 │                  │
       └─────────────────┴──────────────────┘
                         │
                  ┌──────────────┐
                  │ แกนหลักที่ใช้ร่วมกัน │
                  │   ไลบรารี    │
                  └──────────────┘

ข้อดี: การปรับขนาดที่เป็นอิสระ, การแยกโปรโตคอล ข้อเสีย: การปรับใช้ซับซ้อนยิ่งขึ้น

กลยุทธ์ที่ 3: API Gateway

ใช้ API gateway เพื่อกำหนดเส้นทางไปยังแบ็คเอนด์เฉพาะโปรโตคอล:

                ┌─────────────┐
                │ API Gateway │
                └─────────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ แบ็คเอนด์ REST │ │ GraphQL  │ │ แบ็คเอนด์ gRPC│
└──────────────┘ └──────────┘ └─────────────┘

ข้อดี: การกำหนดเส้นทางแบบรวมศูนย์, การจำกัดอัตรา, การรับรองความถูกต้อง ข้อเสีย: ความหน่วงเพิ่มเติม, ความซับซ้อนของเกตเวย์

Modern PetstoreAPI ใช้กลยุทธ์ที่ 1 เพื่อความเรียบง่าย

บทสรุป

API แบบหลายโปรโตคอลช่วยให้ลูกค้ามีความยืดหยุ่นโดยไม่ต้องทำซ้ำตรรกะทางธุรกิจ ด้วยการแยกชั้นโปรโตคอลออกจากตรรกะโดเมน คุณสามารถรองรับ REST, GraphQL และ gRPC ได้จากแกนหลักที่ใช้ร่วมกัน

Modern PetstoreAPI แสดงให้เห็นสถาปัตยกรรมนี้ด้วยโมเดลข้อมูลที่สอดคล้องกัน, การตรวจสอบความถูกต้องที่ใช้ร่วมกัน และอะแดปเตอร์เฉพาะโปรโตคอล ไม่ว่าลูกค้าจะใช้ REST เพื่อความเรียบง่าย, GraphQL เพื่อความยืดหยุ่น หรือ gRPC เพื่อประสิทธิภาพ พวกเขาก็เข้าถึงร้านขายสัตว์เลี้ยงเดียวกันด้วยกฎทางธุรกิจเดียวกัน

ทดสอบ API แบบหลายโปรโตคอลของคุณด้วย Apidog เพื่อให้มั่นใจถึงความสอดคล้องกันในโปรโตคอลต่างๆ สำรวจ Modern PetstoreAPI เพื่อดูสถาปัตยกรรมหลายโปรโตคอลในการทำงาน

ปุ่ม

คำถามที่พบบ่อย

ฉันจำเป็นต้องรองรับทั้งสามโปรโตคอลหรือไม่?

ไม่จำเป็น เริ่มต้นด้วย REST สำหรับ Public API เพิ่ม GraphQL หากไคลเอนต์ต้องการการดึงข้อมูลที่ยืดหยุ่น เพิ่ม gRPC สำหรับไมโครเซอร์วิสภายใน เพิ่มโปรโตคอลเมื่อคุณมีกรณีการใช้งานที่ชัดเจนเท่านั้น

ฉันจะรักษาความสอดคล้องของโปรโตคอลได้อย่างไร?

แบ่งปันตรรกะทางธุรกิจในชั้นโดเมน อะแดปเตอร์โปรโตคอลควรแปลรูปแบบเท่านั้น ไม่ควรรวมกฎทางธุรกิจ ทดสอบโปรโตคอลทั้งหมดเพื่อยืนยันว่าส่งคืนข้อมูลเดียวกัน

ฉันสามารถกำหนดเวอร์ชันโปรโตคอลแยกกันได้หรือไม่?

ได้ REST อาจเป็น v2 ในขณะที่ GraphQL เป็น v1 แต่สิ่งนี้ทำให้เกิดความซับซ้อน พยายามรักษาโปรโตคอลให้ตรงกันเมื่อเป็นไปได้

ฉันจะจัดการการรับรองความถูกต้องในโปรโตคอลต่างๆ ได้อย่างไร?

ใช้การรับรองความถูกต้องเดียวกันในทุกโปรโตคอล REST ใช้ Bearer tokens ในส่วนหัว GraphQL ใช้โทเค็นเดียวกัน gRPC ใช้เมตาเดตา ชั้นโดเมนจะตรวจสอบโทเค็นด้วยวิธีเดียวกัน

แล้ว WebSocket และ SSE ล่ะ?

WebSocket และ SSE เป็นโปรโตคอลการขนส่ง ไม่ใช่โปรโตคอล API คุณสามารถเพิ่มสิ่งเหล่านี้ควบคู่ไปกับ REST/GraphQL/gRPC สำหรับการอัปเดตแบบเรียลไทม์ Modern PetstoreAPI มีทั้งสองอย่าง

ฉันจะจัดทำเอกสาร API แบบหลายโปรโตคอลได้อย่างไร?

ใช้ OpenAPI สำหรับ REST, GraphQL schema สำหรับ GraphQL และไฟล์ .proto สำหรับ gRPC Modern PetstoreAPI จัดเตรียมทั้งสามอย่างที่ https://docs.petstoreapi.com/

ฉันสามารถใช้ฐานข้อมูลที่แตกต่างกันสำหรับโปรโตคอลที่แตกต่างกันได้หรือไม่?

ได้ แต่จะสร้างความท้าทายด้านความสอดคล้อง ควรใช้ชั้นข้อมูลเดียวกันสำหรับทุกโปรโตคอล ให้อะแดปเตอร์โปรโตคอลจัดการความแตกต่างของรูปแบบ

ฉันจะทดสอบ API แบบหลายโปรโตคอลได้อย่างไร?

ใช้ Apidog เพื่อทดสอบทุกโปรโตคอลในเครื่องมือเดียว สร้างชุดการทดสอบที่ยืนยันความสอดคล้องกันในโปรโตคอลต่างๆ ทดสอบว่า REST, GraphQL และ gRPC ส่งคืนข้อมูลเดียวกันสำหรับการดำเนินการเดียวกัน

ฝึกการออกแบบ API แบบ Design-first ใน Apidog

ค้นพบวิธีที่ง่ายขึ้นในการสร้างและใช้ API