Validation patterns using fp-ts with error accumulation, form validation, and API input validation
This skill covers validation patterns using fp-ts, focusing on error accumulation, form validation, and API input validation.
Either<E, A> represents a computation that can fail with error E or succeed with value A.
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
// Basic validation function signature
type Validation<E, A> = E.Either<E, A>
// Simple validation returning Either
const validateNonEmpty = (field: string) => (value: string): E.Either<string, string> =>
value.trim().length > 0
? E.right(value.trim())
: E.left(`${field} cannot be empty`)
const validateMinLength = (field: string, min: number) => (value: string): E.Either<string, string> =>
value.length >= min
? E.right(value)
: E.left(`${field} must be at least ${min} characters`)
const validateEmail = (email: string): E.Either<string, string> =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
? E.right(email)
: E.left('Invalid email format')
Stops at the first error encountered. Use when subsequent validations depend on previous ones.
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
// Fail-fast: stops at first error
const validateUserFailFast = (input: { name: string; email: string }) =>
pipe(
validateNonEmpty('name')(input.name),
E.chain(name => pipe(
validateEmail(input.email),
E.map(email => ({ name, email }))
))
)
// Usage
validateUserFailFast({ name: '', email: 'invalid' })
// Result: Left('name cannot be empty') - email error not reported
Collects ALL errors using Applicative with a Semigroup for combining errors.
import * as E from 'fp-ts/Either'
import * as A from 'fp-ts/Apply'
import * as NEA from 'fp-ts/NonEmptyArray'
import { pipe } from 'fp-ts/funct...