UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【gRPC】KotlinでFieldMaskで無駄なフィールドを更新しないようにする

gRPCでの更新の際、クライアントからEntity情報をサーバーに渡すが、不要なフィールドは更新しないようにしたい

例えば、以下のようなデータのemailだけ更新したい場合

{
  "user": {
    "id": "001",
    "name": "uga",
    "email": "uga@gmail.com"
  }
}

gRPCでは Protobuf FieldMask という機能を使ってフィルタリングできる

google.aip.dev

方法

まず、protoファイル内のUpdateメソッドのリクエストにgoogle.protobuf.FieldMaskフィールドを追加する

...
import "google/protobuf/field_mask.proto";
...
message UpdateUserRequest {
  User user = 1;
  google.protobuf.FieldMask update_mask = 2;
}

message UpdateUserReply {
  User user = 1;
}

次に、server側では以下のようにする

...
updateUserReply {
    val responseUser = User.newBuilder()
        .setId(request.user.id)
        .setName(request.user.name)
        .setEmail(request.user.email).build()
    var userWithMaskedFields = User.newBuilder()
    FieldMaskUtil.merge(
        request.updateMask,
        responseUser,
        userWithMaskedFields )
    var updateUser = userWithMaskedFields.build()
    // 更新処理
    user = updateUser
}

この時、FieldMaskUtil.merge(FieldMask, User, User.builder)を使って必要最低限のデータにフィルタする

最後に、client側で更新するEntityと一緒に、FieldMaskも送信するようにする

val fieldMask = FieldMask.newBuilder().addPaths("email").build()
val request = updateUserRequest {
    this.user = user; // emailを"uga2@gmail.com"に更新したUserエンティティ
    this.updateMask = fieldMask
}
val response = stub.sayHello(request)
println("Received: ${response.user}") // Received: email: "uga2@gmail.com"

これで、不要なフィールドは更新しないようにすることができた

参考

Practical API Design at Netflix, Part 1: Using Protobuf FieldMask | by Netflix Technology Blog | Netflix TechBlog