On this page
  1. Install
  2. Compose one schema across boundaries
  3. Stable wire contract
  4. Configuration reference
  5. NestJS bridge
  6. React bridge
  7. Boundary
  8. API entry points and requirements
  9. Peer dependencies

@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

sh
npm install @playstack/core @playstack/validation zod

Install 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.

ts
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:

ts
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:

APIInputResult
parse(schema, input, options?)Any synchronous Zod schemaValidated output or ValidationFailedError.
parseAsync(schema, input, options?)Schema with async refinementsValidated output or ValidationFailedError.
formatValidationError(error, message?)A ZodErrorPortable ValidationErrorBody.
validationBodyFromError(error)ValidationFailedErrorPortable 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

ts
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:

ts
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 pointDeclaration 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.jsonNo 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.

PeerCompatible rangeWhen needed
@nestjs/common^10.0.0 || ^11.0.0Optional; only for the entry points that use it.
@nestjs/swagger^7.4.2 || ^8.0.0 || ^11.0.0Optional; only for the entry points that use it.
nestjs-zod^5.5.0Optional; 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.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software