Skip to content

Validating Data

Use the ELPS schema package to check the type and shape of a value before your code uses it.

The schema package checks that a value has the type and shape your code expects. Use it at the edge of a phylum, on each request and on each value you read from storage, so that the rest of the code can trust its data.

The package name is s. Write each name with the prefix, for example s:validate. Do not (use-package 's): some of its names, such as s:list and s:when, then hide the core forms with the same names.

The schema package is in every phylum. Every example on this page runs in your browser. Edit an example to see the new result.


Why validate

A request body is JSON from outside the phylum. A field can be missing, have the wrong type or hold a value your business rules do not allow. Without a check, the error shows up later, far from its cause, or bad data goes into the ledger.

A validator states the rules in one place. s:validate returns () when the value passes, and raises an error that names the field when it does not.

Change "GBP" to "JPY", or remove the amount field, to see the error.


Make a validator

(s:make-validator name type constraint...)
  • name is a string. It appears in error messages.
  • type is one of the built-in types below, or another validator.
  • Each constraint adds a rule. A value must pass all of them.
  • s:make-validator returns the validator. Bind it to a name with set.

(s:validate validator value) checks a value. (s:is? value validator) returns true or false in place of an error, so you can use it in a condition:

(s:as value validator) returns the value when it passes, and raises the error when it does not.


Types

Type Passes
s:int An integer
s:float A floating-point number
s:number Any number
s:string A string
s:bool The symbols true and false, not the strings "true" and "false"
s:bytes A byte sequence
s:sorted-map A sorted map, for example a JSON object
s:array An array, for example a JSON array
s:list A list. () is the empty list
s:sequence A list or an array
s:nil (), for a field that can be empty
s:fun A function
s:error An error value
s:tagged-value A value of a user-defined type
s:any Any value

Constraints

Constraint The value must
(s:in v1 v2 ...) Be equal to one of the values
(s:regexp pattern) Match the regular expression (Go syntax)
(s:gt n), (s:gte n), (s:lt n), (s:lte n) Be greater than, at least, less than, or at most n
(s:positive), (s:negative) Be more than zero, or less than zero
(s:len n) Have length n
(s:lengt n), (s:lengte n), (s:lenlt n), (s:lenlte n) Have a length more than, at least, less than, or at most n
(s:of type) Be a list or array whose every item passes type
(s:map-of key-type value-type) Be a sorted map whose every key and value pass the types
(s:is-true), (s:is-false) Be the symbol true, or the symbol false
(s:is-truthy), (s:is-falsy) Count as true, or as false
(s:not constraint) Fail the constraint

A constraint can also stand alone as the type of a key: (s:has-key "currency" (s:in "GBP" "EUR")). Write constraints and validator names without a quote: (s:gt 1) and (s:has-key "age" age), not '(s:gt 1) or 'age.


Maps and JSON objects

These constraints check the keys of a sorted map. The keys are strings, as in a map from json:load-string.

Constraint Rule
(s:has-key "k" type...) The key must be present, and its value must pass one of the types
(s:may-have-key "k" type...) If the key is present, its value must pass one of the types
(s:no-other-keys key-constraint...) The map must have no keys other than the ones the key constraints name
(s:when "k" constraint "k2" constraint2...) When the value of k passes constraint, the value of k2 must pass constraint2

Each type after the key is an alternative: the value must pass one of them. So (s:has-key "email" s:string s:nil) accepts a string or null. To add rules to a key, make a validator with the rules and give it as the type. This is also how you build a large schema from small ones.

A JSON number is a float in ELPS, also when it has no fraction. Use s:number, not s:int, for a number that comes from JSON.

Set age to 16 to see the s:when rule fail, or add a key that is not in the list to see s:no-other-keys fail.


Handle a failed check

A check that fails raises one of two errors:

Error Cause
wrong-type The value does not have the type, or the value of a key does not pass its validator
failed-constraint A constraint on the value fails, for example a key is missing

The error message names the key that failed.

A mistake in the validator itself, for example a constraint with the wrong arguments, raises bad-arguments. To turn a failed check into a response, catch only wrong-type and failed-constraint, so that a mistake in your own validator is not hidden:


Validate a user-defined type

Pass a type that deftype made in place of the name. The validator then checks the value inside the tagged value. See User-Defined Types.


Next

A validator checks data. To keep data and the functions that change it together, with the checks run on every change, use a record type.

We use cookies to give you the best experience of using this website. By continuing to use this site, you accept our use of cookies. Please read our Cookie Policy for more information.