LoginSignup
1
3

golang ✕ gRPCのprotoでルールベースValidateを行う [gRPC ✕ golangをDRYに Part.3 Final]

Last updated at Posted at 2023-05-29

読者ターゲット

  • 株式会社viviONにちょっと興味ある
  • golang ✕ gRPCで毎回Validateのロジック書いてる方
  • gRPCちょっとわかるよって方
  • protoちょっとわかるよって方

課題感

毎回golangのvalidateロジックを書くの、疲れませんか?

golangのValidationといえばPackage validatorだと思います。
(v10まで出てますし、老舗感ありますよね。)

gRPCのRPCに対して毎回個別のvalidator structを用意して、開発するのって結構しんどいんですよね。。。

jsonのお取り扱いだったらzodも似たように先にvalidator objを作って検証するんですけど、json書くのとstruct書くのって全然工数違ってくるんですよね。。。 (疲弊感)

解決しよう

折角protoファイル書いてるんだからスキーマ定義段階で事前にスキーマにカスタムパラメータを付与することで、回避できないか?
と考え始めました。

同じことを考えた先人たちは素晴らしいことに、既に用意してくれていました。
そのOSSはPGV (protoc-gen-validate)です。
(※Buf Technologies, Inc.の製品ばかりご紹介していますが回し者ではないです。)

例えば特定のstring型のスキーマを必須項目にしたい場合

min_len = 1とすることにより、「最低1文字以上の文字列を入力する必要がある」となります。
その為、擬似的にrequired = trueとすることが可能になります。

syntax = "proto3";

package greet.v1;

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

import "validate/validate.proto"; // ココ重要 ☆

message GreetRequest {
  string name = 1 [(validate.rules).string.min_len = 1]; // ココ重要 ☆
}

message GreetResponse {
  string greeting = 1;
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}

packageをprotoに認識させる

公式Docにも書いてるんですが、若干わかりにくいので書いておきます。

Install

pathがenvoyproxyになっているのはまだ、go側がmodule pathの変更に時間がかかってるそうです ><
(※新しくpathを切っただけだと、旧と新の2つのmoduleが存在してしまう為だと思います。)

go get -d github.com/envoyproxy/protoc-gen-validate

Buf config

※ Bufについて知りたい方はこちら!!

buf.yaml
version: v1
deps:
  - buf.build/envoyproxy/protoc-gen-validate ## ココ重要 ☆
breaking:
  use:
    - FILE
lint:
  use:
    - DEFAULT
buf.gen.yaml
version: v1
plugins:
  - plugin: go
    out: gen
    opt: paths=source_relative
  - plugin: connect-go ## connect-goでも通常のgRPCでも実現できるのでconnect-goを使わない場合は不要です
    out: gen
    opt: paths=source_relative
  - plugin: buf.build/bufbuild/validate-go ## ココ重要 ☆
    out: gen
    opt: paths=source_relative

buf generateコマンドでvalidateメソッドが実装されたRPCのリクエスト structが生成されます。

ValidationをDRYにする

生成されたprotobufには既に.Validate()メソッドが実装されたリクエスト structが生成されているので、各RPCで以下を書いてもいいのですが、記述量が少なくなったとはいえ毎回ロジックを書かなければいけない世界線からは開放されません。

if err := req.Validate(); err != nil {

毎回コードを書かずに自動Validationを行う方法

gRPCオフィシャルのValidator moduleを使用することで、リクエスト structに実装されている.Validate()関数の存在チェックを行い勝手にCallしてくれます。

Interceptorで実装されている為、各RPCで記述しなくてもすべてのRPC Callに対して自動的にValidateをかけてくれるようになります。

connect-goでも独自にこんな風にカスタムInterceptorを実装することで、自動Validateが可能になります。

まとめ

第三部作で書いてきた「gRPCを取り巻く環境はどんどん進化していた」ですが、如何でしたでしょうか?

2,3年前にはなかった「これが欲しかった」がどんどん増えてきており、コミッター達には感謝してもしきれないです。

採用PR

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3