proto3 dropped the required keyword, and editions did not bring it back except as a legacy feature for proto2 files that already use it. Required fields get in the way of schema evolution. Every message has to include a required field. If you stop sending it, every older reader fails to decode the new message, and there is no way to retire the field gradually. Protobuf's own maintainers came to see required as a mistake, and the designers of Cap'n Proto reached the same conclusion.
You can still require a field. The check happens in validation now, which can change without breaking the wire format. Tightening a rule can still reject requests that existing clients send, so roll it out like any other API change. In application code, generated getters return the zero value for a missing field and your handler checks for it. In the schema, a custom option declares the field required and a validator enforces it when a message is checked rather than when it is decoded, so the field stays wire compatible. protovalidate's required rule is the common choice, and since the mechanism is just options, you can define and check your own the same way.
import "buf/validate/validate.proto"; message CreateUserRequest { // Required at the validation layer // but optional at the wire layer. string email = 1 [ (buf.validate.field).string.email = true, (buf.validate.field).required = true ]; }
// Safe access even if req is nil if req.GetEmail() == "" { return status.Error(codes.InvalidArgument, "email is required") }
Further Reading
- Protobuf Tip #8: Never use required
The history of proto2's required label, and why Buf recommends never using it.
Next
Size Limits
Practical size limits for Protobuf messages: the 2GB hard ceiling, recursion and memory behavior, and how to design schemas for large payloads.