Skip to content

Using Protovalidate standard rules#

Once you've added Protovalidate to your project, you're ready to begin adding validation rules to your Protobuf files. On this page, you'll learn how rules are defined and explore the standard rules for scalar types, oneofs, maps, enums, and even entire messages.

Protovalidate rules#

Protovalidate rules are Protobuf option annotations representing validation rules and business requirements. When a message is validated, every rule must pass for it to be considered valid.

All Protovalidate rules are defined in the Common Expression Language (CEL). Protovalidate's built-in rules provide a shorthand syntax for dozens of common validation rules, and you're free to examine their CEL definitions within the API itself.

On this page, we'll explore Protovalidate's built-in rules, starting with simple scalar fields.

Field rules#

Field rules are the simplest and most commonly used Protovalidate rules. A field rule applies one requirement to one field in a message.

For example, you can require that first_name meet a minimum length:

Simple field rule
message User {
    string first_name = 1 [
        (buf.validate.field).string.min_len = 1
    ];
}

Multiple rules#

You can combine field rules to express a complete set of requirements:

Multiple field rules
message User {
    string first_name = 1 [
        (buf.validate.field).string.min_len = 1,
        (buf.validate.field).string.max_len = 50
    ];
}

You can avoid repetition with message literal syntax:

Multiple field rules with message literal syntax
message User {
    string name = 1 [
        (buf.validate.field).string = {
          min_len: 1,
          max_len: 100,
          not_in: ["bot", "agent"]
        }
    ];
}

Scalar (simple) rules#

Scalar fields—bool, string, bytes, and numeric types—are the simplest to validate. Protovalidate's standard rules handle most common validation requirements, including regular expression matching.

Scalar rule examples
message User {
    string name = 1 [
        (buf.validate.field).string.min_len = 1,
        (buf.validate.field).string.max_len = 100
    ];
    string email = 2 [(buf.validate.field).string.email = true];
    bool verified = 3 [(buf.validate.field).bool.const = true];
    bytes password = 4 [(buf.validate.field).bytes.pattern = "^[a-zA-Z0-9]*$"];
}

View reference documentation for each scalar type's rules:

Enum rules#

Protovalidate provides validation rules for enum types, allowing you to validate that a message's value is within the defined values (defined_only), in a set of values (in), not within a set of values (not_in), and more.

Enum rule example
message Order {
    enum Status {
        STATUS_UNSPECIFIED = 0;
        STATUS_PENDING = 1;
        STATUS_PROCESSING = 2;
        STATUS_SHIPPED = 3;
        STATUS_CANCELED = 4;
    }

    // `status` should only allow values within the Status enum.
    Status status = 1 [
        (buf.validate.field).enum.defined_only = true
    ];
}

View reference documentation for enum rules.

Repeated rules#

Repeated fields can be validated for minimum and maximum length, uniqueness, and even have their contents validated. In this example, a repeated field must meet a minimum number of items, and each item must meet a set of string rules:

Repeated rule example
message RepeatedExample {
    repeated string terms = 1 [
        (buf.validate.field).repeated.min_items = 1,
        (buf.validate.field).repeated.items = {
            string: {
                min_len: 5
                max_len: 20
            }
        }
    ];
}

View reference documentation for repeated rules.

Map rules#

Protovalidate's map rules provide common map validation tasks. You can validate minimum or maximum numbers of key-value pairs and even express sets of rules applied to keys and values.

In this example, each value in a map must meet a minimum and maximum length, and the map must have at least one key-value pair:

Map rule example
message MapExample {
    map<string, string> terms = 1 [
        (buf.validate.field).map.min_pairs = 1,
        (buf.validate.field).map.values = {
            string: {
                min_len: 5
                max_len: 20
            }
        }
    ];
}

View reference documentation for map rules.

Oneof rules#

Protovalidate provides a single oneof rule: required. It states that exactly one field in the oneof must be set. Note that fields within the oneof may have their own rules applied.

In this example, a or b must be set, but any a value must be non-empty:

Oneof rule example
message OneofExample {
    oneof value {
        // Exactly one of `a` or `b` must be set. If `a` is set, it must also be
        // non-empty; whereas if `b` is set, it can still be an empty string.
        option (buf.validate.oneof).required = true;
        string a = 1 [(buf.validate.field).string.min_len = 1];
        string b = 2;
    }
}

As an alternative to the Protobuf oneof construct, Protovalidate provides a message rule that ensures that only one of a set of fields is set.

View reference documentation for oneof rules.

Well-Known Type rules#

Protovalidate provides standard rules for Protobuf's Well-Known Types such as Any, Duration, and Timestamp:

Well-Known Type rules
message Event {
    google.protobuf.Any data = 1 [
        (buf.validate.field).any = {
            in: ["type.googleapis.com/MyType1"]
        }
    ];
    google.protobuf.Duration duration = 2 [
        (buf.validate.field).duration.gte = {
            seconds: 1
        },
        (buf.validate.field).duration.lte = {
            seconds: 3600
        }
    ];
    google.protobuf.Timestamp timestamp = 3 [
        (buf.validate.field).timestamp.lte = {
            seconds: 1735689600 // 2025-01-01T00:00:00Z
        }
    ];
}

View reference documentation for each well-known type's rules:

Message rules#

Message rules are validation constraints that apply to entire Protobuf messages. They're especially useful when you need to implement validation rules that need to work with multiple fields in CEL expressions. Message rules use the buf.validate.message option, applied to the message itself.

disabled#

The disabled rule causes the entire message to be ignored by Protovalidate:

Disabling validation for a message
message DisableValidationMessage {
    option (buf.validate.message).disabled = true;
}

View reference documentation for disabled.

cel#

The cel rule provides the foundation for custom validation rules capable of evaluating multiple fields within a message:

Message-level CEL rule example
message CelRuleExample {
    // `first_name + last_name` cannot exceed 100 characters in length.
    option (buf.validate.message).cel = {
        id: "name.total.length",
        message: "first_name and last_name, combined, cannot exceed 100 characters"
        expression: "this.first_name.size() + this.last_name.size() < 100"
    };

    string first_name = 1;
    string last_name = 2;
}

View reference documentation for cel.

oneof#

The oneof message rule provides a validation-based alternative to Protobuf's oneof construct, allowing you to enforce that at most one (or exactly one) field from a specified set is populated without the code generation and compatibility limitations of traditional oneof fields.

This rule is particularly useful when you need oneof-like behavior but want to avoid the ergonomic issues with generated code, use oneof behavior with repeated or map fields, honor implicit presence, or need to maintain backwards compatibility in your schema evolution.

Configuration#

  • fields (repeated string): A list of field names that should be treated as mutually exclusive.
  • required (bool): When true, requires one field to be set. When false (default), allows at most one field to be set.

Examples#

Allow at most one field to be set:

import "buf/validate/validate.proto";

message UserRef {
  option (buf.validate.message).oneof = { fields: ["id", "name"] };
  string id = 1;
  string name = 2;
}

Require one field to be set:

import "buf/validate/validate.proto";

message UserRef {
  option (buf.validate.message).oneof = { fields: ["id", "name"], required: true };
  string id = 1;
  string name = 2;
}

View reference documentation for the oneof message rule. For rules that apply to oneof fields, see oneof rules.

Nested messages#

Protovalidate validates the entire message, including nested messages.

In the following example, Person contains an Address, each requiring all fields. Because no postal_code is provided for the address, the entire Person message is invalid.

Ignoring#

You can ignore nested messages by adding the ignore rule using a value from the Ignore enum.

message Person {
    string name = 1 [(buf.validate.field).required = true];
-    Address address = 2 [(buf.validate.field).required = true];
+    Address address = 2 [
+        (buf.validate.field).ignore = IGNORE_ALWAYS
+    ];
}

View reference documentation for the ignore enum.

Next steps#

  • Learn to validate domain logic with CEL-based custom rules.