@playstack/validation
Zod-backed shared schemas and one portable HTTP validation-error contract.
Free. MIT licensed. Check preview availability before installing. See package access.
@playstack/validation lets browser forms, APIs, and generated OpenAPI descriptions agree on schemas and validation failures.
Install
npm install @playstack/core @playstack/validation zodInstall the optional Nest peers only when using @playstack/validation/nest.
Compose one schema across boundaries
Keep schemas in an application contract library, parse at the transport edge, and pass only validated values to domain code.
import { emailSchema, parse, z } from '@playstack/validation'
export const createUserSchema = z.object({
email: emailSchema,
displayName: z.string().trim().min(1).max(80),
})
export type CreateUserInput = z.output<typeof createUserSchema>
export function createUser(input: unknown) {
const command = parse(createUserSchema, input)
return users.create(command)
}Stable wire contract
formatValidationError and validationBodyFromError produce the same response shape for every HTTP framework:
interface ValidationErrorBody {
error: { code: string; retryable: boolean; message: string }
fields: Readonly<Record<string, readonly string[]>>
form: readonly string[]
}fields uses dot-separated schema paths. Issues without a path go in form. malformedJsonErrorBody() and refinementFailedErrorBody() produce the smaller standard error body.
Configuration reference
The portable package has no global configuration. Parsing functions accept an optional Zod error map:
| API | Input | Result |
|---|---|---|
parse(schema, input, options?) | Any synchronous Zod schema | Validated output or ValidationFailedError. |
parseAsync(schema, input, options?) | Schema with async refinements | Validated output or ValidationFailedError. |
formatValidationError(error, message?) | A ZodError | Portable ValidationErrorBody. |
validationBodyFromError(error) | ValidationFailedError | Portable ValidationErrorBody. |
Built-in schemas cover normalized email, kebab-case slugs, offset ISO timestamps, sort direction, and cursor pagination with a default limit of 25 and maximum of 100.
NestJS bridge
import {
PlaystackValidationPipe,
cleanupOpenApiDoc,
createZodDto,
} from '@playstack/validation/nest'
class CreateUserDto extends createZodDto(createUserSchema) {}
@Post()
create(
@Body(new PlaystackValidationPipe(createUserSchema)) input: CreateUserInput,
) {
return users.create(input)
}
const document = cleanupOpenApiDoc(SwaggerModule.createDocument(app, config))PlaystackValidationPipe accepts the schema and the same optional error-map configuration. Zod issues become HTTP 400 responses; unexpected refinement failures become HTTP 500 responses. DTO and serializer helpers are re-exported from nestjs-zod so request, response, and OpenAPI schemas remain aligned.
React bridge
applyValidationErrors works with any form library that exposes a setError(path, { type, message }) callback:
import { applyValidationErrors } from '@playstack/validation/react'
const { fieldCount, form } = applyValidationErrors(response, setError)
setFormErrors(form)It returns the number of field messages applied plus the form-level messages; it does not choose a React form library or render error UI.
Boundary
Validation is not a sanitizer, form library, localization framework, authorization layer, or database-schema generator. The application owns schema placement, localized error maps, and transport-specific parsing of malformed JSON.
API entry points and requirements
Reference snapshot: @playstack/validation@0.1.0-beta.1. Import only the entry point your runtime needs. Paths below are relative to the installed package; use Go to Definition in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.
| Public entry point | Declaration file |
|---|---|
@playstack/validation | ./dist/index.d.ts |
@playstack/validation/errors | ./dist/errors.d.ts |
@playstack/validation/nest | ./dist/nest.d.ts |
@playstack/validation/react | ./dist/react.d.ts |
@playstack/validation/package.json | No TypeScript declaration (asset or metadata export). |
Node.js engine requirement: >=20. This is not a claim that every entry point works in browsers or Workers.
Peer dependencies
Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.
| Peer | Compatible range | When needed |
|---|---|---|
@nestjs/common | ^10.0.0 || ^11.0.0 | Optional; only for the entry points that use it. |
@nestjs/swagger | ^7.4.2 || ^8.0.0 || ^11.0.0 | Optional; only for the entry points that use it. |
nestjs-zod | ^5.5.0 | Optional; only for the entry points that use it. |
For a complete first program, start with Getting started. For API lookup and partial-example conventions, see Reading the reference. Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.