DEV Community

Cover image for I build payload-guard-filter
Sannu kumar
Sannu kumar

Posted on

I build payload-guard-filter

SHARE UR REVIEW
GIT HUB
LIVE PACKAGES

✨ Features

  • Shape-based filtering — Define what you want, auto-remove everything else
  • Sensitive field protectionpassword, token, secret automatically 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
# or
yarn add payload-guard
# or
pnpm add payload-guard
Enter fullscreen mode Exit fullscreen mode

🚀 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: 'john@example.com',
  password: 'secret123',      // ❌ Will be removed
  internalNotes: 'VIP user',  // ❌ Will be removed
};

const safeData = userShape(rawData);
// Result: { id: 1, name: 'John Doe', email: 'john@example.com' }
Enter fullscreen mode Exit fullscreen mode

Express Middleware

import express from 'express';
import { guard, guardMiddleware } from 'payload-guard';

const app = express();
app.use(express.json());

// Apply middleware
app.use(guardMiddleware({
  sanitizeBody: true,
  sensitiveFields: ['password', 'token'],
  devMode: process.env.NODE_ENV === 'development',
}));

const userShape = guard.shape({
  id: 'number',
  name: 'string',
  email: 'string',
});

app.post('/users', (req, res) => {
  const user = createUser(req.body);
  res.guardJson(userShape, user);  // ✅ Filtered response
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Frontend Usage

import { validateShape } from 'payload-guard/client';

const userShape = { id: 'number', name: 'string', email: 'string' };
const validateUser = validateShape(userShape, { devMode: true });

// Fetch and validate
const user = await fetch('/api/user')
  .then(r => r.json())
  .then(validateUser);

// Dev mode warnings:
// ⚠️ Unexpected field "createdAt" in response
// ⚠️ Missing field "email" in response
Enter fullscreen mode Exit fullscreen mode

📖 API Reference

guard.shape(descriptor, options?)

Create a filter function from a shape descriptor.

const userShape = guard.shape({
  id: 'number',
  name: 'string',
  email: 'string',
  role: { type: 'string', default: 'user' },
});
Enter fullscreen mode Exit fullscreen mode

Supported types:

  • 'string' — String values (auto-trimmed)
  • 'number' — Numeric values
  • 'boolean' — Boolean values
  • 'any' — Any value (no transformation)

Field config options:

{
  type: 'string',
  required: false,  // Optional field
  default: 'value', // Default value if missing
}
Enter fullscreen mode Exit fullscreen mode

guard.array(itemShape)

Create an array filter.

const postsShape = guard.shape({
  posts: guard.array({
    id: 'number',
    title: 'string',
  }),
});
Enter fullscreen mode Exit fullscreen mode

guardMiddleware(options)

Express middleware for automatic sanitization.

app.use(guardMiddleware({
  sanitizeBody: true,       // Filter req.body
  requestShape: userShape,  // Shape for request body
  filterResponse: true,     // Auto-filter all res.json()
  sensitiveFields: [],      // Extra sensitive field names
  devMode: false,           // Enable dev warnings
}));
Enter fullscreen mode Exit fullscreen mode

res.guardJson(shape, data)

Added by middleware — send filtered JSON response.

app.get('/user', (req, res) => {
  res.guardJson(userShape, userData);
});
Enter fullscreen mode Exit fullscreen mode

validateShape(shape, options?) (Client)

Create a validator for frontend use.

import { validateShape } from 'payload-guard/client';

const validate = validateShape(userShape, {
  devMode: true,   // Log warnings
  strict: false,   // Throw on errors
});
Enter fullscreen mode Exit fullscreen mode

🔒 Security

Default Sensitive Fields

These fields are automatically removed from all outputs:

  • password, password_hash, password_reset_token, pwd
  • token, access_token, refresh_token, auth_token
  • secret, api_key, private_key, encryption_key
  • authorization, auth, session_id
  • ssn, credit_card, cvv, card_number

Add Custom Sensitive Fields

guard.config({
  sensitiveFields: ['internal_id', 'admin_notes', 'salary'],
});
Enter fullscreen mode Exit fullscreen mode

🏗️ Nested Objects & Arrays

const postShape = guard.shape({
  id: 'number',
  title: 'string',
  author: guard.shape({
    id: 'number',
    name: 'string',
    // author.password, author.token auto-removed!
  }),
  tags: guard.array({
    id: 'number',
    name: 'string',
  }),
  comments: guard.array(
    guard.shape({
      id: 'number',
      text: 'string',
      user: guard.shape({
        id: 'number',
        name: 'string',
      }),
    })
  ),
});
Enter fullscreen mode Exit fullscreen mode

⚡ Performance

Operation payload-guard Zod JOI
Simple filter 0.05ms 0.8ms 1.2ms
Nested (3 levels) 0.15ms 2.5ms 3.8ms
Array (100 items) 1.2ms 15ms 22ms
Bundle size <8KB 50KB+ 70KB+
Dependencies 0 5+ 10+

🏢 Enterprise Features

These features are designed for high-throughput, production systems where bandwidth, predictability and observability matter.

  • Payload Metrics (payload reduction): enable measurements to show before/after sizes and percentage saved. This makes cost and bandwidth impact visible to engineers.
// enable metrics globally
import { guard } from 'payload-guard';

guard.config({
  payloadMetrics: true,
  logger: (msg, level = 'info') => console.log(`[payload-guard:${level}]`, msg),
});
Enter fullscreen mode Exit fullscreen mode

When enabled middleware or res.guardJson() will log reductions such as:

payload reduced: 18KB → 4KB (78%)
Enter fullscreen mode Exit fullscreen mode
  • Strict Mode: in strict mode the filter will throw on invalid or missing required fields instead of silently ignoring them. Use when you want validation failures to fail-fast in production.
// per-shape strict mode
const userStrict = guard.shape({ id: 'number', email: 'string' }, { strict: true });

// or global
guard.config({ strict: true });
Enter fullscreen mode Exit fullscreen mode
  • Compile Mode: pre-compile shapes once and reuse the compiled function for maximum throughput. This avoids repeated runtime parsing and makes performance predictable.
// compile once at startup
const compiledUser = guard.compile({ id: 'number', name: 'string' });

// then use per-request
app.get('/user/:id', (req, res) => {
const raw = getUserFromDb();
  res.json(compiledUser(raw));
});
Enter fullscreen mode Exit fullscreen mode
  • Ignore Routes: skip middleware processing for low-value routes (health checks, metrics, webhooks) to avoid adding overhead.
app.use(guardMiddleware({ ignoreRoutes: ['/health', '/metrics', '/webhook'] }));
Enter fullscreen mode Exit fullscreen mode
  • Middleware non-blocking guarantee: the middleware is defensive — any internal error, oversized payload, or stream-like body will be skipped and the response path will continue unblocked. Use logger or devMode to surface warnings.

🛠️ CLI

npx payload-guard init
Enter fullscreen mode Exit fullscreen mode

Creates example files in your project:

  • shapes.ts — Example shape definitions
  • server-example.ts — Express middleware setup

📄 License

MIT


Made with ❤️ for safer APIs

Top comments (0)