yup
@seampact/codegen-yup writes a yup schema for each schema of your contract. NestJS, Express, Fastify, AdonisJS and Hono can use them to refuse a bad request before your handler runs.
Used by: any Node server that takes the validators link: NestJS, Express, Hono, Fastify and AdonisJS. The default is zod.
Set up
Section titled “Set up”Install yup in your app, then point the validators entry at this package.
# seampact.config.yaml, under targets - id: validators use: "@seampact/codegen-yup" apis: [notes]Link it from your server entry, next to types:
link: types: types validators: validatorsRun npx seampact generate. The files land in validators/<api>/ under your outDir, with the same layout as zod plus a yup-support.ts.
What you get
Section titled “What you get”For the CreateTask schema of the overview, trimmed from schemas/input.ts:
export const CreateTaskSchema = yup .object({ title: yup .string() .strict() .min(1, "Expected at least 1 character.") .max(200, "Expected at most 200 characters.") .defined(), effort: yup .number() .strict() .integer() .test({ name: "minimum", message: "Expected at least -9007199254740991.", test: (value) => value === undefined || !Number.isInteger(value) || value >= -Number.MAX_SAFE_INTEGER, }) .test({ name: "maximum", message: "Expected at most 9007199254740991.", test: (value) => value === undefined || !Number.isInteger(value) || value <= Number.MAX_SAFE_INTEGER, }) .min(0, "Expected at least 0.") .optional(), }) .strict() .default(undefined) .test(unknownKeysTest);The schema is strict: it refuses a value of the wrong type instead of converting it, and unknownKeysTest refuses a key the contract does not declare. The operation schemas in operations/input.ts check path, query, headers and body together.
Use them in your code
Section titled “Use them in your code”import { inputSchemas } from "./__generated__/validators/notes/index.js";
const isValid = inputSchemas.CreateTaskSchema.isValidSync({ title: "" });When a check fails
Section titled “When a check fails”Each yup error becomes one issue of the 400 body. The generated checks carry the contract rule (minimum, unknownKeys) in their params, and the server reads that rule, never yup’s own message.
Native or consistent rules
Section titled “Native or consistent rules”With the default rules: native, the schemas use yup’s own rules, which differ from the contract in a few places.
- Lengths count UTF-16 units: an emoji counts 2, so
maxLength: 1refuses “😀”. uuidalso checks the version and variant digits, and refuses version 7 and the max UUIDffffffff-ffff-ffff-ffff-ffffffffffff.date-timechecks only the digit layout: it accepts an empty string, month 13, hour 99, second 60,2026-02-30, an offset like+01or+0100, and more than 9 fraction digits. It refuses a lowercasetorz.- A number with a wrong type also reports its range issues.
multipleOfhas no yup rule: it stays the contract’s check.
Set rules: seampact-consistent for the contract’s exact checks (Switch the validator).
Limits
Section titled “Limits”The generator refuses by name a oneOf, a property default, a nullable value other than an opaque one and the other forms in Generate fails. No client guide uses these schemas.
- Switch the validator.
- The 400 body: When a check fails.