Cara Membuat API Multi-Protokol dengan REST, GraphQL, dan gRPC

Ashley Innocent

Ashley Innocent

13 March 2026

Cara Membuat API Multi-Protokol dengan REST, GraphQL, dan gRPC

Inti Sari

Bangun API multi-protokol dengan memisahkan logika bisnis dari lapisan protokol. Buat lapisan domain bersama, lalu tambahkan adaptor REST, GraphQL, dan gRPC di atasnya. Modern PetstoreAPI mendemonstrasikan arsitektur ini dengan model data yang konsisten di ketiga protokol.

Pendahuluan

API Anda melayani klien web, aplikasi seluler, dan layanan mikro internal. Klien web menginginkan REST untuk kesederhanaan. Aplikasi seluler menginginkan GraphQL untuk mengurangi transfer data. Layanan mikro menginginkan gRPC untuk kinerja. Apakah Anda akan membangun tiga API yang terpisah?

Tidak. Anda membangun satu API dengan tiga lapisan protokol. Logika bisnis tetap sama. Hanya adaptor protokol yang berubah. Ini adalah arsitektur API multi-protokol.

Modern PetstoreAPI mengimplementasikan REST, GraphQL, dan gRPC dari inti bersama. Logika toko hewan peliharaan yang sama melayani ketiga protokol dengan perilaku yang konsisten.

💡
Jika Anda sedang membangun atau menguji API multi-protokol, Apidog mendukung pengujian REST, GraphQL, dan gRPC dalam satu alat. Anda dapat memverifikasi bahwa semua protokol mengembalikan data yang konsisten dan menguji alur kerja lintas-protokol.
tombol

Dalam panduan ini, Anda akan belajar cara mengarsitektur API multi-protokol, mengimplementasikan lapisan protokol, dan memastikan konsistensi di seluruh protokol menggunakan Modern PetstoreAPI sebagai referensi.

Arsitektur Multi-Protokol

API multi-protokol memisahkan kekhawatiran ke dalam lapisan.

Arsitektur Berlapis

┌─────────────────────────────────────────┐
│  Protocol Layer (REST/GraphQL/gRPC)     │ (Lapisan Protokol)
├─────────────────────────────────────────┤
│  Application Layer (Use Cases)          │ (Lapisan Aplikasi - Kasus Penggunaan)
├─────────────────────────────────────────┤
│  Domain Layer (Business Logic)          │ (Lapisan Domain - Logika Bisnis)
├─────────────────────────────────────────┤
│  Data Layer (Database, Cache)           │ (Lapisan Data - Basis Data, Cache)
└─────────────────────────────────────────┘

Lapisan Protokol: Menangani HTTP, kueri GraphQL, panggilan gRPC
Lapisan Aplikasi: Mengatur kasus penggunaan (membuat hewan peliharaan, memesan)
Lapisan Domain: Aturan bisnis (validasi, perhitungan)
Lapisan Data: Persistensi (basis data, cache)

Prinsip Utama

1. Inti yang Agnostik-Protokol

Logika bisnis tidak tahu tentang HTTP, GraphQL, atau gRPC. Ia bekerja dengan objek domain.

2. Adaptor Protokol Tipis

Lapisan protokol menerjemahkan antara format protokol dan objek domain. Mereka tidak mengandung logika bisnis.

3. Model Data Bersama

Semua protokol menggunakan model domain yang sama secara internal, memastikan konsistensi.

4. Penerapan Independen

Setiap protokol dapat diterapkan secara terpisah jika diperlukan.

Lapisan Domain Bersama

Lapisan domain berisi logika bisnis yang dibagi di seluruh protokol.

Model Domain

// Domain model (agnostik-protokol)
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
    });
  }
}

Model ini bekerja untuk REST, GraphQL, dan gRPC. Aturan validasi dan bisnisnya sama.

Kasus Penggunaan

// Kasus penggunaan (agnostik-protokol)
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;
  }
}

Kasus penggunaan ini bekerja terlepas dari apakah dipanggil dari REST, GraphQL, atau gRPC.

Lapisan Protokol REST

Lapisan REST menerjemahkan permintaan HTTP ke operasi domain.

Pengontrol REST

// Adaptor 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
      });
    }
  }
}

Rute REST

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

Titik Akhir REST Modern PetstoreAPI

Lapisan Protokol GraphQL

Lapisan GraphQL menerjemahkan kueri GraphQL ke operasi domain.

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

Resolver GraphQL

// Adaptor 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'
          }
        });
      }
    }
  }
};

Permintaan GraphQL

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

Skema GraphQL Modern PetstoreAPI

Lapisan Protokol gRPC

Lapisan gRPC menerjemahkan pesan Protocol Buffer ke operasi domain.

Definisi 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;
}

Implementasi Layanan gRPC

// Adaptor 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
      });
    }
  }
}

Layanan gRPC Modern PetstoreAPI

Bagaimana Modern PetstoreAPI Mengimplementasikan Multi-Protokol

Modern PetstoreAPI mendemonstrasikan arsitektur multi-protokol dengan contoh nyata.

Ikhtisar Arsitektur

Modern PetstoreAPI
├── Domain Layer                   (Lapisan Domain)
│   ├── Pet (entity)               (Hewan Peliharaan - entitas)
│   ├── Order (entity)             (Pesanan - entitas)
│   └── Use Cases                  (Kasus Penggunaan)
│       ├── CreatePet              (Buat Hewan Peliharaan)
│       ├── AdoptPet               (Adopsi Hewan Peliharaan)
│       └── PlaceOrder             (Buat Pesanan)
├── REST Layer                     (Lapisan REST)
│   ├── /v1/pets
│   ├── /v1/orders
│   └── OpenAPI 3.2 spec           (Spesifikasi OpenAPI 3.2)
├── GraphQL Layer                  (Lapisan GraphQL)
│   ├── Query resolvers            (Resolver Kueri)
│   ├── Mutation resolvers         (Resolver Mutasi)
│   └── GraphQL schema             (Skema GraphQL)
└── gRPC Layer                     (Lapisan gRPC)
    ├── PetService                 (Layanan Hewan Peliharaan)
    ├── OrderService               (Layanan Pesanan)
    └── .proto definitions         (Definisi .proto)

Model Data yang Konsisten

Semua protokol mengembalikan data yang sama:

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
}

Data yang sama, format yang berbeda.

Validasi Bersama

Validasi terjadi di lapisan domain, jadi semua protokol menegakkan aturan yang sama:

// Validasi domain (bersama)
if (name.length < 2) {
  throw new ValidationError('Name must be at least 2 characters');
}

REST mengembalikan:

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

GraphQL mengembalikan:

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

gRPC mengembalikan:

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

Validasi yang sama, format kesalahan spesifik protokol.

Menguji API Multi-Protokol dengan Apidog

Apidog mendukung pengujian ketiga protokol.

Uji Titik Akhir REST

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

{
  "userId": "user-123"
}

Uji Mutasi GraphQL

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

Uji Layanan gRPC

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

Verifikasi Konsistensi

Uji bahwa ketiga protokol mengembalikan data yang sama:

  1. Panggil titik akhir REST
  2. Panggil mutasi GraphQL
  3. Panggil layanan gRPC
  4. Bandingkan hasilnya

Apidog dapat mengotomatiskan pengujian lintas-protokol ini.

Strategi Penerapan

Strategi 1: Layanan Tunggal

Terapkan semua protokol dalam satu layanan:

┌─────────────────────────┐
│  PetstoreAPI Service    │ (Layanan PetstoreAPI)
│  ├── REST (port 8080)   │
│  ├── GraphQL (port 8081)│
│  └── gRPC (port 50051)  │
└─────────────────────────┘

Pro: Penerapan sederhana, sumber daya bersama
Kontra: Semua protokol skalanya bersamaan

Strategi 2: Layanan Terpisah

Terapkan setiap protokol secara terpisah:

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ REST Service │  │ GraphQL Svc  │  │ gRPC Service │
│ (port 8080)  │  │ (port 8081)  │  │ (port 50051) │
└──────────────┘  └──────────────┘  └──────────────┘
       │                 │                  │
       └─────────────────┴──────────────────┘
                         │
                  ┌──────────────┐
                  │ Shared Core  │ (Inti Bersama)
                  │   Library    │ (Pustaka)
                  └──────────────┘

Pro: Skala independen, isolasi protokol
Kontra: Penerapan lebih kompleks

Strategi 3: API Gateway

Gunakan API gateway untuk merutekan ke backend spesifik-protokol:

                ┌─────────────┐
                │ API Gateway │
                └─────────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ REST Backend │ │ GraphQL  │ │ gRPC Backend│
└──────────────┘ └──────────┘ └─────────────┘

Pro: Perutean terpusat, pembatasan laju, autentikasi
Kontra: Latensi tambahan, kompleksitas gateway

Modern PetstoreAPI menggunakan Strategi 1 untuk kesederhanaan.

Kesimpulan

API multi-protokol memberikan fleksibilitas kepada klien tanpa menduplikasi logika bisnis. Dengan memisahkan lapisan protokol dari logika domain, Anda dapat mendukung REST, GraphQL, dan gRPC dari inti bersama.

Modern PetstoreAPI mendemonstrasikan arsitektur ini dengan model data yang konsisten, validasi bersama, dan adaptor spesifik-protokol. Baik klien menggunakan REST untuk kesederhanaan, GraphQL untuk fleksibilitas, atau gRPC untuk kinerja, mereka mengakses toko hewan peliharaan yang sama dengan aturan bisnis yang sama.

Uji API multi-protokol Anda dengan Apidog untuk memastikan konsistensi di seluruh protokol. Jelajahi Modern PetstoreAPI untuk melihat arsitektur multi-protokol beraksi.

tombol

Pertanyaan Umum

Apakah saya perlu mendukung ketiga protokol ini?

Tidak. Mulailah dengan REST untuk API publik. Tambahkan GraphQL jika klien memerlukan pengambilan data yang fleksibel. Tambahkan gRPC untuk layanan mikro internal. Hanya tambahkan protokol ketika Anda memiliki kasus penggunaan yang jelas.

Bagaimana cara menjaga protokol tetap konsisten?

Bagikan logika bisnis dalam lapisan domain. Adaptor protokol seharusnya hanya menerjemahkan format, bukan mengandung aturan bisnis. Uji semua protokol untuk memverifikasi bahwa mereka mengembalikan data yang sama.

Bisakah saya membuat versi protokol secara independen?

Ya. REST bisa berada di v2 sementara GraphQL berada di v1. Tetapi ini menciptakan kompleksitas. Cobalah untuk menjaga protokol tetap sinkron jika memungkinkan.

Bagaimana cara menangani autentikasi di seluruh protokol?

Gunakan autentikasi yang sama di semua protokol. REST menggunakan token Bearer di header. GraphQL menggunakan token yang sama. gRPC menggunakan metadata. Lapisan domain memvalidasi token dengan cara yang sama.

Bagaimana dengan WebSocket dan SSE?

WebSocket dan SSE adalah protokol transportasi, bukan protokol API. Anda dapat menambahkannya di samping REST/GraphQL/gRPC untuk pembaruan waktu nyata. Modern PetstoreAPI menyertakan keduanya.

Bagaimana cara mendokumentasikan API multi-protokol?

Gunakan OpenAPI untuk REST, skema GraphQL untuk GraphQL, dan file .proto untuk gRPC. Modern PetstoreAPI menyediakan ketiganya di https://docs.petstoreapi.com/

Bisakah saya menggunakan basis data yang berbeda untuk protokol yang berbeda?

Ya, tetapi ini menciptakan tantangan konsistensi. Lebih baik menggunakan lapisan data yang sama untuk semua protokol. Biarkan adaptor protokol menangani perbedaan format.

Bagaimana cara menguji API multi-protokol?

Gunakan Apidog untuk menguji semua protokol dalam satu alat. Buat suite pengujian yang memverifikasi konsistensi di seluruh protokol. Uji bahwa REST, GraphQL, dan gRPC mengembalikan data yang sama untuk operasi yang sama.

Mengembangkan API dengan Apidog

Apidog adalah alat pengembangan API yang membantu Anda mengembangkan API dengan lebih mudah dan efisien.