Gubu: Object Shape Validation Utility
Gubu is an object shape validation utility for JavaScript and TypeScript, designed to provide a more intuitive and less verbose developer experience compared to alternatives like Joi or JSON-Schema. Currently stable at version 9.0.0, the library differentiates itself with a "Schema By Example" approach, where validation schemas closely mirror the actual data structure. This design simplifies reading and reasoning about validation rules. A key feature is its ability to deeply fill out objects with default values if properties are missing, differing from shallow merge operations like `Object.assign`. It's crucial for developers to note that Gubu deliberately mutates the input object to inject these defaults, a design choice to simplify internal logic and delegate cloning decisions to the calling code. Gubu functions effectively in both browser and Node.js environments and ships with full TypeScript support.
Common errors
-
Gubu: [path]: is required
cause A field defined with a JavaScript type constructor (e.g., `Number`, `Boolean`) was missing from the input object being validated.fixEnsure all required fields are present in the object provided for validation, or modify the schema to make the field optional with a default if applicable. -
Gubu: [path]: is not a [type]
cause A field in the input object did not match the expected primitive type (e.g., a string was provided for a `Number` field) as defined in the schema.fixCorrect the type of the value in the input object to align with the schema's expectation for that specific field. -
TypeError: Gubu is not a function
cause Incorrect import or require syntax, most commonly attempting to use `import Gubu from 'gubu'` when `Gubu` is a named export, or incorrectly accessing the export in CommonJS (`require('gubu').Gubu`).fixFor ESM, use `import { Gubu } from 'gubu'`. For CommonJS, use `const { Gubu } = require('gubu')`.
Warnings
- gotcha Gubu deliberately mutates the input object passed to the validation function. This can lead to unexpected side effects if the original object is intended to remain immutable elsewhere in the application.
- gotcha The method for defining optional fields with defaults versus strictly required fields differs significantly. Literal values (e.g., `port: 8080`) define optional fields with that literal as the default, while JavaScript type constructors (e.g., `timeout: Number`, `secure: Boolean`) define required fields without defaults.
- breaking Upgrading to a new major version (e.g., 9.x.x) may introduce breaking changes to the API, schema definition syntax, or validation behavior. Always consult the official release notes before upgrading major versions.
Install
-
npm install gubu -
yarn add gubu -
pnpm add gubu
Imports
- Gubu
import Gubu from 'gubu'
import { Gubu } from 'gubu' - Gubu
const Gubu = require('gubu').Gubuconst { Gubu } = require('gubu') - GubuSchema
import type { GubuSchema } from 'gubu'
Quickstart
import { Gubu } from 'gubu' // Using ESM syntax for modern projects
const optionsShape = Gubu({
server: {
port: 8080, // Default 8080, optional
host: 'localhost', // Default 'localhost', optional
secure: Boolean // Required boolean, no default
},
client: {
timeout: Number, // Required number, no default
debug: Boolean, // Required boolean, no default
headers: [String], // Array of strings, optional, empty array default if not provided
retries: 3 // Default 3, optional
},
plugins: [ // Array of objects, optional, empty array default
{
name: String, // Required string
enabled: Boolean // Required boolean
}
]
})
console.log('--- Scenario 1: Empty input, check defaults and required fields ---')
let config1 = {}
try {
optionsShape(config1) // Mutates config1
console.log('Validated Config 1:', JSON.stringify(config1, null, 2))
} catch (e: any) {
console.error('Validation Error 1:', e.message)
}
console.log('\n--- Scenario 2: Partial input, check deep merging and specific values ---')
let config2 = {
server: {
port: 9000,
secure: true
},
client: {
timeout: 5000,
debug: false,
headers: ['X-Custom-Header-A']
}
}
try {
optionsShape(config2) // Mutates config2
console.log('Validated Config 2:', JSON.stringify(config2, null, 2))
} catch (e: any) {
console.error('Validation Error 2:', e.message)
}
console.log('\n--- Scenario 3: Invalid input type for a field ---')
let config3 = {
server: {
port: 'not-a-number', // Invalid type
secure: true
},
client: {
timeout: 1000,
debug: true,
headers: []
}
}
try {
optionsShape(config3)
console.log('Validated Config 3:', JSON.stringify(config3, null, 2))
} catch (e: any) {
console.error('Validation Error 3:', e.message)
}
console.log('\n--- Scenario 4: Missing a required field ---')
let config4 = {
server: {
port: 8080,
// secure is missing
},
client: {
timeout: 1000,
debug: true,
headers: []
}
}
try {
optionsShape(config4)
console.log('Validated Config 4:', JSON.stringify(config4, null, 2))
} catch (e: any) {
console.error('Validation Error 4:', e.message)
}