Gathering detailed insights and metrics for @vortex-js/core
Gathering detailed insights and metrics for @vortex-js/core
Gathering detailed insights and metrics for @vortex-js/core
Gathering detailed insights and metrics for @vortex-js/core
npm install @vortex-js/core
Typescript
Module System
Min. Node Version
Node Version
NPM Version
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
4
1
A comprehensive TypeScript framework for building Express.js APIs with built-in role-based access control (RBAC) and automatic Postman documentation generation.
1npm install @vortex-js/core 2# or 3yarn add @vortex-js/core
Here's a simple example to get you started:
1import express from "express"; 2import { AppWrapper, Routes, Route } from "@vortex-js/core"; 3 4// Create your Express app 5const app = express(); 6 7// Define your routes 8const apiRoutes = new Routes({ 9 prefix: "/api", 10 routes: [ 11 new Route({ 12 method: "GET", 13 path: "/users", 14 middlewares: [ 15 (req, res) => { 16 res.json({ users: ["John", "Jane"] }); 17 }, 18 ], 19 name: "Get Users", 20 description: "Retrieve a list of all users", 21 rai: "users:read", 22 roles: ["admin", "user"], 23 }), 24 new Route({ 25 method: "POST", 26 path: "/users", 27 middlewares: [ 28 (req, res) => { 29 res.status(201).json({ message: "User created" }); 30 }, 31 ], 32 name: "Create User", 33 description: "Create a new user", 34 rai: "users:create", 35 roles: ["admin"], 36 }), 37 ], 38}); 39 40// Create app wrapper 41const appWrapper = new AppWrapper({ 42 app, 43 routes: apiRoutes, 44 roles: ["admin", "user", "guest"], 45 postman: { 46 name: "My API", 47 description: "API documentation", 48 baseUrl: "http://localhost:3000", 49 }, 50}); 51 52// Get configured Express app 53const configuredApp = appWrapper.getExpressApp(); 54 55// Generate Postman documentation 56appWrapper.generatePostmanCollection("./collection.json"); 57appWrapper.generatePostmanEnvironment("./environment.json"); 58 59// Start the server 60configuredApp.listen(3000, () => { 61 console.log("Server running on port 3000"); 62});
The AppWrapper
is the main entry point of the framework. It wraps your Express application and provides:
1const appWrapper = new AppWrapper({ 2 app: expressApp, // Your Express application 3 routes: routesInstance, // Routes configuration 4 roles: ["admin", "user"], // Available roles 5 postman: { 6 // Optional Postman configuration 7 name: "My API", 8 description: "API Documentation", 9 baseUrl: "http://localhost:3000", 10 version: "1.0.0", 11 }, 12}); 13 14// Get the configured Express app 15const configuredApp = appWrapper.getExpressApp();
The Routes
class represents a group of routes with shared configuration:
1const userRoutes = new Routes({ 2 prefix: "/users", // URL prefix for all contained routes 3 routes: [route1, route2, route3], // Array of Route or Routes instances 4 middlewares: [authMiddleware], // Optional shared middleware 5 params: [ 6 { 7 // Optional URL parameters 8 path: "userId", 9 method: (req, res, next, id) => { 10 // Parameter handling logic 11 next(); 12 }, 13 }, 14 ], 15 postman: { 16 // Optional Postman folder configuration 17 folderName: "User Management", 18 }, 19 error: errorRoute, // Optional error handling 20});
The Route
class represents an individual API endpoint:
1const getUserRoute = new Route({ 2 method: "GET", // HTTP method (GET, POST, PUT, DELETE) 3 path: "/:id", // URL path (will be combined with Routes prefix) 4 middlewares: [ 5 // Array of Express middleware functions 6 (req, res) => { 7 res.json({ user: { id: req.params.id, name: "John" } }); 8 }, 9 ], 10 name: "Get User", // Name for documentation 11 description: "Get user by ID", // Description for documentation 12 rai: "users:read", // Resource Access Identifier 13 roles: ["admin", "user"], // Roles that can access this route 14 postman: { 15 // Optional Postman configuration 16 body: { 17 // Example request body 18 name: "John Doe", 19 email: "john@example.com", 20 }, 21 params: [ 22 { 23 // Example URL parameters 24 key: "id", 25 value: "123", 26 description: "User ID", 27 }, 28 ], 29 }, 30});
The ErrorRoute
class provides custom error handling for a route group:
1const customErrorHandler = new ErrorRoute({ 2 middleware: (err, req, res, next) => { 3 if (err.name === 'ValidationError') { 4 return res.status(400).json({ error: err.message }); 5 } 6 next(err); 7 } 8}); 9 10const apiRoutes = new Routes({ 11 prefix: '/api', 12 routes: [...], 13 error: customErrorHandler 14});
Role Access Identifiers (RAIs) are unique strings that identify resources in your API. Each route has an RAI and a list of roles that can access it. The framework automatically:
1// Define a route with RAI and roles 2const route = new Route({ 3 // ...other properties 4 rai: "users:update", // The resource being accessed 5 roles: ["admin", "owner"], // Roles that can access this resource 6}); 7 8// User authentication should set the user's roles 9app.use((req, res, next) => { 10 req.user = { 11 roles: ["user", "owner"], // This user has 'user' and 'owner' roles 12 }; 13 next(); 14}); 15 16// The framework middleware will check if the user can access the route 17// In this example, the user has the 'owner' role, so access is granted
The framework automatically generates Postman collections and environments from your routes:
1// Generate Postman collection 2appWrapper.generatePostmanCollection("./postman/collection.json"); 3 4// Generate Postman environment 5appWrapper.generatePostmanEnvironment("./postman/environment.json");
The generated files include:
1class AppWrapper { 2 constructor(config: AppWrapperConfig); 3 getExpressApp(): Express; 4 generatePostmanCollection(filePath: string): void; 5 generatePostmanEnvironment(filePath: string): void; 6} 7 8interface AppWrapperConfig { 9 app: Express; 10 routes: Routes; 11 postman?: PostmanConfig; 12 roles?: string[]; 13} 14 15interface PostmanConfig { 16 name: string; 17 description?: string; 18 version?: string; 19 baseUrl?: string; 20}
1class Routes { 2 constructor(r: IRoutes); 3 buildRouter(p_router?: Router, p_prefix?: { path: string }): Router; 4 generateFolder(pathPrefix?: string): PostmanRouteItem[] | PostmanRouteItem; 5} 6 7interface IRoutes { 8 prefix?: string; 9 routes: Array<Route | Routes>; 10 error?: ErrorRoute; 11 params?: Param[]; 12 middlewares?: Handler[]; 13 postman?: { 14 folderName: string; 15 }; 16 module?: string; 17} 18 19interface Param { 20 path: string; 21 method: RequestParamHandler; 22}
1class Route { 2 constructor(r: IRoute); 3 buildRoute(router: Router, route: Routes, prefix: { path: string }): void; 4 generateRoute(pathPrefix?: string): PostmanRouteItem; 5} 6 7interface IRoute { 8 method: "GET" | "POST" | "PUT" | "DELETE"; 9 path: string; 10 middlewares: Handler[]; 11 name?: string; 12 description?: string; 13 rai: string; 14 roles: string[]; 15 postman?: { 16 body?: Record<string, unknown>; 17 params?: Array<{ key: string; value: string; description: string }>; 18 }; 19}
1class ErrorRoute { 2 constructor(r: IErrorRoute); 3} 4 5interface IErrorRoute { 6 middleware: PathParams; 7}
1function InitializeCreatingRAIs(RoutesInstance: Routes): { 2 rais: IRAI[]; 3 roles: string[]; 4}; 5 6interface IRAI { 7 method: string; 8 path: string; 9 _id: string; 10 name: string; 11 description: string; 12 rai: string; 13 children: string[]; 14 roles: string[]; 15 isStopped: boolean; 16} 17 18interface IRole { 19 _id: string; 20 name: string; 21}
1class PostmanGenerator { 2 constructor( 3 name: string, 4 description?: string, 5 options?: { 6 baseUrl?: string; 7 version?: string; 8 } 9 ); 10 11 addEnvironmentVariable(key: string, value: string, type?: string): void; 12 addEnvironmentVariables( 13 variables: Array<{ 14 key: string; 15 value: string; 16 type?: string; 17 }> 18 ): void; 19 20 generateCollection(items: PostmanRouteItem[]): PostmanCollection; 21 generateEnvironment(items: PostmanRouteItem[]): PostmanEnvironment; 22 23 saveCollectionToFile(filePath: string, options?: SaveOptions): void; 24 saveEnvironmentToFile(filePath: string, options?: SaveOptions): void; 25 saveToFiles( 26 collectionPath: string, 27 environmentPath: string, 28 options?: SaveOptions 29 ): void; 30}
This example shows how to set up a basic API with JWT authentication:
1import express from "express"; 2import jwt from "jsonwebtoken"; 3import { AppWrapper, Routes, Route } from "@vortex-js/core"; 4 5const app = express(); 6app.use(express.json()); 7 8// Authentication middleware 9const authenticate = (req, res, next) => { 10 const token = req.headers.authorization?.split(" ")[1]; 11 12 if (!token) { 13 return res.status(401).json({ error: "Authentication required" }); 14 } 15 16 try { 17 const decoded = jwt.verify(token, "your-secret-key"); 18 req.user = { 19 id: decoded.id, 20 roles: decoded.roles, 21 }; 22 next(); 23 } catch (err) { 24 return res.status(401).json({ error: "Invalid token" }); 25 } 26}; 27 28// Login route (outside RBAC system) 29app.post("/login", (req, res) => { 30 const { username, password } = req.body; 31 32 // Example authentication (replace with your own) 33 if (username === "admin" && password === "password") { 34 const token = jwt.sign({ id: 1, roles: ["admin"] }, "your-secret-key", { 35 expiresIn: "1h", 36 }); 37 38 return res.json({ token }); 39 } 40 41 if (username === "user" && password === "password") { 42 const token = jwt.sign({ id: 2, roles: ["user"] }, "your-secret-key", { 43 expiresIn: "1h", 44 }); 45 46 return res.json({ token }); 47 } 48 49 return res.status(401).json({ error: "Invalid credentials" }); 50}); 51 52// RBAC routes 53const apiRoutes = new Routes({ 54 prefix: "/api", 55 routes: [ 56 // Public route 57 new Route({ 58 method: "GET", 59 path: "/public", 60 middlewares: [ 61 (req, res) => { 62 res.json({ message: "This is public" }); 63 }, 64 ], 65 name: "Public Endpoint", 66 description: "Accessible to everyone", 67 rai: "public:read", 68 roles: ["admin", "user", "guest"], 69 }), 70 71 // Protected routes 72 new Routes({ 73 prefix: "/users", 74 middlewares: [authenticate], // Apply authentication to all routes in this group 75 routes: [ 76 new Route({ 77 method: "GET", 78 path: "", 79 middlewares: [ 80 (req, res) => { 81 res.json({ 82 users: [ 83 { id: 1, name: "Admin" }, 84 { id: 2, name: "User" }, 85 ], 86 }); 87 }, 88 ], 89 name: "Get All Users", 90 description: "List all users", 91 rai: "users:list", 92 roles: ["admin"], // Only admin can list users 93 }), 94 95 new Route({ 96 method: "GET", 97 path: "/profile", 98 middlewares: [ 99 (req, res) => { 100 res.json({ profile: { id: req.user.id, roles: req.user.roles } }); 101 }, 102 ], 103 name: "Get Profile", 104 description: "Get current user profile", 105 rai: "users:profile", 106 roles: ["admin", "user"], // Both admin and user can access their profile 107 }), 108 ], 109 }), 110 ], 111}); 112 113// Create app wrapper 114const appWrapper = new AppWrapper({ 115 app, 116 routes: apiRoutes, 117 roles: ["admin", "user", "guest"], 118 postman: { 119 name: "Authentication Example API", 120 description: "API with authentication and RBAC", 121 baseUrl: "http://localhost:3000", 122 }, 123}); 124 125// Get configured Express app 126const configuredApp = appWrapper.getExpressApp(); 127 128// Generate Postman documentation 129appWrapper.generatePostmanCollection("./collection.json"); 130appWrapper.generatePostmanEnvironment("./environment.json"); 131 132// Start the server 133configuredApp.listen(3000, () => { 134 console.log("Server running on port 3000"); 135});
This example demonstrates how to organize routes in a hierarchical structure:
1import express from "express"; 2import { AppWrapper, Routes, Route } from "express-rbac-framework"; 3 4const app = express(); 5app.use(express.json()); 6 7// Define some middleware 8const logRequest = (req, res, next) => { 9 console.log(`${req.method} ${req.originalUrl}`); 10 next(); 11}; 12 13const checkApiKey = (req, res, next) => { 14 const apiKey = req.headers["x-api-key"]; 15 if (!apiKey || apiKey !== "valid-key") { 16 return res.status(401).json({ error: "Invalid API key" }); 17 } 18 next(); 19}; 20 21// Define routes with nested structure 22const apiRoutes = new Routes({ 23 prefix: "/api", 24 middlewares: [logRequest, checkApiKey], 25 postman: { folderName: "API" }, 26 routes: [ 27 // Users routes 28 new Routes({ 29 prefix: "/users", 30 postman: { folderName: "User Management" }, 31 routes: [ 32 new Route({ 33 method: "GET", 34 path: "", 35 middlewares: [(req, res) => res.json({ users: [] })], 36 name: "List Users", 37 description: "Get all users", 38 rai: "users:list", 39 roles: ["admin"], 40 }), 41 42 new Route({ 43 method: "POST", 44 path: "", 45 middlewares: [(req, res) => res.status(201).json({ id: 1 })], 46 name: "Create User", 47 description: "Create a new user", 48 rai: "users:create", 49 roles: ["admin"], 50 postman: { 51 body: { 52 name: "John Doe", 53 email: "john@example.com", 54 }, 55 }, 56 }), 57 58 // User details routes 59 new Routes({ 60 prefix: "/:userId", 61 params: [ 62 { 63 path: "userId", 64 method: (req, res, next, id) => { 65 if (isNaN(parseInt(id))) { 66 return res.status(400).json({ error: "Invalid user ID" }); 67 } 68 next(); 69 }, 70 }, 71 ], 72 routes: [ 73 new Route({ 74 method: "GET", 75 path: "", 76 middlewares: [ 77 (req, res) => res.json({ id: req.params.userId, name: "John" }), 78 ], 79 name: "Get User", 80 description: "Get user by ID", 81 rai: "users:read", 82 roles: ["admin", "user"], 83 }), 84 85 new Route({ 86 method: "PUT", 87 path: "", 88 middlewares: [(req, res) => res.json({ updated: true })], 89 name: "Update User", 90 description: "Update a user", 91 rai: "users:update", 92 roles: ["admin"], 93 postman: { 94 body: { 95 name: "Updated Name", 96 email: "updated@example.com", 97 }, 98 }, 99 }), 100 101 new Route({ 102 method: "DELETE", 103 path: "", 104 middlewares: [(req, res) => res.json({ deleted: true })], 105 name: "Delete User", 106 description: "Delete a user", 107 rai: "users:delete", 108 roles: ["admin"], 109 }), 110 ], 111 }), 112 ], 113 }), 114 115 // Products routes 116 new Routes({ 117 prefix: "/products", 118 postman: { folderName: "Product Management" }, 119 routes: [ 120 // Product routes here 121 ], 122 }), 123 ], 124}); 125 126const appWrapper = new AppWrapper({ 127 app, 128 routes: apiRoutes, 129 roles: ["admin", "user", "guest"], 130 postman: { 131 name: "Nested Routes Example", 132 description: "API with hierarchical route structure", 133 baseUrl: "http://localhost:3000", 134 }, 135}); 136 137const configuredApp = appWrapper.getExpressApp(); 138appWrapper.generatePostmanCollection("./collection.json"); 139 140configuredApp.listen(3000, () => { 141 console.log("Server running on port 3000"); 142});
This example shows how to implement custom error handling:
1import express from "express"; 2import { AppWrapper, Routes, Route, ErrorRoute } from "@vortex-js/core"; 3 4const app = express(); 5app.use(express.json()); 6 7// Custom validation middleware 8const validateUser = (req, res, next) => { 9 const { name, email } = req.body; 10 11 if (!name || !email) { 12 const error = new Error("Name and email are required"); 13 error.name = "ValidationError"; 14 return next(error); 15 } 16 17 if (typeof name !== "string" || name.length < 3) { 18 const error = new Error("Name must be at least 3 characters long"); 19 error.name = "ValidationError"; 20 return next(error); 21 } 22 23 if (!email.includes("@")) { 24 const error = new Error("Invalid email format"); 25 error.name = "ValidationError"; 26 return next(error); 27 } 28 29 next(); 30}; 31 32// Custom error handler 33const apiErrorHandler = new ErrorRoute({ 34 middleware: (err, req, res, next) => { 35 console.error("API Error:", err); 36 37 if (err.name === "ValidationError") { 38 return res.status(400).json({ 39 error: "Validation Error", 40 message: err.message, 41 }); 42 } 43 44 if (err.name === "NotFoundRouteError") { 45 return res.status(404).json({ 46 error: "Not Found", 47 message: "The requested resource does not exist", 48 }); 49 } 50 51 if (err.name === "ApiRouteNotFoundError") { 52 return res.status(403).json({ 53 error: "Access Denied", 54 message: "You do not have permission to access this resource", 55 }); 56 } 57 58 // Default error handler 59 res.status(500).json({ 60 error: "Server Error", 61 message: "An unexpected error occurred", 62 }); 63 }, 64}); 65 66// Define routes 67const apiRoutes = new Routes({ 68 prefix: "/api", 69 error: apiErrorHandler, // Apply custom error handling 70 routes: [ 71 new Routes({ 72 prefix: "/users", 73 routes: [ 74 new Route({ 75 method: "POST", 76 path: "", 77 middlewares: [ 78 validateUser, // Apply validation 79 (req, res) => { 80 res.status(201).json({ 81 id: 1, 82 name: req.body.name, 83 email: req.body.email, 84 }); 85 }, 86 ], 87 name: "Create User", 88 description: "Create a new user with validation", 89 rai: "users:create", 90 roles: ["admin"], 91 postman: { 92 body: { 93 name: "John Doe", 94 email: "john@example.com", 95 }, 96 }, 97 }), 98 ], 99 }), 100 ], 101}); 102 103const appWrapper = new AppWrapper({ 104 app, 105 routes: apiRoutes, 106 roles: ["admin", "user", "guest"], 107}); 108 109const configuredApp = appWrapper.getExpressApp(); 110 111// Global fallback error handler 112configuredApp.use((err, req, res, next) => { 113 console.error("Unhandled Error:", err); 114 res.status(500).send("Something went wrong!"); 115}); 116 117configuredApp.listen(3000, () => { 118 console.log("Server running on port 3000"); 119});
This example demonstrates a more complex role-based access control scenario:
1import express from "express"; 2import jwt from "jsonwebtoken"; 3import { AppWrapper, Routes, Route } from "express-rbac-framework"; 4 5const app = express(); 6app.use(express.json()); 7 8// Authentication middleware 9const authenticate = (req, res, next) => { 10 const token = req.headers.authorization?.split(" ")[1]; 11 12 if (!token) { 13 req.user = { roles: ["guest"] }; // Default guest role 14 return next(); 15 } 16 17 try { 18 const decoded = jwt.verify(token, "your-secret-key"); 19 req.user = { 20 id: decoded.id, 21 roles: decoded.roles, 22 organization: decoded.organization, 23 }; 24 next(); 25 } catch (err) { 26 req.user = { roles: ["guest"] }; // Default to guest on error 27 next(); 28 } 29}; 30 31// Mock database 32const users = [ 33 { id: 1, name: "Admin", organization: "org1", isAdmin: true }, 34 { id: 2, name: "Manager", organization: "org1", isManager: true }, 35 { id: 3, name: "User 1", organization: "org1" }, 36 { id: 4, name: "User 2", organization: "org2" }, 37]; 38 39// Example of owner check middleware 40const checkOwnership = (req, res, next) => { 41 const userId = parseInt(req.params.userId); 42 const user = users.find((u) => u.id === userId); 43 44 if (!user) { 45 return res.status(404).json({ error: "User not found" }); 46 } 47 48 // User is either admin, from same organization, or the user themselves 49 const isAdmin = req.user.roles.includes("admin"); 50 const isSameOrg = user.organization === req.user.organization; 51 const isSelf = req.user.id === userId; 52 53 if (isAdmin || isSameOrg || isSelf) { 54 req.targetUser = user; 55 return next(); 56 } 57 58 return res.status(403).json({ error: "Access denied" }); 59}; 60 61// Define routes 62const apiRoutes = new Routes({ 63 prefix: "/api", 64 middlewares: [authenticate], 65 routes: [ 66 new Routes({ 67 prefix: "/users", 68 routes: [ 69 // List all users - admin only 70 new Route({ 71 method: "GET", 72 path: "", 73 middlewares: [ 74 (req, res) => { 75 // Admins see all users 76 if (req.user.roles.includes("admin")) { 77 return res.json({ users }); 78 } 79 80 // Managers see users in their organization 81 if (req.user.roles.includes("manager")) { 82 const orgUsers = users.filter( 83 (u) => u.organization === req.user.organization 84 ); 85 return res.json({ users: orgUsers }); 86 } 87 88 // Regular users see limited info 89 const basicUsers = users.map((u) => ({ 90 id: u.id, 91 name: u.name, 92 })); 93 return res.json({ users: basicUsers }); 94 }, 95 ], 96 name: "List Users", 97 description: "Get all users with role-based filtering", 98 rai: "users:list", 99 roles: ["admin", "manager", "user"], 100 }), 101 102 // Get specific user details - with ownership check 103 new Route({ 104 method: "GET", 105 path: "/:userId", 106 middlewares: [ 107 checkOwnership, 108 (req, res) => { 109 // Admins see everything 110 if (req.user.roles.includes("admin")) { 111 return res.json({ user: req.targetUser }); 112 } 113 114 // Others see limited info 115 const { id, name, organization } = req.targetUser; 116 return res.json({ user: { id, name, organization } }); 117 }, 118 ], 119 name: "Get User", 120 description: "Get user by ID with role-based data filtering", 121 rai: "users:read", 122 roles: ["admin", "manager", "user"], 123 }), 124 125 // Update user - admin or same organization manager 126 new Route({ 127 method: "PUT", 128 path: "/:userId", 129 middlewares: [ 130 checkOwnership, 131 (req, res) => { 132 // Only admins can change organization 133 if (req.body.organization && !req.user.roles.includes("admin")) { 134 return res.status(403).json({ 135 error: "Only admins can change organization", 136 }); 137 } 138 139 // Update user 140 const userIndex = users.findIndex( 141 (u) => u.id === parseInt(req.params.userId) 142 ); 143 users[userIndex] = { ...users[userIndex], ...req.body }; 144 145 return res.json({ 146 message: "User updated", 147 user: users[userIndex], 148 }); 149 }, 150 ], 151 name: "Update User", 152 description: "Update user with role-based permissions", 153 rai: "users:update", 154 roles: ["admin", "manager"], 155 postman: { 156 body: { 157 name: "Updated Name", 158 email: "updated@example.com", 159 }, 160 }, 161 }), 162 163 // Delete user - admin only 164 new Route({ 165 method: "DELETE", 166 path: "/:userId", 167 middlewares: [ 168 (req, res) => { 169 const userId = parseInt(req.params.userId); 170 const userIndex = users.findIndex((u) => u.id === userId); 171 172 if (userIndex === -1) { 173 return res.status(404).json({ error: "User not found" }); 174 } 175 176 users.splice(userIndex, 1); 177 return res.json({ message: "User deleted" }); 178 }, 179 ], 180 name: "Delete User", 181 description: "Delete a user (admin only)", 182 rai: "users:delete", 183 roles: ["admin"], 184 }), 185 ], 186 }), 187 ], 188}); 189 190// Create app wrapper with detailed roles 191const appWrapper = new AppWrapper({ 192 app, 193 routes: apiRoutes, 194 roles: ["admin", "manager", "user", "guest"], 195 postman: { 196 name: "Advanced RBAC Example", 197 description: "API with complex role-based access control", 198 baseUrl: "http://localhost:3000", 199 }, 200}); 201 202const configuredApp = appWrapper.getExpressApp(); 203appWrapper.generatePostmanCollection("./collection.json"); 204 205configuredApp.listen(3000, () => { 206 console.log("Server running on port 3000"); 207});
This project is licensed under the MIT License - see the LICENSE file for details.
No vulnerabilities found.
No security vulnerabilities found.