intro

Core

The main advantage on using this module is defining validation rules once (backend), but leveraging them also on client side, before final form submission.

Classic example consists on email validation when user registers new profile. User should type an unique email, that client side is hard to validate (specific api should be available to verify uniqueness).

With this module the full set of validation rules for email (and only for it) can be verified, after the user types the new email:

<input v-model="form.email" @change="form.validate('email')" />
<div v-if="form.valid('email')">OK!!</div>

How it works?

This module will:

  • differentiate between precognitive validation requests vs full form subsmissions.
  • specify which form keys need validation in case of precognitive request.

Needs instruction on how:

  • assign errors to each form key.

You will need to 'instruct' it by defining Error Parsers.

Error Parsers

Error parsers are functions that extract ValidationErrors from thrown errors.

type ValidationErrors = Record<string, string | string[]>

interface ValidationErrorsData {
  message: string
  errors: ValidationErrors
}

type ValidationErrorParser = (error: Error) => ValidationErrorsData | undefined | null

Example using zod

// app/utils/precognition.ts or shared/utils/precognition.ts
import { ZodError } from 'zod'

export const zodPrecognitionErrorParser: ValidationErrorParser = (error) => {
  if (error instanceof ZodError) {
    const errors: Record<string, string[]> = {}
    for (const issue of error.issues) {
      const key = issue.path.join('.')
      if (key in errors) {
        errors[key].push(issue.message)
        continue
      }
      errors[key] = [issue.message]
    }
    return { errors, message: error.message }
  }
  return null
}

Note: For Server side validation, place this file in shared/utils folder.

Defining a parser is mandatory because each validation library can have different errors shapes. ValidationErrors is the common interface this module will be able to use.

You can define as many error parses as you like. In this way the module will be able to parse also backend http errors.