Advanced Route Features

Router V2 exposes production-critical features directly on route definitions and route groups: Go-Native Rate Limiting & XTRS, Response Caching, and Lifecycle Hooks. Declaring these at the route or group level ensures explicit intent, co-located business logic, and full visibility in the routing registry.

Rate Limiting (Go-Native XHSC & XTRS)

Rate limiting in XyPriss is executed 100% natively in Go via the XHSC (XyPriss Hyper-System Core) engine. It operates before JavaScript execution, ensuring ultra-low overhead and zero Node.js event-loop blocking.

Rate limiting can be declared as a string shorthand, a standard configuration object, or an XTRS (Temporal Rate Shield) policy inside the xtrs property.

1. String Shorthand

Pass a quick duration string directly to rateLimit:

typescript
router.get("/api/search", { rateLimit: "10/1m" }, (req, res) => {
    res.json({ results: [] });
});

2. Standard Configuration Object

Standard rate limit options remain clean and explicit at the root of rateLimit:

typescript
router.get(
    "/api/export",
    {
        rateLimit: {
            max: 10,
            window: "1m", // or windowMs: 60_000
            message: "Rate limit exceeded. Please retry in 1 minute.",
            statusCode: 429,
            keyBy: "ip", // "ip" | "user" | (req) => string
        },
    },
    (req, res) => {
        res.json({ data: "..." });
    },
);

3. XTRS (Temporal Rate Shield) & Multi-Window Protection

Advanced multi-window rules, rate limits by second (limit: "3/s"), and temporary IP blocks are nested cleanly inside rateLimit.xtrs:

typescript
router.post(
    "/api/auth/login",
    {
        rateLimit: {
            xtrs: {
                rules: [
                    {
                        rule: "5/1m",
                        message: "Too many login attempts. Account temporarily locked for 5 minutes.",
                        blockDuration: "5m",
                        statusCode: 429,
                    },
                ],
            },
        },
    },
    async (req, res) => {
        // Login logic
    },
);

You can also pass an XTRS shorthand string or single limit directly inside xtrs:

typescript
router.post(
    "/upload",
    {
        rateLimit: {
            xtrs: {
                limit: "3/s",
                blockDuration: "10s",
            },
        },
    },
    Upload.single("file"),
    CdnController.upload,
);

4. Route Group Rate Limiting

Rate limiting can also be declared globally for an entire route group via router.group(...). Group rate limits apply natively in Go across all routes inside the group, including unhandled 404 requests matching the group prefix:

typescript
router.group(
    {
        prefix: "/stream",
        rateLimit: {
            xtrs: {
                limit: "3/1m",
                message: "Stream group rate limit exceeded!",
                blockDuration: "20s",
            },
        },
    },
    (stream) => {
        stream.get("/video", (req, res) => {
            res.send("Video stream");
        });
    },
);

Rate Limit Options Reference (RoutRateLimit)

OptionTypeDescription
maxnumberMaximum requests allowed per window (Standard Rate Limit).
windowstring | numberWindow duration string ("10s", "1m", "1h") or milliseconds.
windowMsnumberWindow duration in milliseconds (takes precedence over window).
messagestring | anyError message or JSON object returned when limit is exceeded.
statusCodenumberHTTP status code returned when blocked (defaults to 429).
keyBy"ip" | "user" | (req) => stringIdentification strategy for tracking requests (defaults to IP).
xtrsstring | XtrsOptionsAdvanced multi-window XTRS policy object or shorthand string ("3/s").

XTRS Options Object (XtrsOptions)

PropertyTypeDescription
limitstring | numberShorthand limit string (e.g. "3/s", "5/1m").
rulesXtrsRuleInput[]Array of multi-window XTRS rules (e.g. [{ rule: "5/1m", blockDuration: "5m" }]).
blockDurationstring | numberTemporary IP ban duration after breaching threshold (e.g. "20s", "5m").
blockDurationMsnumberBlock duration in milliseconds.
messagestring | anyXTRS error message.
statusCodenumberHTTP status code (defaults to 429).
Type Definition
export type RoutRateLimitInput = string | RoutRateLimit;

Response Caching

Cache the response of GET routes in-memory to eliminate redundant handler invocations and database lookups.

typescript
router.get("/api/products", { cache: "5m" }, (req, res) => {
    // This handler runs once per 5 minutes;
    // subsequent hits are served from the native cache.
    const products = db.getAll();
    res.json({ products });
});

Options Reference

OptionTypeDescription
cachestring | numberTTL as a duration string ("10s", "1m", "1h") or milliseconds.
key(req) => stringOptional custom cache key generator (defaults to the request URL).
Mutation Safety
Caching applies exclusively to GET routes. Mutation endpoints (POST, PUT, PATCH, DELETE) are never cached.

Lifecycle Hooks

Lifecycle hooks intercept the request at precisely defined stages without interfering with the normal handler chain.

beforeEnter

Runs before the main handler. Use for input validation & enriching req.

afterLeave

Runs after the response is sent. Safe for async logging & metrics.

onError

Fires if a hook or handler throws. For structured error responses.

Lifecycle Hooks Example
router.get(
    "/api/users/:id",
    {
        beforeEnter(req, res, next) {
            if (!req.params.id.match(/^\d+$/)) {
                return res.status(400).json({ error: "Invalid ID format" });
            }
            next();
        },
        afterLeave(req, res) {
            // Fires after the response is already sent — safe for async analytics
            analytics.track("user.viewed", { id: req.params.id });
        },
    },
    (req, res) => {
        res.json({ userId: req.params.id });
    },
);

Correct onError Patterns

onError Protocol
If you define an onError hook, you must terminate the request. Calling next() inside onError does not terminate the connection and will cause the request to hang indefinitely.
onError Best Practices
// WRONG — request hangs
onError(err, req, res, next) {
    console.error(err);
    next(); // Do not use next() here
}

// CORRECT — respond to the client
onError(err, req, res) {
    res.status(500).json({ error: "Internal Server Error" });
}

// CORRECT — bubble up to the global error handler
onError(err) {
    throw err;
}

Combining All Features

Combine security guards, rate limiting rules, lifecycle hooks, and error handling in a single robust endpoint definition:

typescript
router.post(
    "/api/orders",
    {
        guards: [authGuard, subscriptionGuard],
        rateLimit: {
            max: 20,
            window: "1m",
            message: "Rate limit exceeded for order placement",
        },
        beforeEnter(req, res, next) {
            if (!req.body.items?.length) {
                return res
                    .status(400)
                    .json({ error: "Order must have at least one item" });
            }
            next();
        },
        onError(err, req, res) {
            res.status(500).json({ error: err.message });
        },
    },
    async (req, res) => {
        const order = await OrderService.create(req.body);
        res.status(201).json({ order });
    },
);
Live Inspection

Learn how to inspect your routing tree and debug your API routes in real-time.