A fast, convention-based MVC framework for Node.js — built on top of Express.
Vulkano is a lightweight MVC framework for building web applications and APIs with Node.js. It wires together Express, MongoDB (Mongoose), Socket.io, i18n, JWT, file uploads, cron jobs, and more — so you spend time writing features, not boilerplate.
Inspired by KumbiaPHP.
- Node.js
^22 - MongoDB (optional — only needed if you use models)
- Redis (optional — only needed for Socket.io Redis adapter or sessions)
npm install @vulkano/coreconst vulkano = require('@vulkano/core');
vulkano();your-app/
├── app.js # Entry point
├── public/ # Static files served over HTTP
│ ├── css/
│ ├── js/
│ ├── img/
│ └── files/ # Uploaded files
└── vulkano/ # Your application
├── config/
│ ├── settings.js # App-wide settings (port, database, JWT…)
│ ├── routes.js # Explicit route overrides (optional)
│ ├── env/ # Per-environment config overrides
│ ├── express/ # Express middleware customization
│ ├── settings.js # App-wide settings (port, database, JWT…)
│ ├── routes.js # Explicit route overrides (optional)
│ └── locales/ # i18n translation files (en.js, es.js, etc.)
├── controllers/ # Request handlers
├── models/ # Mongoose model definitions
└── services/ # Shared services & libs (auto-loaded as globals)
Vulkano resolves routes by convention — no route file required for standard CRUD.
| HTTP method | URL | Resolves to |
|---|---|---|
GET |
/user |
UserController.get |
POST |
/user |
UserController.post |
PUT |
/user/42 |
UserController['put :id'] |
PATCH |
/user/42 |
UserController['patch :id'] |
DELETE |
/user/42 |
UserController['delete :id'] |
POST |
/user/save |
UserController['post save'] |
GET |
/user/42/info |
UserController['get :id/info'] |
// vulkano/controllers/UserController.js
module.exports = {
get(req, res) {
res.vsr(Promise.resolve({ users: [] }));
},
'get :id': function (req, res) {
res.vsr(Promise.resolve({ id: req.params.id }));
},
'post save': function (req, res) {
res.vsr(Promise.resolve({ saved: true }));
}
};module.exports = [
{ method: 'GET', path: '/health', controller: 'StatusController', action: 'ping' },
{ method: 'POST', path: '/auth/login', controller: 'AuthController', action: 'login' },
];You can also register routes with inline handlers or using app.vulkano.get/post/….
Every controller action uses res.vsr(promise). It wraps the resolved value in a standard envelope:
{ "success": true, "statusCode": 200, "data": { … } }Errors are handled automatically:
// Custom error with status code
res.vsr(VSError.reject('Not allowed', 403));
// 404
res.vsr(VSError.notFound('User'));
// Plain rejection → 500
res.vsr(Promise.reject(new Error('Something went wrong')));Point a controller at a model and get a full REST API for free:
// vulkano/controllers/api/ProductController.js
module.exports = {
scaffold: 'Product', // Mongoose model name
allowedMethods: ['get', 'post', 'put', 'patch', 'delete']
};This automatically exposes:
| Method | Path | Action |
|---|---|---|
GET |
/api/product |
List (paginated) |
GET |
/api/product/:id |
Get by ID |
POST |
/api/product |
Create |
PUT |
/api/product/:id |
Replace |
PATCH |
/api/product/:id |
Partial update |
DELETE |
/api/product/:id |
Soft-delete |
Query string params supported on list: page, per_page, sort, search, fields.
Models live in vulkano/models/ and are auto-loaded as globals (e.g., Product).
// vulkano/models/Product.js
module.exports = {
attributes: {
name: { type: String, required: true },
price: { type: Number, default: 0 },
tags: { type: [String] }
},
// Lifecycle hooks
beforeSave(next) {
this.updatedAt = new Date();
next();
}
};Every model automatically gets active, createdAt, and updatedAt fields.
Models use mongoose-paginate-v2 for pagination.
All files in vulkano/services/ are auto-loaded as globals. The framework also exposes:
| Global | Description |
|---|---|
VSError |
Structured error factory (reject, notFound, badRequest) |
Jwt |
Sign / verify JWT tokens |
Paginate |
Query serialization and pagination helpers |
Merge |
Deep-merge utility (deepmerge-compatible, full option support) |
Encrypter |
Hash and compare passwords (bcrypt-based) |
Filter |
Input sanitization helpers |
Crontab |
Schedule recurring jobs with cron expressions |
ApiClient |
HTTP client for calling external APIs (native fetch + undici) |
Download |
File download helper |
i18n |
Internationalization via i18next |
mongoose |
Mongoose instance |
Vulkano uses Multer v2. Files are available on req.files after a multipart/form-data POST:
'post upload': function (req, res) {
const files = (req.files || []).map((f) => ({
fieldname: f.fieldname,
originalname: f.originalname,
mimetype: f.mimetype,
size: f.size
}));
res.vsr(Promise.resolve({ uploaded: files.length, files }));
}Configure in vulkano/config/express/jwt.js:
module.exports = {
secret: process.env.JWT_SECRET,
unless: ['/auth/login', '/health'] // Public paths (no token required)
};Sign a token anywhere in your app:
const token = Jwt.encode({ userId: user._id });// vulkano/services/Jobs.js
module.exports = {
init() {
Crontab.add('cleanup', '0 3 * * *', async () => {
await Report.deleteMany({ active: false });
});
}
};Translation files go in vulkano/config/locales/. Use i18n.t('key') anywhere in your app.
Enabled via vulkano/config/settings.js. Adapters for MongoDB and Redis are included out of the box.
module.exports = {
// PORT to listen on
port: process.env.PORT || 3000,
// Salt for password
salt: process.env.SALT_KEY || '',
// Database configuration
database: {
// MONGO_URI connection
connection: process.env.MONGO_URI,
// Settings before to connect
settings: {
strictQuery: false,
debug: false
},
// Additional config to mongoose
config: {
useNewUrlParser: true,
useUnifiedTopology: true,
// family: 4 // 4 (IPv4), 6 (IPv6), or null (default: OS family)
// useFindAndModify: false,
// useCreateIndex: true
}
}
};npm test # all suites
npm run test:watch # watch mode
npm run test:coverage # with coverage reportTests require Node.js 22. Use
nvm use 22if needed.
MIT © Vulkano Team