🪪 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
NodeblocksErrorfor 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
| Category | Validators | Documentation |
|---|---|---|
| Authentication | isAuthenticated, verifyAuthentication | Authentication Validators » |
| Common | some, checkIdentityType, validateResourceAccess, requireParam, isUUID, isNumber, ownsResource | Common Validators » |
| Identity | isSelf | Identity Validators » |
| User | ownsProfile, validateUserProfileAccess | User Validators » |
| Chat | hasSubscription, ownsSubscription, ownsChannel, ownsMessage, validateChannelAccess, validateMessageAccess, hasOrganizationAccessToMessageTemplate, channelExists | Chat Validators » |
| Order | ownsOrder, validateOrderAccess | Order Validators » |
| Organization | hasOrgRole, validateOrganizationAccess | Organization Validators » |
| Category | doesCategoryExist | Category 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:
- Authentication Validators
- Common Validators
- Identity Validators
- User Validators
- Chat Validators
- Order Validators
- Organization Validators
- Category Validators
📐 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
- Learn about Route Composition for building complex routes
- Explore Error Handling for consistent error responses
- Check out Service Patterns for organizing your API