payload-guard-filter
v1.8.1
Published
Lightweight, zero-dependency shape-based payload filtering and sanitization for Node.js and browser
Maintainers
Readme
payload-guard
Part of the Professional Node.js Backend Toolkit
🛡️ Workflow
graph LR
A[Request] --> B(Gatekeeper)
B --> C{Shape Check}
C -- Valid --> D[Redact & Clean]
C -- Invalid --> E[Strict Error / Fail Safe]
D --> F[Secure Response]
E --> F
F --> G((Metrics))✨ Features
- Shape-based filtering — Define what you want, auto-remove everything else
- Sensitive field protection —
password,token,secretautomatically removed - Zero dependencies — Pure TypeScript, no external packages
- Universal — Works in Node.js, Browser, React Native
- TypeScript-first — Full type inference from shape definitions
- Blazing fast — Compiled schemas for production performance
- Never crashes — Graceful failure mode, production-safe
📦 Installation
npm install payload-guard🚀 Quick Start
Basic Usage
import { guard } from 'payload-guard';
// Define a shape
const userShape = guard.shape({
id: 'number',
name: 'string',
email: 'string',
});
// Filter data
const rawData = {
id: 1,
name: 'John Doe',
email: '[email protected]',
password: 'secret123', // ❌ Will be removed
internalNotes: 'VIP user', // ❌ Will be removed
};
const safeData = userShape(rawData);
// Result: { id: 1, name: 'John Doe', email: '[email protected]' }Advanced Validation (v1.4+)
const userShape = guard.shape({
email: guard.string().email().toLowerCase().trim(),
age: guard.number().min(18).max(100).default(18),
tags: guard.array(guard.string().min(2)),
role: guard.string().validate(v => ['admin', 'user'].includes(v)).default('user'),
bio: guard.string().max(200).optional(),
});Nested Object Validation (v1.7+)
Define nested shapes for deep object validation:
const userShape = guard.shape({
id: 'number',
profile: {
name: 'string',
email: guard.string().email(),
age: guard.number().min(18),
address: {
street: 'string',
city: 'string',
zipCode: guard.string().regex(/^\d{5}(-\d{4})?$/),
},
},
posts: guard.array({
id: 'number',
title: 'string',
tags: guard.array('string'),
}),
});
const input = {
id: 1,
profile: {
name: 'John',
email: '[email protected]',
age: 30,
address: {
street: '123 Main St',
city: 'NYC',
zipCode: '10001',
},
},
posts: [
{ id: 1, title: 'Hello', tags: ['intro'] },
],
};
const result = userShape(input);
// Filters all nested levels automaticallyCustom Error Messages (v1.7+)
Add per-field custom error messages:
const userShape = guard.shape({
email: guard
.string()
.email()
.error('Please provide a valid email address'),
username: guard
.string()
.min(5)
.error((value, field) => `${field} must be at least 5 characters`),
age: guard
.number()
.min(18)
.max(100)
.errorCodes({ min: 'AGE_TOO_YOUNG', max: 'AGE_TOO_OLD' }),
});
// Collect validation errors
const { compile } = require('payload-guard');
const errors = [];
const validator = compile(
{ email: { type: 'string', email: true } },
{ collectErrors: true, _errors: errors }
);
validator({ email: 'invalid' });
// errors: [{ field: 'email', message: '...', code: 'email' }]📖 API Reference
guard.string()
Creates a string builder with chained constraints:
.min(length)— Minimum character length.max(length)— Maximum character length.email()— Basic email validation.regex(pattern)— Regex pattern match.trim()— Auto-trim whitespace (Transformation).toLowerCase()/.toUpperCase()— Case transformation
guard.number()
.min(value)/.max(value)— Range validation.integer()— Ensure number is an integer.positive()— Shortcut for.min(0)
Common Builder Methods
.required()/.optional()— Toggle requirement.default(value)— Value to use if field is missing or invalid.transform(fn)— Custom transformation function.validate(fn)— Custom validation function (returnfalseto fail).error(message)— Custom error message (string or function) (v1.7+).errorCodes(codes)— Custom error codes for validations (v1.7+)
New in v1.7+
Nested Object Support:
- Define nested shapes as plain objects:
{ profile: { name: 'string', email: 'string' } } - Arrays with nested objects:
guard.array({ id: 'number', name: 'string' }) - Deep nesting supported at any level
Custom Error Messages:
.error('Custom message')— Static error message.error((value, field) => \${field} is invalid`)` — Dynamic error message.errorCodes({ min: 'TOO_SHORT', email: 'BAD_EMAIL' })— Error codes
Error Collection:
const { compile } = require('payload-guard');
const errors = [];
const validator = compile(shape, { collectErrors: true, _errors: errors });
validator(data);
// errors array populated with { field, message, code, value }Field Aliasing & Mapping (v1.6+)
Rename fields from your database to match your frontend API:
const userShape = guard.shape({
id: guard.string().from('_id'), // Map DB _id to id
workTime: guard.number().from('totalWorkTimeMs').transform(v => v / 1000),
name: 'string',
});
// Input: { _id: '123', totalWorkTimeMs: 5000, name: 'Sannu' }
// Output: { id: '123', workTime: 5, name: 'Sannu' }🛡️ "Never Crash" Policy
Payload Guard is designed for mission-critical enterprise environments where uptime is non-negotiable.
- Circular Reference Safety: Automatically detects and handles circular objects without infinite loops or stack overflows.
- Hook Isolation: Custom
.transform()and.validate()callbacks are wrapped in internal try/catch blocks. If your code fails, the library logs the error and safely continues using fallback values. - Middleware Fail-Open: If internal filtering logic hits an unexpected edge case,
failOpen: trueensures the original request/response still reaches its destination.
⚡ Performance
| Benchmark | ops/sec | avg (ms) | |-----------|---------|----------| | Small payload (5 fields) | 449,365 | 0.0022ms | | Medium payload (50 posts) | 7,791 | 0.1284ms | | Large payload (1000 users) | 246 | 4.0724ms |
Memory Usage: ~121 MB Heap Used (Stable)
🛡️ Fail-Safe Design
Built for production reliability:
- Isolation: Monitoring failures never break your API responses. If a storage adapter or plugin crashes, the error is caught and logged, while the user's request continues normally.
- Async-Only: All processing is non-blocking to ensure zero impact on event loop latency.
⛑️ Maintained actively.
Bug fixes usually within 24–48 hours.
📄 License
MIT
