From Legacy Rest to GPC - Vanguard-Go ⚔️

So what is Vanguard? Vanguard is a powerful library for Go net/http servers that enables seamless transcoding between REST and RPC protocols. Whether you need to bridge the gap between gRPC, gRPC-Web, Connect, or REST, Vanguard has got you covered. With support for Google's HTTP transcoding options, it can effortlessly translate protocols using strongly typed Protobuf definitions. Why Vanguard? Vanguard offers a range of compelling use cases that make it an invaluable addition to your services: RESTful Transformation: By leveraging HTTP transcoding annotations, you can effortlessly support REST clients. This feature is especially handy during the migration from a REST API to a schema-driven RPC API. With the right annotations, your existing REST clients can seamlessly access your API, even as you transition your server implementations to Protobuf and RPC. Efficiency and Code Generation: Unlike traditional approaches like gRPC-Gateway, Vanguard operates efficiently within Go servers, compatible with various servers such as Connect and gRPC. It doesn't rely on extensive code generation, eliminating the need for additional code generation steps. This flexibility ensures that your code can adapt dynamically, loading service definitions from configuration, schema registries, or via gRPC Server Reflection, making it a perfect fit for proxies without the hassle of recompilation and redeployment each time an RPC service schema changes. Legacy Compatibility: The HTTP transcoding annotations also empower you to support legacy REST API servers when clients are accustomed to using Protobuf RPC. This lets you embrace RPC in specific teams, such as for web or mobile clients, without the prerequisite of migrating all backend API services. Seamless Protocol Bridging: If your organization is transitioning from gRPC to Connect, Vanguard acts as a bridge between the protocols. This facilitates the use of your existing gRPC service handlers with Connect clients, allowing you to smoothly adapt to Connect's enhanced usability and inspectability with web browsers and mobile devices. No need to overhaul your server handler logic before migrating clients to Connect. To bridge a REST API to gRPC using the Vanguard library in Go, as provided by connectrpc/vanguard-go, you can leverage its ability to transcode between REST and RPC protocols seamlessly. This guide outlines the steps to set up a server that supports both REST and gRPC clients using Vanguard, based on the library’s capabilities described in the repository. Prerequisites Go: Ensure Go is installed (version compatible with the two most recent major releases, per the library’s support). Protobuf: Install protoc and the necessary plugins (protoc-gen-go, protoc-gen-connect-go, protoc-gen-grpc-go). Vanguard: Install the Vanguard library: go get connectrpc.com/vanguard Step-by-Step Guide 1. Define Your Protobuf Schema Create a .proto file to define your service and messages. Include Google’s HTTP transcoding annotations to map gRPC methods to RESTful endpoints. For example: syntax = "proto3"; package example.v1; import "google/api/annotations.proto"; service ExampleService { rpc GetItem(GetItemRequest) returns (Item) { option (google.api.http) = { get: "/v1/items/{id}" }; } rpc CreateItem(CreateItemRequest) returns (Item) { option (google.api.http) = { post: "/v1/items" body: "*" }; } } message GetItemRequest { string id = 1; } message CreateItemRequest { string name = 1; } message Item { string id = 1; string name = 2; } The (google.api.http) annotations map gRPC methods to REST paths and HTTP methods. Save this as example.proto in a directory like proto/. 2. Generate Code Compile the .proto file to generate Go code for gRPC, Connect, and REST handling: protoc --go_out=. --go_opt=paths=source_relative \ --connect-go_out=. --connect-go_opt=paths=source_relative \ --grpc-go_out=. --grpc-go_opt=paths=source_relative \ proto/example.proto This generates: gRPC service definitions. Connect handlers for HTTP/1.1 compatibility. Structs for messages. 3. Implement the Service Create a Go server that implements the gRPC service. For example: package main import ( "context" "log" "net/http" examplev1 "path/to/generated/example/v1" "connectrpc.com/connect" ) type ExampleServer struct { examplev1connect.UnimplementedExampleServiceHandler } func (s *ExampleServer) GetItem(ctx context.Context, req *connect.Request[examplev1.GetItemRequest]) (*connect.Response[examplev1.Item], error) { // Fetch item logic (e.g., from a database). item := &examplev1.Item{Id: req.Msg.Id, Name: "Sample Item"} return connect.NewResponse(item), nil } func (s *ExampleServer) CreateItem(ctx context.Context, req *connect.Request[examplev1.CreateItemRequest]) (*connec

May 15, 2025 - 00:18
 0
From Legacy Rest to GPC - Vanguard-Go ⚔️

So what is Vanguard?

Vanguard is a powerful library for Go net/http servers that enables seamless transcoding between REST and RPC protocols. Whether you need to bridge the gap between gRPC, gRPC-Web, Connect, or REST, Vanguard has got you covered. With support for Google's HTTP transcoding options, it can effortlessly translate protocols using strongly typed Protobuf definitions.

Why Vanguard?

Vanguard offers a range of compelling use cases that make it an invaluable addition
to your services:

  1. RESTful Transformation: By leveraging HTTP transcoding annotations, you can effortlessly
    support REST clients. This feature is especially handy during the migration from a REST API
    to a schema-driven RPC API. With the right annotations, your existing REST clients can
    seamlessly access your API, even as you transition your server implementations to Protobuf
    and RPC.

  2. Efficiency and Code Generation: Unlike traditional approaches like gRPC-Gateway,
    Vanguard operates efficiently within Go servers, compatible with various servers such as
    Connect and gRPC.
    It doesn't rely on extensive code generation, eliminating the need for additional code
    generation steps. This flexibility ensures that your code can adapt dynamically, loading
    service definitions from configuration, schema registries, or via
    gRPC Server Reflection,
    making it a perfect fit for proxies without the hassle of recompilation and redeployment
    each time an RPC service schema changes.

  3. Legacy Compatibility: The HTTP transcoding annotations also empower you to support
    legacy REST API servers when clients are accustomed to using Protobuf RPC. This lets
    you embrace RPC in specific teams, such as for web or mobile clients, without the
    prerequisite of migrating all backend API services.

  4. Seamless Protocol Bridging: If your organization is transitioning from gRPC to Connect,
    Vanguard acts as a bridge between the protocols. This facilitates the use of your existing
    gRPC service handlers with Connect clients, allowing you to smoothly adapt to Connect's
    enhanced usability and inspectability with web browsers and mobile devices. No need to
    overhaul your server handler logic before migrating clients to Connect.

To bridge a REST API to gRPC using the Vanguard library in Go, as provided by connectrpc/vanguard-go, you can leverage its ability to transcode between REST and RPC protocols seamlessly. This guide outlines the steps to set up a server that supports both REST and gRPC clients using Vanguard, based on the library’s capabilities described in the repository.

Prerequisites

  • Go: Ensure Go is installed (version compatible with the two most recent major releases, per the library’s support).
  • Protobuf: Install protoc and the necessary plugins (protoc-gen-go, protoc-gen-connect-go, protoc-gen-grpc-go).
  • Vanguard: Install the Vanguard library:
  go get connectrpc.com/vanguard

Step-by-Step Guide

1. Define Your Protobuf Schema

Create a .proto file to define your service and messages. Include Google’s HTTP transcoding annotations to map gRPC methods to RESTful endpoints. For example:

syntax = "proto3";
package example.v1;
import "google/api/annotations.proto";

service ExampleService {
  rpc GetItem(GetItemRequest) returns (Item) {
    option (google.api.http) = {
      get: "/v1/items/{id}"
    };
  }
  rpc CreateItem(CreateItemRequest) returns (Item) {
    option (google.api.http) = {
      post: "/v1/items"
      body: "*"
    };
  }
}

message GetItemRequest {
  string id = 1;
}
message CreateItemRequest {
  string name = 1;
}
message Item {
  string id = 1;
  string name = 2;
}
  • The (google.api.http) annotations map gRPC methods to REST paths and HTTP methods.
  • Save this as example.proto in a directory like proto/.

2. Generate Code

Compile the .proto file to generate Go code for gRPC, Connect, and REST handling:

protoc --go_out=. --go_opt=paths=source_relative \
       --connect-go_out=. --connect-go_opt=paths=source_relative \
       --grpc-go_out=. --grpc-go_opt=paths=source_relative \
       proto/example.proto

This generates:

  • gRPC service definitions.
  • Connect handlers for HTTP/1.1 compatibility.
  • Structs for messages.

3. Implement the Service

Create a Go server that implements the gRPC service. For example:

package main

import (
    "context"
    "log"
    "net/http"
    examplev1 "path/to/generated/example/v1"
    "connectrpc.com/connect"
)

type ExampleServer struct {
    examplev1connect.UnimplementedExampleServiceHandler
}

func (s *ExampleServer) GetItem(ctx context.Context, req *connect.Request[examplev1.GetItemRequest]) (*connect.Response[examplev1.Item], error) {
    // Fetch item logic (e.g., from a database).
    item := &examplev1.Item{Id: req.Msg.Id, Name: "Sample Item"}
    return connect.NewResponse(item), nil
}

func (s *ExampleServer) CreateItem(ctx context.Context, req *connect.Request[examplev1.CreateItemRequest]) (*connect.Response[examplev1.Item], error) {
    // Create item logic.
    item := &examplev1.Item{Id: "new-id", Name: req.Msg.Name}
    return connect.NewResponse(item), nil
}
  • This server implements the ExampleService with basic logic for GetItem and CreateItem.

4. Set Up Vanguard Transcoder

Use Vanguard to wrap the service handler, enabling REST and gRPC support. Here’s a complete main.go:

package main

import (
    "log"
    "net/http"
    "connectrpc.com/vanguard"
    examplev1 "path/to/generated/example/v1"
    examplev1connect "path/to/generated/example/v1/examplev1connect"
)

func main() {
    // Initialize the service implementation.
    svc := &ExampleServer{}

    // Create a Connect handler for the service.
    path, handler := examplev1connect.NewExampleServiceHandler(svc)

    // Create a Vanguard service to transcode REST and RPC.
    services := []*vanguard.Service{
        vanguard.NewService(path, handler),
    }

    // Initialize the Vanguard transcoder.
    transcoder, err := vanguard.NewTranscoder(services)
    if err != nil {
        log.Fatalf("Failed to create transcoder: %v", err)
    }

    // Set up HTTP server with Vanguard handler.
    mux := http.NewServeMux()
    mux.Handle("/", transcoder)

    // Start the server.
    log.Println("Server starting on :8080...")
    if err := http.ListenAndServe(":8080", mux); err != nil {
        log.Fatalf("Server failed: %v", err)
    }
}
  • Vanguard Service: vanguard.NewService wraps the Connect handler, using the generated path and handler.
  • Transcoder: vanguard.NewTranscoder creates a middleware-like handler that routes and transcodes requests between REST, gRPC, gRPC-Web, and Connect protocols.
  • The server listens on port 8080 and handles both REST and gRPC requests.

5. Test the API

  • REST Client (using curl):
  # GET request
  curl http://localhost:8080/v1/items/123
  # POST request
  curl -X POST http://localhost:8080/v1/items -d '{"name": "New Item"}' -H "Content-Type: application/json"

Vanguard transcodes these REST requests to gRPC calls using the HTTP annotations.

  • gRPC Client (using a Go client or grpcurl):
  grpcurl -plaintext localhost:8080 example.v1.ExampleService/GetItem -d '{"id": "123"}'

The server handles native gRPC requests directly.

6. Optional: Add Compression and Configuration

Vanguard supports compression (e.g., gzip) and custom options. To enable compression:

services := []*vanguard.Service{
    vanguard.NewService(path, handler, vanguard.WithCompression(vanguard.CompressionGzip)),
}

You can also configure timeouts or custom codecs if needed.

Key Features of Vanguard

  • RESTful Transformation: Supports REST clients by mapping gRPC methods to HTTP endpoints via annotations, ideal for migrating from REST to gRPC.
  • Protocol Bridging: Allows gRPC and Connect clients to coexist, useful during transitions.
  • Efficiency: Unlike gRPC-Gateway, Vanguard operates within the Go server, reducing overhead and eliminating external dependencies.
  • Legacy Support: Can proxy requests to legacy REST backends, enabling gradual migration.

Example Use Case

Suppose you’re migrating a REST API to gRPC. You can:

  1. Define gRPC methods with HTTP annotations matching existing REST endpoints.
  2. Use Vanguard to handle both REST and gRPC requests.
  3. Gradually update clients to use gRPC while maintaining REST compatibility.

Troubleshooting

  • Schema Errors: Ensure the service path matches the Protobuf schema. Use vanguard.NewServiceWithSchema for dynamic schemas.
  • Content-Type Issues: REST requests must use application/json or compatible types. Vanguard validates this automatically.
  • Debugging: Enable logging or use tools like grpcurl to inspect requests.

Additional Resources

  • Vanguard README: Detailed examples and configuration options.
  • Connect Documentation: Learn more about Connect protocol and handlers.
  • Protobuf HTTP Annotations: Reference for mapping gRPC to REST.

This setup allows you to support both REST and gRPC clients with minimal changes to your server code, leveraging Vanguard’s transcoding capabilities for a smooth transition or hybrid API. _

Let's Connect!!

Image description

W3C Social Icon

X icon

*Coding examples are AI generated.