DTO Validation

SpaceNode has a built-in validation system with array-based rules. You can also plug in Zod, Joi, or Yup via adapters.

Built-in Validation

Define a DTO

// modules/auth/auth.dto.js
import { dto } from 'SpaceNode'

export const loginDto = dto({
  email:    ['string', 'required', 'email'],
  password: ['string', 'required', 'min:6'],
})

export const registerDto = dto({
  name:     ['string', 'required', 'min:2', 'max:50'],
  email:    ['string', 'required', 'email'],
  password: ['string', 'required', 'min:6'],
  role:     ['string', 'enum:customer,seller', 'default:customer'],
})

Use in Routes

routes: [
  ['POST', '/login',    'login',    ['dto:loginDto']],
  ['POST', '/register', 'register', ['dto:registerDto']],
]

The dto:loginDto pipe validates request.body against the schema. If validation fails, it throws a ValidationError (400) with detailed error messages. If it passes, request.body is replaced with the cleaned output.

Available Rules

RuleDescription
stringMust be a string
numberMust be a number (not NaN)
booleanMust be a boolean
arrayMust be an array
objectMust be a non-null, non-array object
emailMust be a valid email format
urlMust start with http:// or https://
uuidMust be a valid UUID
dateMust be parseable by Date.parse()
requiredField cannot be undefined, null or empty string
optionalField is optional (default behavior)
min:NMin length (string/array) or min value (number)
max:NMax length (string/array) or max value (number)
length:NExact length (string/array)
pattern:REGEXMust match the regex pattern
enum:a,b,cMust be one of the listed values
default:VALUEUse this value if field is missing

Nested Objects

export const createOrderDto = dto({
  productId: ['string', 'required'],
  quantity:  ['number', 'required', 'min:1'],
  shipping: {
    address: ['string', 'required'],
    city:    ['string', 'required'],
    zip:     ['string', 'required', 'pattern:^\\d{5}$'],
  },
})

Custom Validators

export const myDto = dto({
  age: (value) => {
    if (typeof value !== 'number' || value < 18) return 'Must be 18+'
    return true
  },
})

External Adapters (Zod, Joi, Yup)

import { registerAdapter } from 'SpaceNode'
import { z } from 'zod'

// Register Zod adapter once
registerAdapter('zod', (schema, data) => {
  const result = schema.safeParse(data)
  if (!result.success) return { errors: result.error.issues }
  return { output: result.data }
})

// Use in DTO files
export const loginDto = {
  adapter: 'zod',
  schema: z.object({
    email: z.string().email(),
    password: z.string().min(6),
  }),
}

Validation Error Response

// POST /auth/login with invalid body
// Response 400:
{
  "error": "Validation failed",
  "status": 400,
  "details": [
    { "field": "email", "message": "email must be a valid email" },
    { "field": "password", "message": "password failed min:6 check" }
  ]
}