Groups & API Versioning

Route groups allow you to apply shared configuration—such as prefixes, guards, and middleware—to a logical set of routes. This significantly reduces boilerplate and enforces consistency across your API.

Basic Groups

Use router.group() to define a set of routes that share a common URL prefix.

typescript
router.group({ prefix: "/users" }, (group) => {
    group.get("/", (req, res) => res.success("User list"));
    group.post("/", (req, res) => res.success("User created"));
    group.get("/:id", (req, res) => res.success(`User ${req.params.id}`));
});
// Resulting paths: GET /users/, POST /users/, GET /users/:id

Nested Groups

Groups can be arbitrarily nested to produce complex hierarchical APIs with overlapping shared logic.

typescript
router.group({ prefix: "/api" }, (api) => {
    api.group({ prefix: "/v1" }, (v1) => {
        v1.get("/status", (req, res) => res.success("V1 OK"));
    });

    api.group({ prefix: "/v2" }, (v2) => {
        v2.get("/status", (req, res) => res.success("V2 OK"));
        v2.get("/users", (req, res) =>
            res.json({ users: [], meta: { page: 1 } }),
        );
    });
});
// Resulting paths: /api/v1/status, /api/v2/status, /api/v2/users

API Versioning

XyPriss provides a semantic shorthand for versioned APIs, ensuring consistency and readability.

Using router.version()

typescript
router.version("v1", (r) => {
    r.get("/users", (req, res) => res.success("V1 Users"));
});

router.version("v2", (r) => {
    r.get("/users", (req, res) => res.success("V2 Users"));
});
// Paths: /v1/users, /v2/users
Path Order
When both prefix and version are provided in a group config, the version is always prepended to the prefix:/{version}/{prefix}.

Group Guards & Security

Security guards applied at the group level are automatically inherited by every route within that group. This is the recommended approach for protecting entire API namespaces.

typescript
const adminGuard = (req: any, _res: any) => {
    if (req.user?.role === "admin") return true;
    return "Admin access required";
};

router.group(
    {
        prefix: "/admin",
        guards: [adminGuard],
    },
    (admin) => {
        admin.get("/dashboard", (req, res) => res.success("Admin dashboard"));
        admin.get("/users", (req, res) => res.json({ users: [] }));
        // adminGuard runs before BOTH routes
    },
);
Parameters & Constraints

Learn how to handle dynamic parameters, regex constraints, and query parsing.