RiserBob logoRiserBobv1.0.0
HomeDocsCLIRelease notes

Getting started

  • Introduction
  • Installation
  • Quick start
  • Project structure

Core concepts

  • Routing
  • Request
  • Responses
  • Error handling

Middleware

  • Middleware
  • Authentication
  • Logger and CORS

Data

  • Validation

Structure and tooling

  • Modules
  • CLI

Examples

  • Build a project
  • Mongoose
  • Drizzle ORM
  • Sequelize
  • JWT and cookies
  • File uploads

Packages

  • Cookies

Resources

  • Troubleshooting
  • Release notes
  • License
  • Security
  • Contributing
Data

Validation

Check incoming data with a simple, readable rule syntax.

Validator.validate(data, schema) checks an object against a schema of rules and returns either the data or a list of errors per field.

TypeScript
import { Validator } from 'riser-bob' const result = Validator.validate(  { name: 'Zen', age: 17, email: 'zen@example.com' },  {    name: ['required', 'string'],    age: ['required', 'number', 'min:13'],    email: ['required', 'email'],  }) if (!result.valid) {  console.log(result.errors) // { field: ['message', ...] }}

The result is either { valid: true, data, errors: {} } or { valid: false, errors }. Pass a type to get a typed data: Validator.validate<CreateUserInput>(body, CreateUserRule).

Rules

RuleDescription
requiredValue must be present and not empty.
nullablenull is allowed. Cannot be combined with required.
string / number / booleanType checks. Numbers must be finite.
emailMust be a valid email address.
confirmedMust match the <field>Confirmation value.
min:n / max:nNumeric limit for numbers, length limit for strings.
minLength:n / maxLength:nString length limits. The limit must be an integer.
Fields without required are optional: missing values and empty strings are skipped.
  • When a type rule fails, the remaining rules of that field are skipped, so you get one clear message instead of a pile of them.
  • Fields that are not in the schema are left in data untouched. Pick the fields you want explicitly before saving, instead of passing the whole body to your database.

Error messages

RuleMessage
requiredname is required
stringname must be a string
numberage must be a number
booleanisCompleted must be a boolean
emailemail must be a valid email
confirmedpassword confirmation does not match
min:13 on a numberage must be at least 13
max:120 on a numberage may not be greater than 120
min:3 on a stringname must be at least 3 characters
minLength:8password must be at least 8 characters
maxLength:50name may not exceed 50 characters

Keep rules in their own files

The starter project keeps rules in their own files, so handlers stay short. The starter's create.ts is empty on purpose. Fill it with the rules of your feature:

src/modules/rules/create.ts
import type { ValidationSchema } from "riserbob"; export const CreateUserRule = {  name: ["required", "string", "minLength:3", "maxLength:50"],  email: ["required", "email", "maxLength:255"],  password: ["required", "string", "minLength:8", "maxLength:40"],  age: ["nullable", "number", "min:1", "max:120"],} satisfies ValidationSchema;

An update rule usually drops required, so a client can send only the fields it wants to change:

src/modules/rules/update.ts
import type { ValidationSchema } from "riser-bob"; export const UpdateUserRule = {  name: ["string", "minLength:3", "maxLength:50"],  email: ["email", "maxLength:255"],  age: ["nullable", "number", "min:1", "max:120"],} satisfies ValidationSchema;

Use it in a handler

src/modules/users/users.controller.ts
readonly create: SpongHandler = async (spongReq, spongRes) => {  const body = await spongReq.json<Record<string, unknown>>();  const result = Validator.validate(body, CreateUserRule);   if (!result.valid) {    return spongRes.SpongUnprocessableContent({      success: false,      message: "Validation failed",      errors: result.errors,    });  }   // result.data is safe to use from here on  return spongRes.SpongCreated({ success: true, message: "User created" });};
Output
{  "success": false,  "message": "Validation failed",  "errors": {    "name": ["name must be at least 3 characters"],    "email": ["email is required"]  }}

Validating form data

Validation works on a plain object. For a multipart request, build the object from the form first:

TypeScript
const formData = await spongReq.formData();const bio = formData.get("bio"); const result = Validator.validate(  { bio: typeof bio === "string" ? bio : "" },  UpdateProfileRule,);

Invalid schemas

A schema mistake throws a TypeError as soon as validation runs, so it never goes unnoticed. This covers an unknown rule, a rule that needs a limit but has none, a limit that is not a number, and required combined with nullable. Passing something that is not a plain object as the data throws too.

PreviousLogger and CORSNextModules

On this page

  • Rules
  • Error messages
  • Keep rules in their own files
  • Use it in a handler
  • Validating form data
  • Invalid schemas