Skip to content

Pass empty JSON arrays to gRPC as empty slices instead of nil #5652

@zhangcz828

Description

@zhangcz828

I'm encountering the same problem described in #1673 and would like to provide more context and a concrete proposal.

Problem:
Given a proto like:

syntax = "proto3";

package example;

import "google/protobuf/empty.proto";
import "google/api/annotations.proto";

option go_package = "examplepb";

// Request message with a repeated string field
message MyRequest {
  repeated string items = 1;
}

// Service definition with HTTP annotation
service MyService {
  rpc SendItems(MyRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      patch: "/v1/items"
      body: "*"
    };
  }
}

When sending a request with an empty array in JSON (e.g., curl -X PATCH -d '{"items":[]}' localhost:7777/v1/items), the generated Go gRPC handler receives nil for the items field, not an empty slice. This is not due to sending null, but specifically an empty array.

Constraints:

  • Wrapping the field in a message is not an option, as the proto definitions are already published and used for OpenAPI generation.
  • Customizing the gateway marshaler does not help, since the protobuf encoding treats both nil and empty slices as nil in the generated Go code, so the handler cannot distinguish between omitted and explicitly empty arrays.

Proposal:

  1. Use a custom runtime.WithMetadata option in grpc-gateway. When the HTTP request reaches the gateway, parse the body and record the paths of empty array fields (including nested fields) in the context metadata.

  2. Add a gRPC interceptor that reads the x-empty-fields metadata, and uses reflection to set the corresponding fields in the request struct to their type's empty value (e.g., set a string slice to []string{}).

Question:
Would this approach to handling "nullable" or explicitly empty repeated fields be acceptable for inclusion in this repo, so that other projects can benefit from it? Feedback and suggestions are welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions