encore icon indicating copy to clipboard operation
encore copied to clipboard

How to use custom validations in encore.ts

Open criskell opened this issue 1 year ago • 6 comments

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>;
};

criskell avatar Dec 11 '24 05:12 criskell

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 avatar Jan 07 '25 15:01 marcuskohlberg

@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

criskell avatar Jan 07 '25 16:01 criskell

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 avatar Jan 08 '25 08:01 marcuskohlberg

@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.

NikitaIT avatar Mar 18 '25 14:03 NikitaIT

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.

gavar avatar Jul 15 '25 14:07 gavar

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;
}

eandre avatar Jul 15 '25 15:07 eandre