npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sentrix

v1.0.3

Published

Comprehensive security middleware library for Express.js

Readme

SentriX

Stop worrying about security. Start building.

SentriX is a simple, all-in-one security package for Express.js that protects your API from common attacks. No security expertise needed – just install and use.

npm install sentrix

Why SentriX?

Building a secure API is hard. You need to worry about:

  • Hackers stealing tokens
  • Bots spamming your endpoints
  • Form submission attacks
  • Malicious code injection
  • Invalid data breaking your app

SentriX handles all of this automatically. Just add one line of code.


30-Second Setup

Step 1: Install

npm install sentrix express cookie-parser

Step 2: Create .env file

JWT_SECRET=put-any-random-32-character-string-here-abc123

Step 3: Add to your Express app

import express from 'express';
import cookieParser from 'cookie-parser';
import { sentrixMiddleware, sentrixErrorHandler } from 'sentrix';

const app = express();

app.use(express.json());
app.use(cookieParser());

// ONE LINE = Full Security
app.post('/api/data', sentrixMiddleware(), (req, res) => {
    res.json({ message: 'This route is now protected!' });
});

// Error handler (catches all security errors)
app.use(sentrixErrorHandler);

app.listen(3000);

Done! Your API now has:

  • Protection against bots and spam
  • Safe from malicious code injection
  • Security headers (blocks common attacks)
  • Automatic rate limiting (100 requests/hour per user)

When Do I Use What?

Scenario 1: Public API (Anyone can access)

Example: Blog posts, product catalog, weather data

app.get('/api/products', 
    sentrixMiddleware(),  // Basic security only
    (req, res) => {
        res.json({ products: [...] });
    }
);

What it does:

  • Blocks spam/bots
  • Prevents XSS attacks
  • Adds security headers

Scenario 2: Login-Protected (User must be logged in)

Example: User profile, account settings, private data

app.get('/api/user/profile', 
    sentrixMiddleware({ requireAuth: true }),  // Requires login
    (req, res) => {
        // req.user has the logged-in user info
        res.json({ name: req.user.name });
    }
);

What it does:

  • Checks user is logged in (via JWT token)
  • Blocks unauthorized users (401 error)
  • All the basic security too

Client needs to send:

fetch('/api/user/profile', {
    headers: {
        'Authorization': 'Bearer USER_TOKEN_HERE'
    }
})

Scenario 3: Form Submission (Changing data)

Example: Update profile, create post, delete item, payment

// Step 1: Give user a CSRF token (when page loads)
app.get('/api/get-token', 
    generateCsrfMiddleware,
    (req, res) => {
        res.json({ token: res.locals.csrfToken });
    }
);

// Step 2: Require token when submitting form
app.post('/api/user/settings', 
    sentrixMiddleware({ 
        requireAuth: true,   // Must be logged in
        requireCSRF: true    // Must have valid token
    }),
    (req, res) => {
        res.json({ message: 'Settings saved!' });
    }
);

What it does:

  • Prevents CSRF attacks (hackers can't forge requests)
  • Ensures request came from your website

Client code:

// Get token first
const { token } = await fetch('/api/get-token').then(r => r.json());

// Use token when submitting
fetch('/api/user/settings', {
    method: 'POST',
    headers: {
        'Authorization': 'Bearer USER_TOKEN',
        'x-csrf-token': token,  // Important!
        'Content-Type': 'application/json'
    },
    credentials: 'include',  // Important!
    body: JSON.stringify({ theme: 'dark' })
});

Scenario 4: Validate User Input (Ensure data is correct)

Example: User registration, contact form, checkout

import { z } from 'zod';

// Define what valid data looks like
const schema = z.object({
    body: z.object({
        email: z.string().email(),           // Must be valid email
        age: z.number().min(18),             // Must be 18+
        name: z.string().min(1).max(100)     // 1-100 characters
    })
});

app.post('/api/register', 
    sentrixMiddleware({ schema }),  // Auto-validates
    (req, res) => {
        // If code reaches here, data is valid!
        const { email, age, name } = req.body;
        res.json({ message: 'User registered!' });
    }
);

What it does:

  • Checks data format before your code runs
  • Returns clear error if invalid
  • Prevents bad data from breaking your app

Example responses:

Valid request:

{ "message": "User registered!" }

Invalid request:

{
    "error": "Invalid request payload",
    "details": {
        "errors": [
            { "path": "body.email", "message": "Invalid email" },
            { "path": "body.age", "message": "Must be 18 or older" }
        ]
    }
}

Scenario 5: Strict Rate Limits (High-security endpoints)

Example: Login, password reset, payment, admin actions

app.post('/api/login', 
    sentrixMiddleware({
        rateLimitOptions: {
            maxRequests: 5,              // Only 5 tries
            windowMs: 15 * 60 * 1000     // Per 15 minutes
        }
    }),
    (req, res) => {
        // Login logic
        res.json({ token: 'user_token' });
    }
);

What it does:

  • Limits login attempts (stops brute force attacks)
  • Blocks user for 15 min after 5 failed tries
  • Prevents account takeover

Scenario 6: Call External APIs Safely

Example: Fetch GitHub data, Stripe payment, SendGrid emails

import { SafeHttpClient } from 'sentrix';

const httpClient = new SafeHttpClient({
    domains: ['api.github.com', 'api.stripe.com'],  // Only these allowed
    timeoutMs: 5000,
    retries: 2
});

app.get('/api/github/:user',
    sentrixMiddleware({ requireAuth: true }),
    async (req, res, next) => {
        try {
            const response = await httpClient.get(
                `https://api.github.com/users/${req.params.user}`
            );
            const data = await response.json();
            res.json(data);
        } catch (error) {
            next(error);
        }
    }
);

What it does:

  • Only allows calls to approved domains
  • Blocks calls to malicious sites
  • Auto-retries on failure
  • Times out if too slow

Complete Example (Real App)

import express from 'express';
import cookieParser from 'cookie-parser';
import { z } from 'zod';
import { 
    sentrixMiddleware, 
    sentrixErrorHandler,
    generateCsrfMiddleware 
} from 'sentrix';

const app = express();

app.use(express.json());
app.use(cookieParser());

// PUBLIC ROUTES

// Health check
app.get('/health', 
    sentrixMiddleware({ enableDoS: false }),  // No rate limit
    (req, res) => res.json({ status: 'ok' })
);

// Get products (public)
app.get('/api/products', 
    sentrixMiddleware(),  // Basic security
    (req, res) => res.json({ products: [] })
);

// Get CSRF token
app.get('/api/csrf-token',
    generateCsrfMiddleware,
    (req, res) => res.json({ token: res.locals.csrfToken })
);

// Login (strict rate limit)
const loginSchema = z.object({
    body: z.object({
        email: z.string().email(),
        password: z.string().min(8)
    })
});

app.post('/api/login',
    sentrixMiddleware({
        schema: loginSchema,
        rateLimitOptions: { maxRequests: 5, windowMs: 900000 }
    }),
    (req, res) => {
        // Check credentials, generate JWT
        res.json({ token: 'generated_jwt_token' });
    }
);

// PROTECTED ROUTES (Login Required)

// Get user profile
app.get('/api/user/profile',
    sentrixMiddleware({ requireAuth: true }),
    (req, res) => {
        res.json({ 
            userId: req.user.id,
            name: req.user.name 
        });
    }
);

// Update profile
const updateSchema = z.object({
    body: z.object({
        name: z.string().min(1).max(100),
        bio: z.string().max(500).optional()
    })
});

app.put('/api/user/profile',
    sentrixMiddleware({
        schema: updateSchema,
        requireAuth: true,
        requireCSRF: true
    }),
    (req, res) => {
        // Update user in database
        res.json({ message: 'Profile updated!' });
    }
);

// Create post
const postSchema = z.object({
    body: z.object({
        title: z.string().min(1).max(200),
        content: z.string().min(1)
    })
});

app.post('/api/posts',
    sentrixMiddleware({
        schema: postSchema,
        requireAuth: true,
        requireCSRF: true
    }),
    (req, res) => {
        // Save post to database
        res.json({ message: 'Post created!', post: req.body });
    }
);

// ERROR HANDLER (MUST BE LAST)
app.use(sentrixErrorHandler);

app.listen(3000, () => {
    console.log('Server running on http://localhost:3000');
});

Configuration

Environment Variables (.env file)

# Required
JWT_SECRET=your-32-char-secret-here-change-this-in-production

# Optional (with defaults)
NODE_ENV=development           # development, production, or test
LOG_LEVEL=info                # debug, info, warn, error
PORT=3000
TRUST_PROXY=false             # Set true if behind nginx/load balancer
SAFE_DOMAINS=api.example.com  # Comma-separated
REDIS_URL=redis://localhost:6379  # For distributed rate limiting

Generate Strong JWT Secret

# On Mac/Linux
openssl rand -base64 32

# Copy output to .env
JWT_SECRET=the-random-string-from-above

Common Issues & Solutions

"Missing JWT token" error

Problem: Frontend not sending Authorization header

Solution:

fetch('/api/profile', {
    headers: {
        'Authorization': `Bearer ${yourToken}`  // Add this!
    }
})

"Invalid CSRF token" error

Problem: Not including token or cookies

Solution:

fetch('/api/settings', {
    headers: {
        'x-csrf-token': token  // Add this!
    },
    credentials: 'include'  // AND this!
})

"Too many requests" error (429)

Problem: Hit rate limit

Solutions:

  • Wait 15-60 minutes (depends on endpoint)
  • Or increase limit in production:
sentrixMiddleware({
    rateLimitOptions: {
        maxRequests: 1000,  // Increase for production
        windowMs: 3600000   // 1 hour
    }
})

Rate limiting not working across servers

Problem: Running multiple server instances

Solution: Use Redis

# Install Redis
brew install redis  # Mac
sudo apt install redis  # Linux

# Start Redis
redis-server

# Add to .env
REDIS_URL=redis://localhost:6379

All Options

sentrixMiddleware({
    // Validation
    schema: zodSchema,           // Validate request with Zod

    // Authentication
    requireAuth: false,          // true = require JWT token

    // CSRF Protection  
    requireCSRF: false,          // true = require CSRF token

    // Rate Limiting
    enableDoS: true,             // false = disable rate limiting
    rateLimitOptions: {
        maxRequests: 100,        // Max requests per window
        windowMs: 3600000,       // 1 hour window
        maxPayloadSize: 1048576  // Max 1MB request size
    }
})

Ready for Production?

Checklist before deploying:

  • [ ] Change JWT_SECRET to strong random value
  • [ ] Set NODE_ENV=production in .env
  • [ ] Enable TRUST_PROXY=true if using nginx/load balancer
  • [ ] Set up Redis for multi-server rate limiting
  • [ ] Use HTTPS (not HTTP)
  • [ ] Set LOG_LEVEL=warn or error
  • [ ] Test all endpoints with real frontend
  • [ ] Monitor error logs

License

MIT License - use it however you want!