You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature Request] Allow certain rules to be run only within certain contexts #486
Feature description:
For an API I am trying to design, I would like to support Create/Patch style requests that have exactly the same payloads, except the Create doesn't require an ID (and should reject the request if provided) and the Patch doesn't have any required fields other than ID.
Problem it solves or use case:
I don't want to have to maintain multiple separate message payloads, and the validation rules on each field across those messages, when the payloads are pretty much identical. There would be a lot of duplication across the validation rules, which can lead to issues down the line.
Proposed implementation or solution:
Allow a way to specify the contexts in which a rule should be applied, and then when you validate a message you can specify the context in which to validate.
A naive, not fully fleshed out, approach could be to create new rules that take the rule plus a list of contexts for which the rule would be applied:
The context ids above are free text, but maybe a hard coded enum, or an int so that user can use a custom defined enum might be better?
Then, when you call validate you could pass a single/list of contexts for which to validate against.
Contribution:
I can try to do some of this stuff once the schema has been defined and approved. I can help implement the python side of things of needed.
Thanks for the request, @jeffsawatzky! Some similar issue related to filtering #112. I see where you're going with contexts but the caller of validation needs to be context-aware, while the solution in #112 (both as a static field mask, or as another field mask field on the message) allows this information to be self-contained on the proto like the rest of the validation rules.
@rodaine the solution in #112 will skip all validation on a field. I just want to skip the required validation on a field, but if the field is provided I want it validated still. What if, instead of the context being provided at the time of the validation call, it was embedded in the proto message definition as a custom option? That way the call site doesn't need to be context-aware. And these custom context options could be combined with ones from child messages. That way my CreateRequest could specify a custom context option of create and my UpdateRequest could specify a custom context option of update and then those contexts are applied to those messages as well as any child messages.
@rodaine basically the solution in #112 runs all validations on a field if specified in a field mask. What I would like is a way to run a subset of validations in certain situations. Specifically the required validation, but if it can be generalized that would be great. The required validation is a bit different than all others because it is checked whether or not the field was specified, whereas all other validations only run when a field is specified.
Also, if there was a way to specify the ctx in a message and then have that provided in custom cel expressions, then that might work too, though I think required still needs a special case since the custom cel expression would only be run when the field is specified.
Maybe I could put the required field validation into a message cel expression and it could get the ctx and then validate the required fields depending on the ctx?
Maybe a simple skip_required option somewhere (I could set it in the Update request) to skip any required fields in the current and child fields would suffice for my needs? But the ctx solution opens up a lot more customization.
Another use case for the ctx solution would be if we wanted to perform validation on server responses too, to make sure that the server is returning valid responses. For example, if I have a field marked with (google.api.field_behavior) = OUTPUT_ONLY then I could create a validation that is only run for the UpdatePersonResponse message to make sure that output field on the Person is set for the response (maybe we include some metadata about the person in the response, like created/updated timestamps)
That way my CreateRequest could specify a custom context option of create and my UpdateRequest could specify a custom context option of update and then those contexts are applied to those messages as well as any child messages.
...
Another use case for the ctx solution would be if we wanted to perform validation on server responses too, to make sure that the server is returning valid responses. For example, if I have a field marked with (google.api.field_behavior) = OUTPUT_ONLY then I could create a validation that is only run for the UpdatePersonResponse message to make sure that output field on the Person is set for the response (maybe we include some metadata about the person in the response, like created/updated timestamps)
From your responses on #112, I suspect you're doing this to match AIP/AEP rules? If so, the response message on many of the standard RPCs is the resource message itself (i.e., the correct form is rpc UpdatePerson(UpdatePersonRequest) returns (Person)). How would you specify the context in those cases if you want to validate the response messages? For what it's worth, we internally perform validation on response messages for bug-detection as well at Buf.
Maybe a simple skip_required option somewhere (I could set it in the Update request) to skip any required fields in the current and child fields would suffice for my needs? But the ctx solution opens up a lot more customization.
There's some nuance here. required itself boils down to msg.has(field) returns true. However, other standard rules have this built-in by construction (said another way, the constraint is a subset of required) that will still apply for implicit-presence fields. For instance, string.uuid requires a particular shape that will always satisfy required. Disabling required in this case is effectively a no-op since an empty string cannot satisfy a UUID regardless. The only time these rules are independent is when a field has explicit presence (composite fields, proto2/3 optional scalar fields, oneof members, and explicit presence fields in editions, which is the default).
Speaking of AIP/AEP, the field mask is required to do partial updates without ambiguity. When provided, the caller is specifying the exact fields that will be updated, and with #112, what fields need validation. This is required for implicit presence fields since there's no difference between unset and a zero value. Adding a context won't help with this distinction either.
No, I'm not using google's AIP, but I do think the update_mask FieldMask is a good idea and only validating what's in the mask.
I am using edition 2024 so all fields (other than map/repeated) have explicit presence by default. So I need to use required to ensure required fields are present, otherwise other validators are skipped. I like the explicit presence because it allows patch style updates without having to generate a field mask (I use python and the field mask helpers are non-existent there)
As for the responses that are the resource itself, I guess you can't really include a "response" ctx in that case. Are there ways to supply the ctx in the service method as an option? Though that would require changes to the connectrpc code generator or something. There's always the option of providing additional ctx at the time of the validate call, or if you need this functionality then wrap your response in a response message (goes against AIP, I know, but does follow other industry best practices like buf's own linter).
Also, I see validation as mainly a server side concern. Sure, clients can validate their requests before sending, but don't need to, and the server should always perform its own validation. So, if we need to supply ctx at the time of the validate call, it could be restricted to the server only, which would be in one spot, and isn't that bad (though I agree it would be nice to encode all this in the proto somehow)
You'd be surprised how many folks use pv for client-side validation! And yes, while protos are definitely strongly associated with RPCs, they have standalone uses outside of client/server interactions too. Protovalidate is agnostic to the usecase.
It would be kind of amazing if (buf.validate.field).ignore = IGNORE_IF_ZERO_VALUE extended into the referenced message like IGNORE_ALWAYS does. Would you be open to another option for ignore that did so since this would probably be a rough breaking change?
Activity
rodaine commentedon May 27, 2026
Thanks for the request, @jeffsawatzky! Some similar issue related to filtering #112. I see where you're going with contexts but the caller of validation needs to be context-aware, while the solution in #112 (both as a static field mask, or as another field mask field on the message) allows this information to be self-contained on the proto like the rest of the validation rules.
jeffsawatzky commentedon May 27, 2026
@rodaine the solution in #112 will skip all validation on a field. I just want to skip the
requiredvalidation on a field, but if the field is provided I want it validated still. What if, instead of the context being provided at the time of the validation call, it was embedded in the proto message definition as a custom option? That way the call site doesn't need to be context-aware. And these custom context options could be combined with ones from child messages. That way myCreateRequestcould specify a custom context option ofcreateand myUpdateRequestcould specify a custom context option ofupdateand then those contexts are applied to those messages as well as any child messages.jeffsawatzky commentedon May 27, 2026
@rodaine so if I were to update my example above, it would now look something like:
jeffsawatzky commentedon May 27, 2026
@rodaine basically the solution in #112 runs all validations on a field if specified in a field mask. What I would like is a way to run a subset of validations in certain situations. Specifically the
requiredvalidation, but if it can be generalized that would be great. Therequiredvalidation is a bit different than all others because it is checked whether or not the field was specified, whereas all other validations only run when a field is specified.Also, if there was a way to specify the
ctxin a message and then have that provided in custom cel expressions, then that might work too, though I thinkrequiredstill needs a special case since the custom cel expression would only be run when the field is specified.Maybe I could put the required field validation into a message cel expression and it could get the
ctxand then validate the required fields depending on thectx?jeffsawatzky commentedon May 27, 2026
Maybe a simple
skip_requiredoption somewhere (I could set it in the Update request) to skip any required fields in the current and child fields would suffice for my needs? But thectxsolution opens up a lot more customization.jeffsawatzky commentedon May 27, 2026
Another use case for the
ctxsolution would be if we wanted to perform validation on server responses too, to make sure that the server is returning valid responses. For example, if I have a field marked with(google.api.field_behavior) = OUTPUT_ONLYthen I could create a validation that is only run for theUpdatePersonResponsemessage to make sure that output field on thePersonis set for the response (maybe we include some metadata about the person in the response, like created/updated timestamps)rodaine commentedon May 28, 2026
From your responses on #112, I suspect you're doing this to match AIP/AEP rules? If so, the response message on many of the standard RPCs is the resource message itself (i.e., the correct form is
rpc UpdatePerson(UpdatePersonRequest) returns (Person)). How would you specify the context in those cases if you want to validate the response messages? For what it's worth, we internally perform validation on response messages for bug-detection as well at Buf.There's some nuance here.
requireditself boils down tomsg.has(field)returns true. However, other standard rules have this built-in by construction (said another way, the constraint is a subset ofrequired) that will still apply for implicit-presence fields. For instance,string.uuidrequires a particular shape that will always satisfyrequired. Disablingrequiredin this case is effectively a no-op since an empty string cannot satisfy a UUID regardless. The only time these rules are independent is when a field has explicit presence (composite fields, proto2/3 optional scalar fields, oneof members, and explicit presence fields in editions, which is the default).Speaking of AIP/AEP, the field mask is required to do partial updates without ambiguity. When provided, the caller is specifying the exact fields that will be updated, and with #112, what fields need validation. This is required for implicit presence fields since there's no difference between unset and a zero value. Adding a context won't help with this distinction either.
jeffsawatzky commentedon May 28, 2026
No, I'm not using google's AIP, but I do think the update_mask FieldMask is a good idea and only validating what's in the mask.
I am using edition 2024 so all fields (other than map/repeated) have explicit presence by default. So I need to use required to ensure required fields are present, otherwise other validators are skipped. I like the explicit presence because it allows patch style updates without having to generate a field mask (I use python and the field mask helpers are non-existent there)
As for the responses that are the resource itself, I guess you can't really include a "response" ctx in that case. Are there ways to supply the ctx in the service method as an option? Though that would require changes to the connectrpc code generator or something. There's always the option of providing additional ctx at the time of the validate call, or if you need this functionality then wrap your response in a response message (goes against AIP, I know, but does follow other industry best practices like buf's own linter).
jeffsawatzky commentedon May 28, 2026
Also, I see validation as mainly a server side concern. Sure, clients can validate their requests before sending, but don't need to, and the server should always perform its own validation. So, if we need to supply ctx at the time of the validate call, it could be restricted to the server only, which would be in one spot, and isn't that bad (though I agree it would be nice to encode all this in the proto somehow)
rodaine commentedon May 28, 2026
You'd be surprised how many folks use pv for client-side validation! And yes, while protos are definitely strongly associated with RPCs, they have standalone uses outside of client/server interactions too. Protovalidate is agnostic to the usecase.
gpoirier-cw commentedon Sep 28, 2026
It would be kind of amazing if
(buf.validate.field).ignore = IGNORE_IF_ZERO_VALUEextended into the referenced message likeIGNORE_ALWAYSdoes. Would you be open to another option forignorethat did so since this would probably be a rough breaking change?