メインコンテンツまでスキップ
バージョン: 🚧 Canary

🪪 Validator

Validators are functions that perform additional validation beyond schema validation. They run before your route handlers and can check business logic, resource existence, authentication, and more.


🔍 What are Validators?

Validators are predicate functions that run in the request pipeline before handlers. They can:

  • Validate business logic (e.g., "does this resource exist?")
  • Check authentication (e.g., "is the user authorized?")
  • Validate parameters (e.g., "is this a valid UUID?")
  • Enforce constraints (e.g., "does the user have permission?")
┌─────────────┐
│ Request │
├─────────────┤
│ Schema │ ← JSON Schema validation
│ Validators │ ← Custom business logic
│ Handler │ ← Your route logic
└─────────────┘

Key characteristics:

  • Async functions - Can perform database queries
  • Throw on failure - Use NodeblocksError for consistent error responses
  • Composable - Chain multiple validators together
  • Context-aware - Access database, request params, and context

📋 Available Validators

Validators are organized by domain in the Backend Blocks documentation. Each block provides domain-specific validators for its operations.

Validator Categories

CategoryValidatorsDocumentation
AuthenticationisAuthenticated, verifyAuthenticationAuthentication Validators »
Commonsome, checkIdentityType, validateResourceAccess, requireParam, isUUID, isNumber, ownsResourceCommon Validators »
IdentityisSelfIdentity Validators »
UserownsProfile, validateUserProfileAccessUser Validators »
ChathasSubscription, ownsSubscription, ownsChannel, ownsMessage, validateChannelAccess, validateMessageAccess, hasOrganizationAccessToMessageTemplate, channelExistsChat Validators »
OrderownsOrder, validateOrderAccessOrder Validators »
OrganizationhasOrgRole, validateOrganizationAccessOrganization Validators »
CategorydoesCategoryExistCategory Validators »

ℹ️ Note: For detailed documentation on each validator, including parameters, behavior, and usage examples, see the specific block documentation linked above.


🛠️ Creating Custom Validators

Validator Function Signature

type Validator = (payload: primitives.RouteHandlerPayload) => Promise<void>;

Basic Validator Example

import { primitives } from '@nodeblocks/backend-sdk';

const validateUserExists = async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const userId = params.requestParams?.userId;

const user = await context.db.users.findOne({ id: userId });
if (!user) {
throw new primitives.NodeblocksError(404, 'User not found', 'USER_NOT_FOUND');
}
};

Advanced Validator Example

import { primitives } from '@nodeblocks/backend-sdk';

const validateUserPermission = async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const { userId, organizationId } = params.requestParams;

// Check if user exists
const user = await context.db.users.findOne({ id: userId });
if (!user) {
throw new primitives.NodeblocksError(404, 'User not found', 'USER_NOT_FOUND');
}

// Check if user belongs to organization
const membership = await context.db.memberships.findOne({
userId,
organizationId,
status: 'active'
});

if (!membership) {
throw new primitives.NodeblocksError(403, 'Access denied', 'ACCESS_DENIED');
}
};

Factory Function Pattern

Create reusable validator factories:

const requireResourceExists = (collectionName: string, paramName: string) => {
return async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const resourceId = params.requestParams?.[paramName];

const resource = await context.db[collectionName].findOne({ id: resourceId });
if (!resource) {
throw new primitives.NodeblocksError(
404,
`${collectionName} not found`,
'RESOURCE_NOT_FOUND'
);
}
};
};

// Usage
const validateProductExists = requireResourceExists('products', 'productId');
const validateOrderExists = requireResourceExists('orders', 'orderId');

🔗 Using Validators in Routes

Single Validator

export const getCategoryRoute = withRoute({
method: 'GET',
path: '/categories/:categoryId',
validators: [doesCategoryExist],
handler: getCategoryHandler,
});

Multiple Validators

export const protectedUserRoute = withRoute({
method: 'PATCH',
path: '/users/:userId',
validators: [
isAuthenticated(),
validateResourceAccess(['admin'])
],
handler: updateUserHandler,
});

Conditional Validators

const validators = [
// Only check authentication for non-public routes
...(isPublicRoute ? [] : [isAuthenticated()])
];

export const getUserRoute = withRoute({
method: 'GET',
path: '/users/:userId',
validators,
handler: getUserHandler,
});

Alternative Validation Paths

export const flexibleUserRoute = withRoute({
method: 'GET',
path: '/users/:identifier',
validators: [
// Use standard validators here. Legacy parameter validators like
// requireParam/isUUID/isNumber cannot be used directly with `some()`.
some(validateHasUserId, validateHasEmail),
validateUserExists
],
handler: getUserHandler,
});

🚨 Error Handling

Using NodeblocksError

Always use NodeblocksError for consistent error responses:

import { primitives } from '@nodeblocks/backend-sdk';

const validateResource = async (payload: primitives.RouteHandlerPayload) => {
// Your validation logic
if (!isValid) {
throw new primitives.NodeblocksError(
400, // HTTP status code
'Validation failed', // Human-readable message
'VALIDATION_ERROR' // Error code for client handling
);
}
};

Error Response Format

When validators throw errors, clients receive:

{
"error": {
"message": "Category does not exist"
}
}

📋 Validator Reference

For detailed documentation on each validator, see the Backend Blocks documentation:


📐 Best Practices

1. Fail Fast

  • Validators run before handlers, so fail early for invalid requests
  • Don't perform expensive operations in validators unless necessary

2. Reusable Patterns

  • Create factory functions for common validation patterns
  • Use domain-specific validators for business logic

3. Clear Error Messages

  • Provide descriptive error messages
  • Use consistent error codes for client-side handling

4. Database Efficiency

  • Use database indexes for validator queries
  • Consider caching for frequently accessed data

5. Composition

  • Chain validators logically (required → format → business logic)
  • Keep validators focused on single responsibilities

6. Access Control

  • Use appropriate access control validators for protected resources
  • Combine authentication and authorization validators
  • Consider using some() for flexible validation scenarios

➡️ Next