Open20

ConnectのGetting startedをやってみる

島袋恵島袋恵

ひとまずインストールした

mkdir connect-go-example
cd connect-go-example
go mod init example
go install github.com/bufbuild/buf/cmd/buf@latest
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
島袋恵島袋恵

環境変数にパスを追加する

[ -n "$(go env GOBIN)" ] && export PATH="$(go env GOBIN):${PATH}"
[ -n "$(go env GOPATH)" ] && export PATH="$(go env GOPATH)/bin:${PATH}"

-n はシェルスクリプト内で使用される条件テストオプション。具体的には、-n は与えられた文字列が空でない場合に真(true)を返す

島袋恵島袋恵

Protocol Buffer でサービスを定義する

mkdir -p greet/v1
touch greet/v1/greet.proto

greet/v1/greet.proto 編集する。リクエストとレスポンスとサービスが定義されてるっぽい

syntax = "proto3";

package greet.v1;

option go_package = "example/gen/greet/v1;greetv1";

message GreetRequest {
  string name = 1;
}

message GreetResponse {
  string greeting = 1;
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}
島袋恵島袋恵

コード生成するのに、Bufというツールを使うらいしい

We're going to generate our code using Buf,

島袋恵島袋恵

設定ファイルが整ったら、スキーマをリントしてコードを生成することができます。

buf lint
buf generate
島袋恵島袋恵

buf generate したら以下のファイルが生成されてた。

gen/greet/v1/greetv1connect/greet.connect.go
gen/greet/v1/greet.pb.go

gen
└── greet
    └── v1
        ├── greet.pb.go
        └── greetv1connect
            └── greet.connect.go
島袋恵島袋恵

生成されたコードには、まだGreetのロジックは実装されていない。GreetServiceHandler インターフェースを満たすように Greet のロジックを実装する。

// GreetServiceHandler is an implementation of the greet.v1.GreetService service.
type GreetServiceHandler interface {
	Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error)
}
`cmd/server/main.go`
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"

	"connectrpc.com/connect"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"

	greetv1 "example/gen/greet/v1"        // generated by protoc-gen-go
	"example/gen/greet/v1/greetv1connect" // generated by protoc-gen-connect-go
)

type GreetServer struct{}

func (s *GreetServer) Greet(
	ctx context.Context,
	req *connect.Request[greetv1.GreetRequest],
) (*connect.Response[greetv1.GreetResponse], error) {
	// ログ出力
	log.Println("Request headers: ", req.Header())
	// レスポンスを生成
	res := connect.NewResponse(&greetv1.GreetResponse{
		Greeting: fmt.Sprintf("Hello, %s!", req.Msg.Name),
	})
	// レスポンスヘッダーの設定:
	res.Header().Set("Greet-Version", "v1")
	return res, nil
}

func main() {
	greeter := &GreetServer{}
	mux := http.NewServeMux()
	path, handler := greetv1connect.NewGreetServiceHandler(greeter)
	mux.Handle(path, handler)
	http.ListenAndServe(
		"localhost:8080",
		// Use h2c so we can serve HTTP/2 without TLS.
		h2c.NewHandler(mux, &http2.Server{}),
	)
}
島袋恵島袋恵

サーバーを起動する。

$ go run ./cmd/server/main.go

リクエストする

curl \
    --header "Content-Type: application/json" \
    --data '{"name": "Jane"}' \
    http://localhost:8080/greet.v1.GreetService/Greet

レスポンス

{"greeting":"Hello, Jane!"}%
島袋恵島袋恵

buf generate 実行して生成されたクライアントを使ってリクエストすることもできる。

`cmd/client/main.go`
package main

import (
    "context"
    "log"
    "net/http"

    greetv1 "example/gen/greet/v1"
    "example/gen/greet/v1/greetv1connect"

    "connectrpc.com/connect"
)

func main() {
    client := greetv1connect.NewGreetServiceClient(
        http.DefaultClient,
        "http://localhost:8080",
    )
    res, err := client.Greet(
        context.Background(),
        connect.NewRequest(&greetv1.GreetRequest{Name: "Jane"}),
    )
    if err != nil {
        log.Println(err)
        return
    }
    log.Println(res.Msg.Greeting)
}
$ go run ./cmd/client/main.go
2023/09/16 17:42:53 Hello, Jane!
島袋恵島袋恵

コネクトとは?

  • gRPC互換のHTTP APIを構築するライブラリ。短い protocol buffer スキーマを定義して、コード生成して、アプリケーションロジックを実装するだけで、Connect が堅安全のクラアントを生成してくれたり、他にもいろいろ便利なことをやってくれてる。

  • gRPCサーバー/クライアントフレームワーク

3つのプロトコルをサポートしてる

gRPCプロトコル

これは、gRPCエコシステム全体で広く利用されているプロトコルです。connect-goは、他のgRPCの実装と簡単に互換性を持たせることができます。

gRPC-Webプロトコル

これは、特にWebのフロントエンドとバックエンド間の通信を効率的にするために設計されたプロトコルです。connect-goは、特定の中継プロキシを必要とせずに、grpc-webのフロントエンドと連携することができます。

Connectプロトコル

これは新しいプロトコルで、HTTPベースのシンプルな設計になっています。gRPCとgRPC-Webの優れた機能を組み込んでおり、さまざまなシステムや環境で一貫して動作します。

島袋恵島袋恵

So what?

  • 少量のコードで効率的にAPIサーバーを構築できる。
  • 特にgRPCやConnectのようなプロトコルを使用すると、多くの通常のRESTfulなサービスで必要な作業(URLの設計やマーシャリングなど)を大幅に減少させることができる。
  • このアプローチの利点として、エンドユーザーには、開発者の追加の労力なしで、標準的で安全なクライアントが提供できる。
島袋恵島袋恵

コード生成される感じが、graphql codegen の server preset と雰囲気似てるなと思った。そもそも gRPCを触ったことがない & つかいどころがよくわからんって感じなので、またいつか触る時が来たら思い出そう。