Skip to content

Domain Model with Zod & fp-ts

One-stop-shop for all our domain needs

TypeScript
domains/User.ts
import * as Either from 'fp-ts/Either';
import type {Opaque} from 'type-fest';
import {z} from 'zod';
 
// Validation rules
export const userValidationSchema = z.object({
  id: z.string().uuid(),
  fullName: z.string().min(2).trim(),
  email: z.string().email(),
  emailVerifiedAt: z.date(),
  createdAt: z.date(),
  updatedAt: z.date(),
});
 
// Opaque types are used to prevent accidental type coercion
export type User = Opaque<z.infer<typeof userValidationSchema>, 'User'>;
 
// 1. Parse don't validate
// 2. Handle error as an Either.left
export function parseUser(value: unknown): Either.Either<Error, User> {
  return Either.tryCatch(
    () => userValidationSchema.parse(value) as User,
    (error) => new Error(error.message)
  );
}