How to use custom validations in encore.ts
How can I use my own validation rules based on the ideas in this PR? https://github.com/encoredev/encore/pull/1621
What do you think about this way?
declare const customValidator: (input: string) => boolean;
type X = {
y: Min<6>;
z: CustomRule<typeof customValidator>;
};
Hey, great catch we had missed publishing the docs for this new feature. Fixed now. Here you go: https://encore.dev/docs/ts/primitives/validation#value-based-validation-rules
@marcuskohlberg Actually, I was talking about custom validations, where I can pass a custom function at the type level, when the existing validations are not suitable for my use case.
For example, in the deepkit framework this is possible: https://github.com/deepkit/deepkit-framework/blob/fb1263dbd9fbaae0bd9ecbf40f840698e4523e0c/website/src/pages/documentation/runtime-types/validation.md?plain=1#L316-L329
Ah, makes sense. You can create custom validation by composing the built-in rules in the type definition: https://encore.dev/docs/ts/primitives/validation#combining-rules
For custom rules beyond what's possible using the built-in rules, you can do this yourself inside the endpoint handler.
For context, is there a use case where the built-in rules aren't sufficient?
@marcuskohlberg simple case is to validate that incoming type is int or int64 (bigint) or byte not just "double" between 0 and 255. I never saw double (number) used as "double" in any production code. Also uuid validation. If it possible it would be nice to have some validators for all Rust types or any other type system, because if in db byte is byte we need to typecheck it on the boundary. Also almost all existed validator compilers allows us to "refine", "coerce" or do some encoder/decoder things.
Last, but not least, dto schemas mostly reused for sql/redis/cassandra validation checks, which is not possible in this case and it leads to redefinition of the schemas with some typescript land validator compiler like typebox. The IO validation it's not just http request/response IO validation.
For context, is there a use case where the built-in rules aren't sufficient?
yes, combining rules is not scalable. For example, I have to receive API requests from an external system that follows the regex like ^\d{1,18}(\.\d{1,12})?$. I don't want to copy paste it over in all places, but rather have something like:
type MyValidation = MatchesRegexp<^\d{1,18}(\.\d{1,12})?$>
and later use it like
interface MyObject {
amount: string | MyValidation;
}
I regret Encore is so opinionated on how we should structure validations. I understand it's all for better performance, but maintainability is an important aspect as well.
That should work fine, but the syntax is string & MyValidation not string | MyValidation. For example, this works just fine:
type MyValidation = MatchesRegexp<"^\\d{1,18}(\\.\\d{1,12})?$"> & MaxLen<10>;
interface MyRequestParams {
name: string & MyValidation;
}