How validation works in Base
The Verify decorator family that checks your inputs at the edge before your logic ever sees them.
How validation works in Base
Data from outside your worker is a promise, not a fact. A request body claims to have an email; a GraphQL argument claims to be a number in range. Validation is how Base turns those claims into something your handler can trust. You describe what each field must be, and Base checks incoming data against that description before your logic ever sees it.
You validate by describing the field
Validation in Base is declarative. You do not write a function that walks the input and checks it. You decorate the fields of a class with rules, and the class becomes its own specification of what valid data looks like.
The rules come from the Verify* family, and they read like plain statements about the field:
A reader, and the compiler, can see at a glance what this data must be: a non-empty name, a real email, an age no greater than 120. The description lives on the fields, so it cannot drift away from the type it guards.
The rules read like a sentence
The family is large and named so you can guess it. Presence and type: @VerifyIsNotEmpty, @VerifyIsDefined, @VerifyIsString, @VerifyIsNumber, @VerifyIsBoolean, @VerifyIsArray. Formats: @VerifyIsEmail, @VerifyIsUUID, @VerifyIsUrl, @VerifyIsIP, @VerifyIsDomain, @VerifyIsPhoneNumber. Bounds: @VerifyMin, @VerifyMax, @VerifyMinLength, @VerifyMaxLength, @VerifyLength. There are more, for dates, enums, country codes, time zones, and array sizes, but you do not learn them as a list. You reach for the one whose name says what you mean.
When the rule you need is not in the family, you are not stuck. Base lets you register your own rule and apply it with the same decorator style, so a project-specific check looks like a first-class part of the vocabulary rather than a special case bolted on.
Where validation runs
The point of declaring rules on a class is that Base enforces them at the boundary. When incoming data is received as one of these classes, on a route or as a GraphQL argument, the rules run before your handler does. Data that fails is rejected with a reason; data that reaches your code has already passed. Your handler does not start with a defensive wall of checks, because the wall is the class definition, and the framework ran it for you.
This is why validation pairs so naturally with the rest of Base. The same class that is a GraphQL input type or a route's body can carry its validation rules on the very same fields. One class says what the data is shaped like and what it must satisfy, and each subsystem reads the part it cares about.
The mental model to keep
Validation is a description, not a procedure. You decorate a field with the rule it must satisfy, Base runs the rules at the edge, and your handler only ever sees data that passed. The Verify* family covers the common cases by name, and VerifyBy lets you add your own. The data your logic works with is trustworthy because the framework checked the promise before letting it in.