The library enforces strict data validation and input contract definition to protect against malformed payloads, prototype pollution, and unexpected type coercion. Developers must explicitly define shape, boundaries, and type constraints because default configurations or loose schemas can leave systems vulnerable to mass assignment and unauthorized property injection. Security-sensitive surfaces include untrusted JSON deserialization, custom validators, and dynamic schema composition, all of which must fail closed when encountering unvetted properties or invalid types.
Essential implementation rules
Verify API Support Across Runtimes
Verify API support and avoid calling undefined methods in environment-restricted runtimes such as browsers, ensuring methods like Joi.binary() do not cause runtime exceptions that could compromise validation controls.
Enforce Temporal Boundary Checks
Utilize boundary methods such as .greater() or .less() combined with 'now' or dynamic references via Joi.ref() to validate relative temporal boundaries and prevent invalid state transitions.
Sanitize Deserialized Objects and Prevent Prototype Pollution
Pass untrusted parsed JSON through Joi.object() schemas and enforce unknown property rejection or stripping to eliminate prototype pollution vectors like __proto__ and prevent mass assignment attacks.
Enforce Strict Base Types and Reject Unknown Properties
Avoid generic untyped schemas by explicitly configuring strict base types, disallowing unknown attributes, and stripping unvetted payload properties to prevent arbitrary input injection and type confusion.
Explicitly Allow Empty Strings and Define Allowed Values
Explicitly allow empty strings using .allow('') when empty inputs are valid, and use .valid() or .forbidden() to enforce strict whitelists and block unauthorized attribute modifications.
Enforce Strict Array Element Types and Filter Options
Define explicit element schemas using Joi.array().items() and configure stripUnknown: { arrays: true } in validation preferences to correctly filter out unexpected or non-matching array items.
Validate Complex Nested Models and Manage Hierarchy References
Compose nested object and array structures explicitly, validate dependencies against upper hierarchy levels using validated relative path references like Joi.ref('...parentField'), and use asynchronous validation via validateAsync() for external checks.
Disable Implicit Type Coercion for Security-Critical Inputs
Call .strict() or configure { convert: false } on schemas to disable implicit type casting and ensure inputs strictly match expected primitive or symbol mapping types without unexpected coercion.
Manage Property Aliases and Renaming Explicitly
Configure explicit alias options when renaming keys to prevent parameter ambiguity, ensuring source property retention is explicitly controlled and alias transformations inside alternatives are properly isolated.
Escape HTML in Validation Error Messages
Configure errors.escapeHtml to true within validation options to ensure that error messages containing user-supplied input are safely HTML-escaped and protected against Cross-Site Scripting.
Limit Recursive Link Schema Depth
Configure maxRecursion() on recursive link schemas defined with Joi.link() to explicitly limit nested object validation depth and prevent excessive stack allocation and resource exhaustion.
Preserve Schema Immutability Across Method Chaining
Store or return the result of chained Joi method calls since schema chain methods return new instances rather than mutating existing ones in place, preventing unconstrained schemas from accepting malicious input.
Secure Custom Validators and Extension Rules
Wrap custom validation logic in try-catch blocks, use helper methods like helpers.error() to signal failures safely, define error message maps for all custom error codes, and execute asynchronous validation via validateAsync().
joi: All Security Cards
Approximately 6,693 tokens
On this card
Category: api contract misuse
Avoid referencing unsupported API signatures and environment-specific methods
Use when
Developing data validation logic across different execution environments where specific library methods may be undefined or unsupported.
Secure rules
Rule 1: Verify API support and avoid calling undefined methods in environment-restricted runtimes.
Ensure that methods such as Joi.binary() are not invoked in browser environments where they are undefined, preventing runtime exceptions that could break validation controls.
Enforce temporal boundary checks at data validation time
Use when
Validating incoming date and time payloads against relative thresholds and dynamic state transitions using Joi schemas.
Secure rules
Rule 1: Validate relative temporal boundaries and field interdependencies using boundary methods and references.
Ensure temporal security constraints are enforced by utilizing methods like .greater() or .less() combined with 'now' or dynamic references via Joi.ref() to prevent invalid state transitions.
Safely Validate and Cleanse Deserialized JSON Objects
Use when
Validating untrusted JSON payloads or parsed objects using Joi.object() schemas to protect against prototype pollution and mass assignment vulnerabilities.
Secure rules
Rule 1: Use Joi.object() schemas to validate deserialized JSON objects and strip prototype key pollution vectors like proto.
When handling untrusted deserialized data, pass the parsed object to a defined Joi.object() schema to ensure prototype keys are strictly denied and stripped from the validated output.
const schema = Joi.object({ name: Joi.string().required()});const payload = JSON.parse(untrustedJsonInput);const { value, error } = schema.validate(payload);if (!error) { // value is safe from __proto__ prototype pollution vectors}
Rule 2: Pass stripUnknown to schema validation to remove unvalidated properties from deserialized objects.
Pass { stripUnknown: true } during validation to automatically strip unvalidated properties and prevent mass assignment attacks when binding deserialized inputs to application models.
const schema = Joi.object({ itemName: Joi.string().required()});const { value, error } = schema.validate(deserializedPayload, { stripUnknown: true });if (!error) { // value only includes fields explicitly defined in the schema}
Category: input contract definition
Construct and Validate Attribute-Based Models Securely Using Strict Schemas
Use when
Building, extending, and compiling attribute-based object models or dynamic validation schemas from untrusted inputs and custom type definitions.
Secure rules
Rule 1: Disallow or strip unknown object attributes during model construction.
When validating untrusted input to construct object models, enforce strict property controls by setting allowUnknown to false or enabling stripUnknown. Allowing arbitrary unknown keys into model structures enables mass assignment vulnerabilities.
Rule 2: Compile raw attribute structures safely into formal validation models.
When constructing validation schemas dynamically from attribute-based plain JavaScript objects, array specs, or literals, use Joi.compile() to safely cast raw object specifications into formal executable validation models. Ensure schema boundaries do not implicitly mix incompatible Joi schema instances across major dependency versions without intentional handling.
Rule 3: Construct object schemas only from plain object attribute definitions.
When dynamically building object schemas from key-value definition structures, ensure that schema attributes are specified using plain JavaScript objects. Joi enforces strict prototype checks during schema compilation, rejecting non-plain objects to preserve schema integrity.
Rule 4: Enforce strict type validation during model attribute construction.
When constructing schemas for model attribute validation, call .strict() or configure convert: false preferences to disable automatic type coercion. Disabling implicit type conversion ensures that model attributes strictly match expected data types rather than coercing malformed or unexpected input into valid domain model values.
Rule 5: Contextualize model schemas to prevent attribute injection.
Use schema manipulation methods like .fork() or .alter() to adjust attribute presence controls (required, optional, or forbidden) based on application contexts such as model creation versus updates. Explicitly marking sensitive or read-only attributes as forbidden() in user-facing construction contexts prevents mass-assignment vulnerabilities.
Rule 6: Inherit base types explicitly when constructing custom model extensions.
When creating custom extended validation models using Joi.extend(), explicitly specify a strict base schema (e.g. Joi.string().min(2) or Joi.object()) so basic type enforcement and constraints run before custom validation rules execute.
Enforce Strict Base Types and Element Validation for All Input Schemas
Use when
Building and validating incoming data schemas using joi to prevent type confusion, arbitrary input injection, and unvetted payload properties.
Secure rules
Rule 1: Avoid generic untyped schemas and explicitly configure strict base types and unknown property rejection.
Define specific schema types rather than generic Joi.any() schemas, and ensure object schema validation keeps allowUnknown as false to prevent unvetted payload properties and arbitrary types from being processed.
Rule 2: Enforce explicit string and type constraints to block unexpected non-string primitives.
Enforce explicit type boundaries using Joi.string() to prevent untrusted payloads from supplying arbitrary non-string types such as booleans, numbers, or null into string processing paths.
Rule 3: Enforce strict element schemas on arrays to reject arbitrary input types.
Call .items() with specific Joi schemas on array definitions to ensure every element is validated against allowed types, preventing arbitrary objects or unexpected primitives from bypassing validation.
Rule 4: Restrict polymorphic inputs using explicit schema alternatives.
When handling fields that accept multiple data types, define an explicit list of allowed schemas using Joi.alternatives() or array schema notation to reject any arbitrary types falling outside the configured set.
Rule 5: Enforce strict object schemas and specific class instances to block arbitrary object types.
Use Joi.object().instance(Constructor) when validating JavaScript object inputs that must belong to a specific class or constructor to ensure arbitrary object types or incompatible instances are rejected.
Rule 6: Enforce strict date validation to reject non-finite numbers and booleans.
Validate date fields using Joi.date() and explicitly disable implicit type conversion using .prefs({ convert: false }) when strict type boundaries are required to prevent type confusion.
Enforce Strict Input Contracts and Type Boundaries Using Joi Schemas
Use when
Validating incoming request payloads, form data, or external data structures to ensure they adhere to strict input contracts before application processing.
Secure rules
Rule 1: Explicitly allow empty strings when empty inputs are valid.
By default, Joi.string() rejects empty strings. When empty string values are acceptable inputs, developers must explicitly allow them using .allow('') rather than assuming empty strings will pass string validation.
Rule 2: Enforce strict array element types and forbidden item constraints.
Define explicit element schemas using Joi.array().items() and enforce negative constraints on array elements using Joi.schema().forbidden() to prevent unvalidated or unauthorized array elements from passing schema validation.
Rule 3: Enforce explicit CIDR constraints on IP address schemas.
When defining IP address schemas with Joi.string().ip(), explicitly set the cidr option to ‘forbidden’, ‘required’, or ‘optional’ to strictly constrain allowed network formats.
Rule 4: Use Joi.override to safely reset permitted values.
When extending or refining base schemas, calling .allow() appends new values to the existing whitelist by default. To replace previously permitted values or remove unwanted baseline defaults, pass Joi.override as the first argument to .allow().
Rule 5: Define polymorphic contracts explicitly with conditional schemas.
When validating polymorphic data structures with Joi.alternatives() or .conditional(), ensure all expected branches explicitly define input schemas and valid references to prevent runtime errors or insecure handling of dynamic payload types.
Rule 6: Explicitly define custom truthy and falsy input values.
By default, Joi.boolean() rejects common web/form boolean string representations. When parsing inputs from form submissions or non-standard external interfaces, explicitly declare acceptable non-boolean representations using .truthy() and .falsy() rather than disabling validation.
Rule 7: Validate ES6 class constructors with Joi class schema.
Use Joi.function().class() when a configuration or API specifically expects an ES6 class constructor rather than a standard callable function to prevent runtime type errors or unexpected execution behavior.
Rule 8: Enforce strict number validation to prevent boolean type confusion.
Use Joi.number() to strictly validate numeric input without implicit boolean coercion. Joi rejects true and false values for numeric schemas, preventing type-confusion bugs.
Enforce strict object property validation and prevent mass assignment
Use when
Validating untrusted object payloads prior to database storage or internal variable assignment to prevent unauthorized property injection.
Secure rules
Rule 1: Disallow unknown keys and strip unvalidated properties during validation
Ensure that allowUnknown is set to false (the default) or explicitly set stripUnknown to true to prevent unvetted input fields from passing through validation to downstream assignment operations.
Rule 2: Configure explicit presence constraints for required assignment fields
Use the presence option set to 'required' or 'forbidden' on validation options to ensure missing or unexpectedly present keys do not bypass assignment validation checks.
Rule 3: Disable implicit type coercion and enforce strict data types
Chain .strict() or set { convert: false } via schema preferences to ensure assigned inputs conform strictly to declared data types rather than undergoing automatic type coercion.
Rule 4: Restrict property assignment using valid values and forbidden fields
Use .valid(...values) to enforce strict whitelists of acceptable values for assigned fields, and use .forbidden() to explicitly block unauthorized attribute modifications.
Strictly Validate and Filter Unknown Fields in Object and Array Schemas
Use when
Defining object and array input schemas in joi to process untrusted payloads safely and prevent unvalidated properties or elements from reaching internal functions.
Secure rules
Rule 1: Reject or explicitly strip unknown keys on object schemas to prevent mass assignment and unauthorized data processing.
Avoid using bare Joi.object() without key definitions on untrusted inputs because empty object schemas permit arbitrary unknown keys. Instead, explicitly define object schemas to reject unknown fields by default or configure stripUnknown: true in validation options to remove unvalidated properties.
Rule 2: Configure explicit array strip options to safely filter out non-matching elements from untrusted array inputs.
When validating arrays using Joi.array().items(), passing a simple boolean stripUnknown: true preference does not automatically discard non-matching elements and instead causes validation to fail. Set stripUnknown: { arrays: true } in the preferences to correctly filter out unknown or unexpected array items.
Validate Complex Nested Models and Arrays with Explicit Schemas and Path References
Use when
Building schemas for structured, nested, or hierarchical data payloads where validation requires precise type definitions, unknown property rejection, and inter-field dependency checks across object levels.
Secure rules
Rule 1: Define explicit nested structures and restrict unknown fields to prevent malformed payloads and injection vectors.
Explicitly compose nested schemas using Joi.object() and Joi.array().items(), setting rules like .required() on mandatory nested fields. Use options such as allowUnknown: false and stripUnknown: { objects: true, arrays: true } to reject or strip unexpected properties and enforce strict schema bounds.
Rule 2: Enforce inter-field dependencies across parent and nested schemas using validated relative path references.
Use relative path references such as Joi.ref('...parentField') or Joi.ref('..0') within nested array items or child objects to validate dependencies against upper hierarchy levels. Ensure relative references stay within the bounds of the schema hierarchy to prevent runtime exceptions.
Rule 3: Use asynchronous validation for nested models containing external validation rules.
When defining nested object models that include custom asynchronous verification rules using .external(), always perform validation using validateAsync() rather than synchronous validation methods to prevent runtime errors.
const schema = Joi.object({ user: Joi.object({ id: Joi.string().external(async (value, helpers) => { const exists = await db.checkUserExists(value); if (!exists) { throw new Error('User ID does not exist'); } return value; }) })});const validated = await schema.validateAsync(payload);
Rule 4: Manage nested schema variations immutably using explicit IDs and forks.
Assign explicit IDs using .id('identifier') to target sub-schemas contained within complex structures, and use schema.fork() with property paths to adjust validation requirements contextually without mutating shared base schemas.
Defining schema default values and processing inputs where automatic default substitution must be explicitly controlled or disabled.
Secure rules
Rule 1: Disable schema defaults when validation must not add missing values
Joi applies a schema default when the original value is undefined. Pass noDefaults: true to validate() when the validated result must preserve missing values instead of populating them from schema defaults.
Enforce Strict Type Checking and Input Conversion Settings
Use when
Validating untrusted input where exact types and unambiguous string interpretations are security-critical.
Secure rules
Rule 1: Disable implicit type casting on schemas to prevent unexpected data coercion.
By default, joi attempts to coerce input types to match schema definitions. When validating untrusted input where exact types are security-critical, call .strict() on the schema or configure { convert: false } in validation preferences to disable implicit type casting.
Rule 2: Disable input conversion for strict symbol mapping to prevent untrusted key execution.
When using Joi.symbol().map() to convert string or numeric input keys into internal Symbol instances, recognize that joi automatically coerces matching keys by default. To prevent untrusted string inputs from being translated into internal application Symbols, explicitly configure schema preferences with convert set to false.
Manage Object Key Aliasing and Renaming Explicitly
Use when
Renaming or aliasing object properties during schema validation to ensure unambiguous and canonical data interpretation.
Secure rules
Rule 1: Configure explicit alias options when renaming keys to prevent parameter ambiguity and unexpected data loss.
When using .rename(from, to, options), joi defaults to removing the source property key. Explicitly set alias: true in the rename options if downstream handlers require preserving the original property key alongside the renamed key, or leave it as false to eliminate legacy keys.
Rule 2: Isolate property alias transformations within schema alternative branches.
Ensure that key renaming operations inside schema alternatives are scoped safely. joi guarantees that key renaming side effects from a failed schema branch are discarded and not applied to subsequent alternative evaluations.
Prevent Prototype Pollution by Validating Untrusted Objects with Joi Schemas
Use when
Parsing and validating untrusted input objects and JSON payloads to ensure prototype properties like __proto__ are securely stripped or rejected.
Secure rules
Rule 1: Pass untrusted input objects through Joi.object() schemas to safely strip or ignore injected __proto__ properties.
When handling untrusted data, always run parsed input through explicit Joi object schemas using schema.validate() so that internal prototype properties and injected prototype pollution vectors are not preserved in validated output objects.
const Joi = require('joi');const schema = Joi.object({ name: Joi.string().required()});const payload = JSON.parse(untrustedInput);const { value, error } = schema.validate(payload);if (!error) { // value is safely validated and Object.prototype is preserved}
Rule 2: Enforce strict key boundaries and disallow unknown keys on object validation schemas.
Explicitly specify expected properties using .keys() and strictly enforce boundaries with .unknown(false) to prevent unvalidated properties such as __proto__ or constructor from persisting in validated output objects.
Enable HTML escaping for validation error messages and templates
Use when
Rendering Joi validation error messages or dynamic templates in HTML user interfaces where user-supplied input may be present.
Secure rules
Rule 1: Set errors.escapeHtml to true when validating inputs to ensure error messages containing user input are safely HTML-escaped.
By default, escapeHtml is false. When validation error messages containing user-supplied input are rendered directly into HTML without escaping, malicious input can lead to Cross-Site Scripting (XSS) vulnerabilities. Configure errors.escapeHtml to true within the validate options.
Defining recursive validation schemas using Joi.link() to validate nested object hierarchies.
Secure rules
Rule 1: Configure maxRecursion on recursive link schemas to prevent excessive stack allocation and CPU consumption.
When defining recursive schemas using Joi.link(), always configure maxRecursion() to explicitly limit the depth of nested object validation and protect against Denial of Service conditions.
Rely on Schema Immutability and Store Chained Instance Results
Use when
Building and configuring Joi validation schemas using method chaining.
Secure rules
Rule 1: Store or return the result of chained Joi method calls to ensure validation constraints remain active.
Joi schema chain methods do not mutate schema instances in place; they return new schema instances. Developers must store or return the result of chained method calls such as .valid(), .required(), or .email() to ensure validation constraints are active. Assuming that Joi schema methods mutate existing instances in place leaves schemas unconstrained, causing endpoints to accept invalid or malicious input.
const baseSchema = Joi.string();// Correct: store the newly created schema instanceconst restrictedSchema = baseSchema.valid('admin', 'user');// Incorrect (leaves baseSchema unconstrained):// baseSchema.valid('admin', 'user');
Secure custom validators and extension rules in Joi schemas
Use when
Building custom validation extensions, asynchronous hooks, or custom methods for Joi schemas.
Secure rules
Rule 1: Execute asynchronous validation for custom external rules
Always use schema.validateAsync() when schemas contain asynchronous custom validators registered via any.external(), as synchronous validation calls will either fail or skip critical checks when externals: false is configured.
const schema = Joi.object({ userId: Joi.string().external(async (value, helpers) => { const valid = await verifyUserExists(value); if (!valid) { throw new Error('User does not exist'); } return value; })});const result = await schema.validateAsync(inputData);
Wrap custom validation logic inside try-catch blocks and use helper methods like helpers.error() or helpers.message() to signal validation failures rather than letting uncaught exceptions propagate.
Validate user-provided configuration arguments inside method implementations using Joi.assert() or explicit type checks before assigning flags or mutating custom schema state.
Rule 4: Apply standard schema constraints before custom validator callbacks
Chain standard built-in Joi validation rules before custom external or custom validators so that execution halts on validation failure before invoking custom hooks.
Rule 5: Define error messages for all custom error codes in extensions
Map every custom error code returned via helpers.error() to a message template in the extension’s messages map to prevent unhandled runtime errors during validation.
Rule 6: Return standard error structures in custom schema extension validators
Inspect schema rules and flags using official methods such as schema.$_getRule() and schema.$_getFlag(), and return standard result objects containing { value, errors } via the error helper.
const CustomJoi = Joi.extend({ type: 'million', base: Joi.number(), messages: { 'million.base': '{{#label}} must be at least a million' }, coerce(value, { schema }) { if (schema.$_getRule('round')) { return { value: Math.round(value) }; } }, validate(value, { schema, error }) { if (value < 1000000) { return { value, errors: error('million.base') }; } }});
Avoid referencing unsupported API signatures and environment-specific methods
Approximately 167 tokens
Use when
Developing data validation logic across different execution environments where specific library methods may be undefined or unsupported.
Secure rules
Rule 1: Verify API support and avoid calling undefined methods in environment-restricted runtimes.
Ensure that methods such as Joi.binary() are not invoked in browser environments where they are undefined, preventing runtime exceptions that could break validation controls.
Enforce temporal boundary checks at data validation time
Approximately 171 tokens
Use when
Validating incoming date and time payloads against relative thresholds and dynamic state transitions using Joi schemas.
Secure rules
Rule 1: Validate relative temporal boundaries and field interdependencies using boundary methods and references.
Ensure temporal security constraints are enforced by utilizing methods like .greater() or .less() combined with 'now' or dynamic references via Joi.ref() to prevent invalid state transitions.
Safely Validate and Cleanse Deserialized JSON Objects
Approximately 312 tokens
Use when
Validating untrusted JSON payloads or parsed objects using Joi.object() schemas to protect against prototype pollution and mass assignment vulnerabilities.
Secure rules
Rule 1: Use Joi.object() schemas to validate deserialized JSON objects and strip prototype key pollution vectors like proto.
When handling untrusted deserialized data, pass the parsed object to a defined Joi.object() schema to ensure prototype keys are strictly denied and stripped from the validated output.
const schema = Joi.object({ name: Joi.string().required()});const payload = JSON.parse(untrustedJsonInput);const { value, error } = schema.validate(payload);if (!error) { // value is safe from __proto__ prototype pollution vectors}
Rule 2: Pass stripUnknown to schema validation to remove unvalidated properties from deserialized objects.
Pass { stripUnknown: true } during validation to automatically strip unvalidated properties and prevent mass assignment attacks when binding deserialized inputs to application models.
const schema = Joi.object({ itemName: Joi.string().required()});const { value, error } = schema.validate(deserializedPayload, { stripUnknown: true });if (!error) { // value only includes fields explicitly defined in the schema}
Construct and Validate Attribute-Based Models Securely Using Strict Schemas
Approximately 3,893 tokens
On this card
Use when
Building, extending, and compiling attribute-based object models or dynamic validation schemas from untrusted inputs and custom type definitions.
Secure rules
Rule 1: Disallow or strip unknown object attributes during model construction.
When validating untrusted input to construct object models, enforce strict property controls by setting allowUnknown to false or enabling stripUnknown. Allowing arbitrary unknown keys into model structures enables mass assignment vulnerabilities.
Rule 2: Compile raw attribute structures safely into formal validation models.
When constructing validation schemas dynamically from attribute-based plain JavaScript objects, array specs, or literals, use Joi.compile() to safely cast raw object specifications into formal executable validation models. Ensure schema boundaries do not implicitly mix incompatible Joi schema instances across major dependency versions without intentional handling.
Rule 3: Construct object schemas only from plain object attribute definitions.
When dynamically building object schemas from key-value definition structures, ensure that schema attributes are specified using plain JavaScript objects. Joi enforces strict prototype checks during schema compilation, rejecting non-plain objects to preserve schema integrity.
Rule 4: Enforce strict type validation during model attribute construction.
When constructing schemas for model attribute validation, call .strict() or configure convert: false preferences to disable automatic type coercion. Disabling implicit type conversion ensures that model attributes strictly match expected data types rather than coercing malformed or unexpected input into valid domain model values.
Rule 5: Contextualize model schemas to prevent attribute injection.
Use schema manipulation methods like .fork() or .alter() to adjust attribute presence controls (required, optional, or forbidden) based on application contexts such as model creation versus updates. Explicitly marking sensitive or read-only attributes as forbidden() in user-facing construction contexts prevents mass-assignment vulnerabilities.
Rule 6: Inherit base types explicitly when constructing custom model extensions.
When creating custom extended validation models using Joi.extend(), explicitly specify a strict base schema (e.g. Joi.string().min(2) or Joi.object()) so basic type enforcement and constraints run before custom validation rules execute.
Enforce Strict Base Types and Element Validation for All Input Schemas
Use when
Building and validating incoming data schemas using joi to prevent type confusion, arbitrary input injection, and unvetted payload properties.
Secure rules
Rule 1: Avoid generic untyped schemas and explicitly configure strict base types and unknown property rejection.
Define specific schema types rather than generic Joi.any() schemas, and ensure object schema validation keeps allowUnknown as false to prevent unvetted payload properties and arbitrary types from being processed.
Rule 2: Enforce explicit string and type constraints to block unexpected non-string primitives.
Enforce explicit type boundaries using Joi.string() to prevent untrusted payloads from supplying arbitrary non-string types such as booleans, numbers, or null into string processing paths.
Rule 3: Enforce strict element schemas on arrays to reject arbitrary input types.
Call .items() with specific Joi schemas on array definitions to ensure every element is validated against allowed types, preventing arbitrary objects or unexpected primitives from bypassing validation.
Rule 4: Restrict polymorphic inputs using explicit schema alternatives.
When handling fields that accept multiple data types, define an explicit list of allowed schemas using Joi.alternatives() or array schema notation to reject any arbitrary types falling outside the configured set.
Rule 5: Enforce strict object schemas and specific class instances to block arbitrary object types.
Use Joi.object().instance(Constructor) when validating JavaScript object inputs that must belong to a specific class or constructor to ensure arbitrary object types or incompatible instances are rejected.
Rule 6: Enforce strict date validation to reject non-finite numbers and booleans.
Validate date fields using Joi.date() and explicitly disable implicit type conversion using .prefs({ convert: false }) when strict type boundaries are required to prevent type confusion.
Enforce Strict Input Contracts and Type Boundaries Using Joi Schemas
Use when
Validating incoming request payloads, form data, or external data structures to ensure they adhere to strict input contracts before application processing.
Secure rules
Rule 1: Explicitly allow empty strings when empty inputs are valid.
By default, Joi.string() rejects empty strings. When empty string values are acceptable inputs, developers must explicitly allow them using .allow('') rather than assuming empty strings will pass string validation.
Rule 2: Enforce strict array element types and forbidden item constraints.
Define explicit element schemas using Joi.array().items() and enforce negative constraints on array elements using Joi.schema().forbidden() to prevent unvalidated or unauthorized array elements from passing schema validation.
Rule 3: Enforce explicit CIDR constraints on IP address schemas.
When defining IP address schemas with Joi.string().ip(), explicitly set the cidr option to ‘forbidden’, ‘required’, or ‘optional’ to strictly constrain allowed network formats.
Rule 4: Use Joi.override to safely reset permitted values.
When extending or refining base schemas, calling .allow() appends new values to the existing whitelist by default. To replace previously permitted values or remove unwanted baseline defaults, pass Joi.override as the first argument to .allow().
Rule 5: Define polymorphic contracts explicitly with conditional schemas.
When validating polymorphic data structures with Joi.alternatives() or .conditional(), ensure all expected branches explicitly define input schemas and valid references to prevent runtime errors or insecure handling of dynamic payload types.
Rule 6: Explicitly define custom truthy and falsy input values.
By default, Joi.boolean() rejects common web/form boolean string representations. When parsing inputs from form submissions or non-standard external interfaces, explicitly declare acceptable non-boolean representations using .truthy() and .falsy() rather than disabling validation.
Rule 7: Validate ES6 class constructors with Joi class schema.
Use Joi.function().class() when a configuration or API specifically expects an ES6 class constructor rather than a standard callable function to prevent runtime type errors or unexpected execution behavior.
Rule 8: Enforce strict number validation to prevent boolean type confusion.
Use Joi.number() to strictly validate numeric input without implicit boolean coercion. Joi rejects true and false values for numeric schemas, preventing type-confusion bugs.
Enforce strict object property validation and prevent mass assignment
Use when
Validating untrusted object payloads prior to database storage or internal variable assignment to prevent unauthorized property injection.
Secure rules
Rule 1: Disallow unknown keys and strip unvalidated properties during validation
Ensure that allowUnknown is set to false (the default) or explicitly set stripUnknown to true to prevent unvetted input fields from passing through validation to downstream assignment operations.
Rule 2: Configure explicit presence constraints for required assignment fields
Use the presence option set to 'required' or 'forbidden' on validation options to ensure missing or unexpectedly present keys do not bypass assignment validation checks.
Rule 3: Disable implicit type coercion and enforce strict data types
Chain .strict() or set { convert: false } via schema preferences to ensure assigned inputs conform strictly to declared data types rather than undergoing automatic type coercion.
Rule 4: Restrict property assignment using valid values and forbidden fields
Use .valid(...values) to enforce strict whitelists of acceptable values for assigned fields, and use .forbidden() to explicitly block unauthorized attribute modifications.
Strictly Validate and Filter Unknown Fields in Object and Array Schemas
Use when
Defining object and array input schemas in joi to process untrusted payloads safely and prevent unvalidated properties or elements from reaching internal functions.
Secure rules
Rule 1: Reject or explicitly strip unknown keys on object schemas to prevent mass assignment and unauthorized data processing.
Avoid using bare Joi.object() without key definitions on untrusted inputs because empty object schemas permit arbitrary unknown keys. Instead, explicitly define object schemas to reject unknown fields by default or configure stripUnknown: true in validation options to remove unvalidated properties.
Rule 2: Configure explicit array strip options to safely filter out non-matching elements from untrusted array inputs.
When validating arrays using Joi.array().items(), passing a simple boolean stripUnknown: true preference does not automatically discard non-matching elements and instead causes validation to fail. Set stripUnknown: { arrays: true } in the preferences to correctly filter out unknown or unexpected array items.
Validate Complex Nested Models and Arrays with Explicit Schemas and Path References
Use when
Building schemas for structured, nested, or hierarchical data payloads where validation requires precise type definitions, unknown property rejection, and inter-field dependency checks across object levels.
Secure rules
Rule 1: Define explicit nested structures and restrict unknown fields to prevent malformed payloads and injection vectors.
Explicitly compose nested schemas using Joi.object() and Joi.array().items(), setting rules like .required() on mandatory nested fields. Use options such as allowUnknown: false and stripUnknown: { objects: true, arrays: true } to reject or strip unexpected properties and enforce strict schema bounds.
Rule 2: Enforce inter-field dependencies across parent and nested schemas using validated relative path references.
Use relative path references such as Joi.ref('...parentField') or Joi.ref('..0') within nested array items or child objects to validate dependencies against upper hierarchy levels. Ensure relative references stay within the bounds of the schema hierarchy to prevent runtime exceptions.
Rule 3: Use asynchronous validation for nested models containing external validation rules.
When defining nested object models that include custom asynchronous verification rules using .external(), always perform validation using validateAsync() rather than synchronous validation methods to prevent runtime errors.
const schema = Joi.object({ user: Joi.object({ id: Joi.string().external(async (value, helpers) => { const exists = await db.checkUserExists(value); if (!exists) { throw new Error('User ID does not exist'); } return value; }) })});const validated = await schema.validateAsync(payload);
Rule 4: Manage nested schema variations immutably using explicit IDs and forks.
Assign explicit IDs using .id('identifier') to target sub-schemas contained within complex structures, and use schema.fork() with property paths to adjust validation requirements contextually without mutating shared base schemas.
Defining schema default values and processing inputs where automatic default substitution must be explicitly controlled or disabled.
Secure rules
Rule 1: Disable schema defaults when validation must not add missing values
Joi applies a schema default when the original value is undefined. Pass noDefaults: true to validate() when the validated result must preserve missing values instead of populating them from schema defaults.
Enforce Strict Type Checking and Input Conversion Settings
Approximately 918 tokens
Use when
Validating untrusted input where exact types and unambiguous string interpretations are security-critical.
Secure rules
Rule 1: Disable implicit type casting on schemas to prevent unexpected data coercion.
By default, joi attempts to coerce input types to match schema definitions. When validating untrusted input where exact types are security-critical, call .strict() on the schema or configure { convert: false } in validation preferences to disable implicit type casting.
Rule 2: Disable input conversion for strict symbol mapping to prevent untrusted key execution.
When using Joi.symbol().map() to convert string or numeric input keys into internal Symbol instances, recognize that joi automatically coerces matching keys by default. To prevent untrusted string inputs from being translated into internal application Symbols, explicitly configure schema preferences with convert set to false.
Manage Object Key Aliasing and Renaming Explicitly
Use when
Renaming or aliasing object properties during schema validation to ensure unambiguous and canonical data interpretation.
Secure rules
Rule 1: Configure explicit alias options when renaming keys to prevent parameter ambiguity and unexpected data loss.
When using .rename(from, to, options), joi defaults to removing the source property key. Explicitly set alias: true in the rename options if downstream handlers require preserving the original property key alongside the renamed key, or leave it as false to eliminate legacy keys.
Rule 2: Isolate property alias transformations within schema alternative branches.
Ensure that key renaming operations inside schema alternatives are scoped safely. joi guarantees that key renaming side effects from a failed schema branch are discarded and not applied to subsequent alternative evaluations.
Prevent Prototype Pollution by Validating Untrusted Objects with Joi Schemas
Use when
Parsing and validating untrusted input objects and JSON payloads to ensure prototype properties like __proto__ are securely stripped or rejected.
Secure rules
Rule 1: Pass untrusted input objects through Joi.object() schemas to safely strip or ignore injected __proto__ properties.
When handling untrusted data, always run parsed input through explicit Joi object schemas using schema.validate() so that internal prototype properties and injected prototype pollution vectors are not preserved in validated output objects.
const Joi = require('joi');const schema = Joi.object({ name: Joi.string().required()});const payload = JSON.parse(untrustedInput);const { value, error } = schema.validate(payload);if (!error) { // value is safely validated and Object.prototype is preserved}
Rule 2: Enforce strict key boundaries and disallow unknown keys on object validation schemas.
Explicitly specify expected properties using .keys() and strictly enforce boundaries with .unknown(false) to prevent unvalidated properties such as __proto__ or constructor from persisting in validated output objects.
Enable HTML escaping for validation error messages and templates
Approximately 216 tokens
Use when
Rendering Joi validation error messages or dynamic templates in HTML user interfaces where user-supplied input may be present.
Secure rules
Rule 1: Set errors.escapeHtml to true when validating inputs to ensure error messages containing user input are safely HTML-escaped.
By default, escapeHtml is false. When validation error messages containing user-supplied input are rendered directly into HTML without escaping, malicious input can lead to Cross-Site Scripting (XSS) vulnerabilities. Configure errors.escapeHtml to true within the validate options.
Defining recursive validation schemas using Joi.link() to validate nested object hierarchies.
Secure rules
Rule 1: Configure maxRecursion on recursive link schemas to prevent excessive stack allocation and CPU consumption.
When defining recursive schemas using Joi.link(), always configure maxRecursion() to explicitly limit the depth of nested object validation and protect against Denial of Service conditions.
Rely on Schema Immutability and Store Chained Instance Results
Approximately 1,046 tokens
Use when
Building and configuring Joi validation schemas using method chaining.
Secure rules
Rule 1: Store or return the result of chained Joi method calls to ensure validation constraints remain active.
Joi schema chain methods do not mutate schema instances in place; they return new schema instances. Developers must store or return the result of chained method calls such as .valid(), .required(), or .email() to ensure validation constraints are active. Assuming that Joi schema methods mutate existing instances in place leaves schemas unconstrained, causing endpoints to accept invalid or malicious input.
const baseSchema = Joi.string();// Correct: store the newly created schema instanceconst restrictedSchema = baseSchema.valid('admin', 'user');// Incorrect (leaves baseSchema unconstrained):// baseSchema.valid('admin', 'user');
Secure custom validators and extension rules in Joi schemas
Use when
Building custom validation extensions, asynchronous hooks, or custom methods for Joi schemas.
Secure rules
Rule 1: Execute asynchronous validation for custom external rules
Always use schema.validateAsync() when schemas contain asynchronous custom validators registered via any.external(), as synchronous validation calls will either fail or skip critical checks when externals: false is configured.
const schema = Joi.object({ userId: Joi.string().external(async (value, helpers) => { const valid = await verifyUserExists(value); if (!valid) { throw new Error('User does not exist'); } return value; })});const result = await schema.validateAsync(inputData);
Wrap custom validation logic inside try-catch blocks and use helper methods like helpers.error() or helpers.message() to signal validation failures rather than letting uncaught exceptions propagate.
Validate user-provided configuration arguments inside method implementations using Joi.assert() or explicit type checks before assigning flags or mutating custom schema state.
Rule 4: Apply standard schema constraints before custom validator callbacks
Chain standard built-in Joi validation rules before custom external or custom validators so that execution halts on validation failure before invoking custom hooks.
Rule 5: Define error messages for all custom error codes in extensions
Map every custom error code returned via helpers.error() to a message template in the extension’s messages map to prevent unhandled runtime errors during validation.
Rule 6: Return standard error structures in custom schema extension validators
Inspect schema rules and flags using official methods such as schema.$_getRule() and schema.$_getFlag(), and return standard result objects containing { value, errors } via the error helper.
const CustomJoi = Joi.extend({ type: 'million', base: Joi.number(), messages: { 'million.base': '{{#label}} must be at least a million' }, coerce(value, { schema }) { if (schema.$_getRule('round')) { return { value: Math.round(value) }; } }, validate(value, { schema, error }) { if (value < 1000000) { return { value, errors: error('million.base') }; } }});