ensure for data that arrives at runtime.
Constraints on types
In Forst, types can declare exactly what makes data valid. You attach constraints directly to types. The compiler checks those rules when values are known at compile time. Dynamic request data must be validated withensure before you use it.
- Forst
- Generated Go
- Generated TypeScript
placeOrder receives a literal that already satisfies the constraints,
the compiler can prove that at compile time. For request bodies and other
dynamic input, write ensure before business logic so invalid SKUs and
quantities never reach your catalog.
Forst also lets you define your own rules for types with
type guards.
Built-in constraints
Constraints appear in two places:
The same built-in names work in both. For
Result narrowing (Ok, Err) and
pointer checks (Nil, Present), see
Check a value.
Chaining
Constraints chain with dots on the base type. All chained rules must pass:- Forst
- Generated Go
Reference
String.Min and Max limit length by Unicode character count. That matches
JSON Schema minLength and maxLength. Use MinBytes and MaxBytes when you
need a UTF-8 size limit, such as a header or payload cap.
Use constraints on struct fields, function parameters, and nested shapes.
Validation follows the data structure, including deeply nested records.
Nested constraints on an order input, including tags on the payload:
- Forst
- Generated Go
Ok()/Err():Resultdiscriminants; use inensure order is Ok()(Check a value)Value(…): literal refinement in assertion types (advanced)
Structural shapes
Forst shapes use field names (camelCase by convention) and compose like TypeScript interfaces:- Forst
- Generated Go
ensure.
You do not re-walk the tree by hand in application code.
Caveats
Constraints stay small and composable
Built-in constraints are a fixed set of simple checks: length, bounds, prefixes, suffixes, and a few similar rules. You combine them with dots (all must pass) andor (one must pass). That keeps types readable and lets the
compiler reuse the same rules for ensure, JSON Schema, and generated clients.
Forst intentionally omits a regex constraint on String. Free regular
expressions can express almost anything, which replaces clear, composable rules
with opaque patterns. Build your formats from simple built-in constraints
instead:
ensure username is Username.
For a closed list of allowed values, use a
literal union such as
"todo" | "done". For richer domain rules, write a
type guard. If you need a true regular expression, put
it in an ordinary function. That validates at runtime. It does not become part
of the type’s built-in constraint chain.
Write ensure to check values at runtime
A constraint on a type records the rule. Runtime checks happen when you write
ensure. Put those checks in handlers that take untrusted input, before
the rest of the function uses the value. See
Check a value.
The compiler only proves values it can see
If the value is a literal in your source, the compiler can prove the constraint when you compile. Request bodies, query params, and other input from outside the program are unknown until the function runs. Writeensure at that
boundary so invalid data never reaches your logic.
Export fields when you serve JSON
Generated Go structs keep their fields private and omitjson tags. In-process
Go can use those structs as they are. If you marshal the shape to JSON for
HTTP, export the fields. Pass -export-struct-fields on forst run or
forst build. You can also set compiler.exportStructFields to true in
ftconfig.json.
Generated TypeScript types are structural
forst generate emits TypeScript types that match field names and shapes.
Constraints such as String.Min(1) stay on the Forst side. If you need the
same checks in the browser or in Node before you call the server, write them in
TypeScript. Calling the Forst server runs the checks you wrote with ensure.
See Generate client types § Caveats.
What generated Go looks like
Generated Go turnsensure checks in the function body into plain
comparisons: String.Min(1) becomes a utf8.RuneCountInString comparison,
String.MaxBytes(64) a len comparison, Int.Min(1) a numeric comparison,
and HasPrefix / HasSuffix / Contains call strings.* with an auto-added
import.
Examples
See the shape and constraint examples on GitHub:Related
Check a value
Assert invariants inside functions with ensure … is ….
Name a reusable rule
Reusable custom predicates on your domain types.