𝕏 f B! L
案件・求人数 12,345
案件を探す(準備中) エージェントを探す(準備中) お役立ち情報 ログイン
案件・求人数 12,345
Google AntigravityでgRPC API開発を効率化|Protocol Buffers自動生成ガイド

Google AntigravityでgRPC API開発を効率化|Protocol Buffers自動生成ガイド

Google AntigravitygRPCProtocol BuffersAPI開発SESエンジニア
目次
⚡ 3秒でわかる!この記事のポイント
  • AntigravityでProtocol Buffers定義とgRPCサーバーコードを自動生成
  • REST APIからgRPCへの移行をAIが支援し、通信速度を最大10倍高速化
  • gRPCエンジニアの月単価は80〜120万円、マイクロサービス案件で需要急増中

マイクロサービス間通信のデファクトスタンダードとなったgRPC。JSON over HTTPに比べて最大10倍高速なバイナリ通信、厳格な型定義、双方向ストリーミングなど、エンタープライズ開発に不可欠な機能を備えています。

しかし、Protocol Buffers(protobuf)の定義、コード生成、サーバー実装、クライアント生成と、学習コストと作業量が多いのがgRPCの課題です。Google Antigravityを使えば、これらの作業を大幅に効率化できます。

この記事でわかること
  • gRPCの基礎概念とREST APIとの比較
  • AntigravityでProtocol Buffers定義を自動生成する方法
  • gRPCサーバー・クライアントの実装をAIで効率化する手法
  • ストリーミングRPCの実装パターン
  • gRPC-Webでフロントエンドからの呼び出しを実現する方法

gRPCとは?REST APIとの違い

gRPCの概要

gRPC(gRPC Remote Procedure Calls)は、Googleが開発した高性能なRPCフレームワークです。HTTP/2上で動作し、Protocol Buffersでデータをシリアライズすることで、高効率な通信を実現します。

比較項目REST APIgRPC
プロトコルHTTP/1.1HTTP/2
データ形式JSON(テキスト)Protocol Buffers(バイナリ)
型安全性OpenAPI / Zod言語レベルで保証
ストリーミングWebSocket別途4種類の通信パターン内蔵
コード生成手動 / OpenAPI Generatorprotoc で自動生成
パフォーマンス基準5〜10倍高速
ブラウザ対応△(gRPC-Web必要)

gRPCの4つの通信パターン

  1. Unary RPC: 1リクエスト → 1レスポンス(REST APIと同様)
  2. Server Streaming: 1リクエスト → 複数レスポンス(リアルタイムフィード)
  3. Client Streaming: 複数リクエスト → 1レスポンス(ファイルアップロード)
  4. Bidirectional Streaming: 双方向リアルタイム通信(チャット・ゲーム)

SES現場でのgRPC需要

2026年現在、gRPCスキルの需要は急増しています:

  • マイクロサービス間通信: サービスメッシュ(Istio/Linkerd)でgRPCが標準に
  • リアルタイム処理: 金融・IoTでのストリーミング処理にgRPCが採用
  • モバイルバックエンド: gRPCのバイナリ通信で帯域幅を削減
  • AI/ML基盤: TensorFlow ServingやTriton推論サーバーのAPI

gRPCエンジニアの月単価は80〜120万円で、特にGo + gRPCの組み合わせは最も需要が高いスキルセットの一つです。

AntigravityでProtocol Buffers定義を自動生成

ユースケースからProtobuf定義を生成

Antigravityに自然言語でユースケースを記述すると、Protocol Buffers定義を自動生成できます:

ECサイトの注文管理マイクロサービスのProtocol Buffers定義を作成して。
- 注文の作成・取得・一覧・更新・キャンセル
- 注文ステータスの変更通知(Server Streaming)
- 注文アイテムには商品ID、数量、単価を含む
- ページネーション対応の一覧API
- 認証用のメタデータ対応

Antigravityが生成するProtobuf定義:

// proto/order/v1/order.proto
syntax = "proto3";

package order.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

option go_package = "github.com/example/order-service/gen/order/v1;orderv1";

// 注文サービス定義
service OrderService {
  // 注文作成
  rpc CreateOrder(CreateOrderRequest) returns (Order);
  // 注文取得
  rpc GetOrder(GetOrderRequest) returns (Order);
  // 注文一覧
  rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
  // 注文更新
  rpc UpdateOrder(UpdateOrderRequest) returns (Order);
  // 注文キャンセル
  rpc CancelOrder(CancelOrderRequest) returns (Order);
  // 注文ステータス変更通知(Server Streaming)
  rpc WatchOrderStatus(WatchOrderStatusRequest) returns (stream OrderStatusEvent);
}

// 注文ステータス
enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;
  ORDER_STATUS_PENDING = 1;
  ORDER_STATUS_CONFIRMED = 2;
  ORDER_STATUS_PROCESSING = 3;
  ORDER_STATUS_SHIPPED = 4;
  ORDER_STATUS_DELIVERED = 5;
  ORDER_STATUS_CANCELLED = 6;
}

// 注文メッセージ
message Order {
  string id = 1;
  string customer_id = 2;
  repeated OrderItem items = 3;
  OrderStatus status = 4;
  int64 total_amount = 5; // 税込合計(円)
  string shipping_address = 6;
  google.protobuf.Timestamp created_at = 7;
  google.protobuf.Timestamp updated_at = 8;
}

message OrderItem {
  string product_id = 1;
  string product_name = 2;
  int32 quantity = 3;
  int64 unit_price = 4; // 単価(円)
  int64 subtotal = 5;   // 小計(円)
}

// リクエスト・レスポンス
message CreateOrderRequest {
  string customer_id = 1;
  repeated OrderItemInput items = 2;
  string shipping_address = 3;
}

message OrderItemInput {
  string product_id = 1;
  int32 quantity = 2;
}

message GetOrderRequest {
  string id = 1;
}

message ListOrdersRequest {
  string customer_id = 1;
  OrderStatus status_filter = 2;
  int32 page_size = 3;
  string page_token = 4;
}

message ListOrdersResponse {
  repeated Order orders = 1;
  string next_page_token = 2;
  int32 total_count = 3;
}

message UpdateOrderRequest {
  string id = 1;
  string shipping_address = 2;
}

message CancelOrderRequest {
  string id = 1;
  string reason = 2;
}

message WatchOrderStatusRequest {
  string order_id = 1;
}

message OrderStatusEvent {
  string order_id = 1;
  OrderStatus previous_status = 2;
  OrderStatus current_status = 3;
  google.protobuf.Timestamp changed_at = 4;
  string message = 5;
}

buf.yamlの設定自動化

Antigravityに「bufのプロジェクト設定もセットアップして」と指示すると、Buf CLI用の設定も自動生成します:

# buf.yaml
version: v2
modules:
  - path: proto
lint:
  use:
    - STANDARD
  except:
    - PACKAGE_VERSION_SUFFIX
breaking:
  use:
    - FILE
# buf.gen.yaml
version: v2
managed:
  enabled: true
plugins:
  - remote: buf.build/protocolbuffers/go
    out: gen
    opt: paths=source_relative
  - remote: buf.build/grpc/go
    out: gen
    opt: paths=source_relative
  - remote: buf.build/connectrpc/go
    out: gen
    opt: paths=source_relative

gRPCサーバーの実装

Go + Connect RPC で高性能サーバーを構築

Antigravityに「生成したProtobufからGoのgRPCサーバーを実装して」と指示します:

Goでorder.v1.OrderServiceのgRPCサーバーを実装して。
- Connect RPCフレームワークを使用
- PostgreSQLをデータストアとして
- 構造化ログ(slog)
- OpenTelemetryトレーシング
- ページネーションはカーソルベース
// internal/service/order.go
package service

import (
	"context"
	"fmt"
	"log/slog"
	"time"

	"connectrpc.com/connect"
	orderv1 "github.com/example/order-service/gen/order/v1"
	"github.com/example/order-service/internal/repository"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"google.golang.org/protobuf/types/known/timestamppb"
)

var tracer = otel.Tracer("order-service")

type OrderService struct {
	repo   repository.OrderRepository
	logger *slog.Logger
}

func NewOrderService(repo repository.OrderRepository, logger *slog.Logger) *OrderService {
	return &OrderService{repo: repo, logger: logger}
}

func (s *OrderService) CreateOrder(
	ctx context.Context,
	req *connect.Request[orderv1.CreateOrderRequest],
) (*connect.Response[orderv1.Order], error) {
	ctx, span := tracer.Start(ctx, "OrderService.CreateOrder")
	defer span.End()

	msg := req.Msg
	s.logger.InfoContext(ctx, "注文作成開始",
		slog.String("customer_id", msg.CustomerId),
		slog.Int("item_count", len(msg.Items)),
	)

	// バリデーション
	if msg.CustomerId == "" {
		return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("customer_id is required"))
	}
	if len(msg.Items) == 0 {
		return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("at least one item is required"))
	}

	// 注文アイテムの構築
	items := make([]repository.OrderItem, len(msg.Items))
	var totalAmount int64
	for i, item := range msg.Items {
		product, err := s.repo.GetProduct(ctx, item.ProductId)
		if err != nil {
			return nil, connect.NewError(connect.CodeNotFound,
				fmt.Errorf("product %s not found", item.ProductId))
		}
		subtotal := product.Price * int64(item.Quantity)
		items[i] = repository.OrderItem{
			ProductID:   item.ProductId,
			ProductName: product.Name,
			Quantity:    item.Quantity,
			UnitPrice:   product.Price,
			Subtotal:    subtotal,
		}
		totalAmount += subtotal
	}

	span.SetAttributes(attribute.Int64("order.total_amount", totalAmount))

	// 注文をDBに保存
	order, err := s.repo.CreateOrder(ctx, repository.CreateOrderInput{
		CustomerID:      msg.CustomerId,
		Items:           items,
		TotalAmount:     totalAmount,
		ShippingAddress: msg.ShippingAddress,
	})
	if err != nil {
		s.logger.ErrorContext(ctx, "注文作成失敗", slog.Any("error", err))
		return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to create order"))
	}

	s.logger.InfoContext(ctx, "注文作成完了",
		slog.String("order_id", order.ID),
		slog.Int64("total_amount", totalAmount),
	)

	return connect.NewResponse(toProtoOrder(order)), nil
}

func (s *OrderService) GetOrder(
	ctx context.Context,
	req *connect.Request[orderv1.GetOrderRequest],
) (*connect.Response[orderv1.Order], error) {
	ctx, span := tracer.Start(ctx, "OrderService.GetOrder")
	defer span.End()

	order, err := s.repo.GetOrder(ctx, req.Msg.Id)
	if err != nil {
		return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("order not found"))
	}

	return connect.NewResponse(toProtoOrder(order)), nil
}

func (s *OrderService) WatchOrderStatus(
	ctx context.Context,
	req *connect.Request[orderv1.WatchOrderStatusRequest],
	stream *connect.ServerStream[orderv1.OrderStatusEvent],
) error {
	ctx, span := tracer.Start(ctx, "OrderService.WatchOrderStatus")
	defer span.End()

	orderID := req.Msg.OrderId
	s.logger.InfoContext(ctx, "ステータス監視開始", slog.String("order_id", orderID))

	// ステータス変更チャネルを購読
	events, cancel := s.repo.SubscribeOrderStatus(ctx, orderID)
	defer cancel()

	for {
		select {
		case <-ctx.Done():
			return nil
		case event, ok := <-events:
			if !ok {
				return nil
			}
			if err := stream.Send(&orderv1.OrderStatusEvent{
				OrderId:        orderID,
				PreviousStatus: toProtoStatus(event.PreviousStatus),
				CurrentStatus:  toProtoStatus(event.CurrentStatus),
				ChangedAt:      timestamppb.New(event.ChangedAt),
				Message:        event.Message,
			}); err != nil {
				return err
			}
		}
	}
}

func toProtoOrder(o *repository.Order) *orderv1.Order {
	items := make([]*orderv1.OrderItem, len(o.Items))
	for i, item := range o.Items {
		items[i] = &orderv1.OrderItem{
			ProductId:   item.ProductID,
			ProductName: item.ProductName,
			Quantity:    item.Quantity,
			UnitPrice:   item.UnitPrice,
			Subtotal:    item.Subtotal,
		}
	}
	return &orderv1.Order{
		Id:              o.ID,
		CustomerId:      o.CustomerID,
		Items:           items,
		Status:          toProtoStatus(o.Status),
		TotalAmount:     o.TotalAmount,
		ShippingAddress: o.ShippingAddress,
		CreatedAt:       timestamppb.New(o.CreatedAt),
		UpdatedAt:       timestamppb.New(o.UpdatedAt),
	}
}

func toProtoStatus(s string) orderv1.OrderStatus {
	switch s {
	case "pending":
		return orderv1.OrderStatus_ORDER_STATUS_PENDING
	case "confirmed":
		return orderv1.OrderStatus_ORDER_STATUS_CONFIRMED
	case "processing":
		return orderv1.OrderStatus_ORDER_STATUS_PROCESSING
	case "shipped":
		return orderv1.OrderStatus_ORDER_STATUS_SHIPPED
	case "delivered":
		return orderv1.OrderStatus_ORDER_STATUS_DELIVERED
	case "cancelled":
		return orderv1.OrderStatus_ORDER_STATUS_CANCELLED
	default:
		return orderv1.OrderStatus_ORDER_STATUS_UNSPECIFIED
	}
}

gRPCのテスト自動化

ユニットテストの自動生成

Antigravityに「OrderServiceのユニットテストを網羅的に生成して」と指示します:

// internal/service/order_test.go
package service_test

import (
	"context"
	"testing"

	"connectrpc.com/connect"
	orderv1 "github.com/example/order-service/gen/order/v1"
	"github.com/example/order-service/internal/service"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCreateOrder_Success(t *testing.T) {
	repo := newMockRepo()
	svc := service.NewOrderService(repo, testLogger())

	req := connect.NewRequest(&orderv1.CreateOrderRequest{
		CustomerId: "customer-1",
		Items: []*orderv1.OrderItemInput{
			{ProductId: "product-1", Quantity: 2},
			{ProductId: "product-2", Quantity: 1},
		},
		ShippingAddress: "東京都渋谷区...",
	})

	resp, err := svc.CreateOrder(context.Background(), req)
	require.NoError(t, err)
	assert.NotEmpty(t, resp.Msg.Id)
	assert.Equal(t, orderv1.OrderStatus_ORDER_STATUS_PENDING, resp.Msg.Status)
	assert.Len(t, resp.Msg.Items, 2)
}

func TestCreateOrder_EmptyItems(t *testing.T) {
	repo := newMockRepo()
	svc := service.NewOrderService(repo, testLogger())

	req := connect.NewRequest(&orderv1.CreateOrderRequest{
		CustomerId: "customer-1",
		Items:      []*orderv1.OrderItemInput{},
	})

	_, err := svc.CreateOrder(context.Background(), req)
	require.Error(t, err)
	assert.Equal(t, connect.CodeInvalidArgument, connect.CodeOf(err))
}

func TestGetOrder_NotFound(t *testing.T) {
	repo := newMockRepo()
	svc := service.NewOrderService(repo, testLogger())

	req := connect.NewRequest(&orderv1.GetOrderRequest{
		Id: "non-existent",
	})

	_, err := svc.GetOrder(context.Background(), req)
	require.Error(t, err)
	assert.Equal(t, connect.CodeNotFound, connect.CodeOf(err))
}

gRPCurlによるE2Eテスト

# grpcurl でサービスの動作確認
grpcurl -plaintext -d '{
  "customer_id": "customer-1",
  "items": [
    {"product_id": "product-1", "quantity": 2}
  ],
  "shipping_address": "東京都渋谷区..."
}' localhost:8080 order.v1.OrderService/CreateOrder

gRPC-WebでブラウザからgRPCを呼ぶ

Connect-Webの導入

ブラウザからgRPCサービスを呼び出すには、Connect-Webが最適です:

// frontend/src/api/orderClient.ts
import { createConnectTransport } from '@connectrpc/connect-web';
import { createClient } from '@connectrpc/connect';
import { OrderService } from '../gen/order/v1/order_connect';

const transport = createConnectTransport({
  baseUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080',
});

export const orderClient = createClient(OrderService, transport);

// 使用例
export async function createOrder(customerId: string, items: OrderItemInput[]) {
  const response = await orderClient.createOrder({
    customerId,
    items,
    shippingAddress: '東京都渋谷区...',
  });
  return response;
}

// Server Streaming の使用例
export async function* watchOrderStatus(orderId: string) {
  for await (const event of orderClient.watchOrderStatus({ orderId })) {
    yield {
      orderId: event.orderId,
      previousStatus: event.previousStatus,
      currentStatus: event.currentStatus,
      changedAt: event.changedAt?.toDate(),
      message: event.message,
    };
  }
}

REST APIからgRPCへの段階的移行

gRPC-GatewayでREST互換性を維持

既存のRESTクライアントとの互換性を維持しながらgRPCに移行するパターン:

// REST互換アノテーション
import "google/api/annotations.proto";

service OrderService {
  rpc CreateOrder(CreateOrderRequest) returns (Order) {
    option (google.api.http) = {
      post: "/v1/orders"
      body: "*"
    };
  }
  rpc GetOrder(GetOrderRequest) returns (Order) {
    option (google.api.http) = {
      get: "/v1/orders/{id}"
    };
  }
  rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse) {
    option (google.api.http) = {
      get: "/v1/orders"
    };
  }
}

これにより、同じサービス定義からgRPCエンドポイントとREST APIの両方を自動生成できます。

SES現場でのgRPCプロジェクト実績の作り方

ポートフォリオ構築

gRPCスキルをアピールするためのプロジェクト例:

  1. マイクロサービスサンプル: 3-4サービスのgRPC通信、サービスメッシュ対応
  2. ストリーミングアプリ: チャットやリアルタイム通知のServer Streaming実装
  3. gRPC-Gateway: REST互換レイヤーの構築と負荷テスト
  4. Buf + CI/CD: Protocol Buffersのlint、breaking change検出の自動化

gRPCスキルのキャリアパス

レベル月単価求められるスキル
ジュニア60〜80万円protobuf定義、Unary RPC実装
ミドル80〜100万円ストリーミング、テスト、CI/CD統合
シニア100〜130万円サービスメッシュ、パフォーマンスチューニング、設計レビュー

Google Antigravity gRPC API開発の全体フロー

まとめ

Google Antigravityを活用することで、gRPC開発の学習コストと作業量を大幅に削減できます。

  • Protocol Buffers: 自然言語から.protoファイルを自動生成
  • サーバー実装: Connect RPCフレームワークでGoサーバーを効率的に実装
  • テスト: ユニットテスト、E2Eテストの自動生成
  • フロントエンド連携: Connect-WebでブラウザからシームレスにgRPCを呼び出し

gRPCは2026年のSES現場で最も需要が高いスキルの一つです。Antigravityの支援を活用して、効率的にスキルアップしていきましょう。

💡 SES BASEでgRPC・マイクロサービス案件を探す

SES BASEでは、Go/gRPC、マイクロサービス、Kubernetesの高単価案件を多数掲載しています。gRPCスキルを活かせる案件をチェックしましょう。

関連記事

SES案件をお探しですか?

SES記事をもっと読む →
🏗️

SES BASE 編集長

SES業界歴10年以上のメンバーが在籍する編集チーム。SES企業での営業・エンジニア経験、フリーランス独立経験を持つメンバーが、業界のリアルな情報をお届けします。

📊 業界データに基づく記事制作 🔍 IPA・経済産業省データ参照 💼 SES実務経験者が執筆・監修