Complete API reference for the Node.js MVC Framework.
The Boot module is responsible for starting the application and managing its lifecycle.
Starts the application by initializing all core modules in sequence.
const Boot = require('@core/boot.core')
await Boot.start()Returns: Promise<void>
Throws: Error if application fails to start
Manages database connections and Sequelize models.
Initializes database connection and loads models.
const Database = require('@core/database.core')
await Database.init()Returns: Promise<void>
Retrieves a loaded Sequelize model by name.
const User = Database.getModel('User')
const users = await User.findAll()Parameters:
name(string): Model name
Returns: Sequelize Model instance
Gets the Sequelize instance.
const sequelize = Database.getInstance()
await sequelize.query('SELECT * FROM users')Returns: Sequelize instance
Closes the database connection.
await Database.close()Returns: Promise<void>
Note: Since v2.0.0,
Database.close()is only called during shutdown ifconfig.database.statusistrue.
Executes a function inside a managed Sequelize transaction. Auto-commits on success, auto-rollbacks on error.
await Database.transaction(async (t) => {
const user = await User.create({ name: 'Alice' }, { transaction: t })
await Profile.create({ userId: user.id }, { transaction: t })
})Parameters:
callback(Function): Async function that receives the transaction objectt
Returns: Promise<*> — result of the callback
Throws: Error if database not initialized or transaction fails
Object containing all loaded models.
const models = Database.models
console.log(Object.keys(models)) // ['User', 'Post', ...]In-memory key-value store with optional TTL. Ideal for caching expensive queries, session data, or rate-limit counters.
const Cache = require('@core/cache.core')| Method | Description |
|---|---|
set(key, value, ttl = 0) |
Store a value. ttl in seconds, 0 = no expiry |
get(key, default = null) |
Retrieve a value or default |
has(key) |
Check if key exists |
delete(key) |
Remove a key |
flush() |
Clear all entries |
size() |
Number of cached entries |
keys() |
Array of all keys |
remember(key, ttl, fn) |
Return cached value or call fn(), cache and return result |
rememberAsync(key, ttl, fn) |
Async version of remember |
add(key, value, ttl) |
Store only if key does not exist; returns boolean |
pull(key, default) |
Get and immediately delete |
increment(key, amount = 1) |
Increment numeric value |
decrement(key, amount = 1) |
Decrement numeric value |
Examples:
// Basic store and retrieve
Cache.set('config', { theme: 'dark' }, 3600) // 1-hour TTL
const config = Cache.get('config', {})
// Get-or-set (async)
const users = await Cache.rememberAsync('users:all', 300, async () => {
return await User.findAll()
})
// One-time use token
Cache.set('reset:abc', userId, 900) // 15-min TTL
const id = Cache.pull('reset:abc') // Get and remove
// Counter
Cache.increment('login:fails:user1')
if (Cache.get('login:fails:user1') >= 5) blockUser()Manages Express application and middleware configuration.
Initializes Express app with middlewares and routes.
const Express = require('@core/express.core')
Express.init()Returns: void
Gets the Express application instance.
const app = Express.instance()
app.get('/custom', (req, res) => {
res.send('Custom route')
})Returns: Express application
The Express application instance.
const app = Express.appThe Express Router instance.
const router = Express.routerWinston-based logging system with file rotation.
Logs informational messages.
const Logger = require('@core/logger.core')
Logger.info('user', 'User logged in successfully')Parameters:
context(string): Log context/layermessage(string): Log message
Logs error messages.
Logger.error('database', 'Connection failed')Parameters:
context(string): Error contextmessage(string): Error message
Logs warning messages.
Logger.warn('auth', 'Invalid token attempt')Parameters:
context(string): Warning contextmessage(string): Warning message
Logs debug messages (only in development).
Logger.debug('query', 'SELECT * FROM users')Parameters:
context(string): Debug contextmessage(string): Debug message
Logs error objects with stack traces.
try {
// some code
} catch (err) {
Logger.set(err, 'controller')
}Parameters:
error(Error): Error objectcontext(string): Error context
JSON Web Token utilities for authentication.
Creates a JWT token.
const JWT = require('@core/jwt.core')
const token = JWT.sign({ userId: 123, role: 'admin' }, '7d')Parameters:
payload(Object): Token payload dataexpiresIn(string, optional): Expiration time (default from config)isRefresh(boolean, optional): Whether to use refresh token secret (default: false)
Returns: string - JWT token
Creates a JWT refresh token using JWT_REFRESH_SECRET.
const refreshToken = JWT.signRefresh({ userId: 123 }, '7d')Parameters:
payload(Object): Token payload dataexpiresIn(string, optional): Expiration time (default from config refreshExpiresIn)
Returns: string - JWT refresh token
Verifies and decodes a JWT token.
const decoded = JWT.verify(token)
if (decoded) {
console.log(decoded.userId) // 123
}Parameters:
token(string): JWT token to verifyisRefresh(boolean, optional): Whether to verify using refresh token secret (default: false)
Returns: Object|null - Decoded payload or null if invalid
Verifies and decodes a JWT refresh token.
const decoded = JWT.verifyRefresh(refreshToken)Parameters:
token(string): JWT refresh token to verify
Returns: Object|null - Decoded payload or null if invalid
Decodes a JWT token without verification.
const decoded = JWT.decode(token)
console.log(decoded)Parameters:
token(string): JWT token
Returns: Object|null - Decoded payload or null
Email sending functionality with template support.
Initializes the mailer transport.
const Mailer = require('@core/mailer.core')
Mailer.init()Returns: void
Sends an email using an EJS template.
v2.0.0: Validates
to,subject,templatebefore processing. Throws descriptive errors instead of propagating nodemailer errors.
await Mailer.send('user@example.com', 'Welcome!', 'welcome', { username: 'John' })Parameters:
to(string): Recipient email — required, throws if emptysubject(string): Email subject — required, throws if emptytemplate(string): Template name (without.email.ejs) — required, throws if file not founddata(Object): Data to pass to template
Returns: Promise<Object> - Email info
Template Location: public/views/templates/email/{template}.email.ejs
Sends raw HTML email without requiring a template file.
await Mailer.sendRaw('user@example.com', 'Hello!', '<h1>Hello World</h1>')Parameters:
to(string): Recipient emailsubject(string): Email subjecthtml(string): Raw HTML content
Returns: Promise<Object> - Email info
Verifies mailer connection.
const isValid = await Mailer.verify()
console.log(isValid) // true or falseReturns: Promise<boolean> - Connection status
Lifecycle hooks system for running code at specific points.
Registers a hook callback.
const Hooks = require('@core/hooks.core')
Hooks.register('before', async () => {
console.log('Before app starts')
})Parameters:
lifecycle(string): Hook lifecycle ('before', 'after', 'shutdown')callback(Function): Async function to execute
Lifecycles:
before: Before application initializationafter: After application startsshutdown: During graceful shutdown
Executes all hooks for a specific lifecycle.
await Hooks.run('after')Parameters:
lifecycle(string): Hook lifecycle to run
Returns: Promise<void>
Socket.IO configuration and management.
Initializes Socket.IO with the HTTP server.
const Socket = require('@core/socket.core')
const server = require('http').createServer(app)
Socket.init(server)Parameters:
server(Object): HTTP/HTTPS server instance
Returns: void
Gets the Socket.IO instance.
const io = Socket.getInstance()
io.emit('broadcast', { message: 'Hello everyone' })Returns: Socket.IO Server instance
Gracefully closes the Socket.IO server. Should be called during application shutdown.
await Socket.close()Returns: Promise<void>
The Socket.IO server instance.
const io = Socket.ioHTTP/HTTPS server creation and management.
Creates an HTTP or HTTPS server.
v2.0.0 Fix: No longer passes invalid options to
http.createServer(). Options likepoweredByare not valid Node.js HTTP server options.
const Server = require('@core/server.core')
const server = Server.create(expressApp)Parameters:
app(Object): Express application instance
Returns: HTTP/HTTPS Server instance
Starts the server listening on specified port.
await Server.listen(server, 3025)
await Server.listen(server, 3025, '127.0.0.1') // Bind to specific hostParameters:
server(Object): Server instanceport(number): Port numberhost(string, optional): Host to bind (default:0.0.0.0)
Returns: Promise<void>
Applies server-level timeout settings from config.server.options.
const server = Server.create(app)
Server.applyOptions(server) // Sets keepAliveTimeout, requestTimeout, headersTimeoutReads from config:
config.server.options.keepAliveTimeoutconfig.server.options.requestTimeoutconfig.server.options.headersTimeout
Runtime environment configuration.
Configures runtime environment (timezone, NODE_ENV).
const Runtime = require('@core/runtime.core')
Runtime.init()Returns: void
Global error handling for Express.
Initializes global error handler middleware.
const ErrorHandler = require('@core/errorHandler.core')
ErrorHandler.init(app)Parameters:
app(Object): Express application instance
Returns: void
Base class for all controllers. Provides standardized JSON response methods.
Async error wrapper. Eliminates repetitive try/catch in controller methods.
const BaseController = require('@app/http/controllers/base.controller')
// Before: manual try/catch
static async getAll({ req, res }) {
try {
const result = await UserService.getAll()
return BaseController.json(res, result)
} catch (err) {
return BaseController.serverError(res, err.message)
}
}
// After: use handle()
static getAll = BaseController.handle(async ({ req, res }) => {
const result = await UserService.getAll()
return BaseController.json(res, result)
})Errors are automatically forwarded to Express error handler via next(err).
Universal JSON response builder. Can accept a BaseService.json() output object directly.
const result = await UserService.getAll()
return BaseController.json(res, result) // Reads status/code/message/data from result200 success response.
201 created response.
Error response.
422 validation error response.
404 response.
401 response.
403 response.
500 response.
Paginated response with meta information.
return BaseController.paginated(res, users, {
total: 100,
page: 1,
limit: 10,
totalPages: 10
})204 no content response.
Base class for all services. Provides standardized response object builders.
Universal response builder. Returns an object compatible with BaseController.json().
return this.json(true, 200, 'Success', data)Shortcut for 200 success.
return this.success('Users retrieved', users)Shortcut for 201 created.
return this.created('User created', newUser)Generic error shortcut (default 400).
return this.fail('Bad request')
return this.fail('Payment required', 402)Shortcut for 404.
return this.notFound('User not found')Shortcut for 401.
return this.unauthorized('Invalid token')Shortcut for 403.
return this.forbidden('Access denied')Shortcut for 409 (duplicate data).
return this.conflict('Email already registered')Shortcut for 422 with errors array.
return this.validationFail('Validation error', [{ field: 'email', message: 'Invalid' }])Shortcut for 500.
return this.serverError('Database error')JWT authentication and RBAC middleware. Import from @app/http/middlewares/auth.middleware.
const AuthMiddleware = require('@app/http/middlewares/auth.middleware')Express middleware. Reads the Authorization: Bearer <token> header, verifies the JWT, and attaches the decoded payload to req.user. Returns 401 if the token is absent or invalid.
Routes.get('profile', [AuthMiddleware.authenticate, UserController, 'profile'])Same as authenticate but never blocks. Sets req.user = null if token is absent or invalid, then calls next().
Routes.get('feed', [AuthMiddleware.optional, FeedController, 'index'])Returns an Express middleware that checks req.user.role against the allowed roles list. Must be used after authenticate. Returns 401 if req.user is not set, 403 if role doesn't match.
Routes.get('admin', [AuthMiddleware.authenticate, AuthMiddleware.role('admin'), AdminController, 'index'])
Routes.get('manage', [AuthMiddleware.authenticate, AuthMiddleware.role('admin', 'editor'), ManageController, 'index'])Parameters:
...roles(string): One or more allowed role strings
Returns an Express middleware that checks all listed permissions against req.user.permissions[]. Returns 401 if not authenticated, 403 if any permission is missing.
Routes.delete('posts/:id', [AuthMiddleware.authenticate, AuthMiddleware.can('post:delete'), PostController, 'destroy'])Parameters:
...permissions(string): One or more required permission strings
Validates required environment variables at application startup. Called automatically by Boot.start() via validateFromConfig().
const EnvValidator = require('@core/env.validator')Validates that all keys in the array are set and non-empty. Throws with a descriptive error listing all missing keys.
EnvValidator.validate(['JWT_SECRET', 'DB_HOST', 'APP_URL'])Parameters:
requiredKeys(string[]): Array of required env var names
Throws: Error listing all missing variable names
Reads config.app.requiredEnv and validates those keys. Called automatically during boot.
To use, add the REQUIRED_ENV var to .env (comma-separated) or set app.requiredEnv directly in app/config.js:
REQUIRED_ENV=JWT_SECRET,DB_HOST,MAIL_USERGET /health is automatically registered by express.core.js. No configuration required.
Response:
{
"status": true,
"code": 200,
"message": "OK",
"data": {
"app": "Node Framework",
"env": "production",
"uptime": 3600,
"timestamp": "2026-05-25T10:00:00.000Z",
"memory": {
"rss": "85 MB",
"heapUsed": "42 MB",
"heapTotal": "56 MB"
}
}
}Factory for express-rate-limit middleware instances. A global rate limiter is auto-applied by express.core.js. Use RateLimit for per-route limits.
const RateLimit = require('@core/helpers/rateLimit.helper')Creates a custom rate limiter.
const limiter = RateLimit.create(60 * 1000, 30) // 30 req/min
Routes.get('search', [limiter, SearchController, 'index'])Parameters:
windowMs(number): Time window in ms (default: 15 min)max(number): Max requests per window (default: 100)message(string): Error message (default: generic 429 message)
Tight limits for sensitive endpoints (default: 10 req/15min).
Routes.post('login', [RateLimit.strict(), AuthController, 'login'])
Routes.post('otp/send', [RateLimit.strict(5), OtpController, 'send'])Loose limits for public endpoints (default: 300 req/15min).
Routes.get('posts', [RateLimit.generous(), PostController, 'index'])Standard global-level limit (200 req/15min). Used internally by express.core.js.
Global rate limit config via .env:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX=200
RATE_LIMIT_WINDOW_MS=900000Using express-fileupload, files are available in req.files.
Routes.post('/upload', ({ req, res }) => {
// Check if files exist
if (!req.files || !req.files.file) {
return res.status(400).json({
success: false,
message: 'No file uploaded',
})
}
const file = req.files.file
// File properties
console.log(file.name) // Original filename
console.log(file.mimetype) // MIME type
console.log(file.size) // File size in bytes
console.log(file.tempFilePath) // Temp file path
// Move file to destination
const uploadPath = __dirname + '/uploads/' + file.name
file.mv(uploadPath, (err) => {
if (err) {
return res.status(500).json({
success: false,
message: 'File upload failed',
error: err,
})
}
res.json({
success: true,
message: 'File uploaded successfully',
filename: file.name,
})
})
})Routes.post('/upload-multiple', ({ req, res }) => {
if (!req.files || !req.files.files) {
return res.status(400).json({ error: 'No files uploaded' })
}
const files = Array.isArray(req.files.files) ? req.files.files : [req.files.files]
files.forEach((file) => {
const uploadPath = __dirname + '/uploads/' + file.name
file.mv(uploadPath)
})
res.json({ message: `${files.length} files uploaded` })
})Routes.post('/upload', ({ req, res }) => {
if (!req.files || !req.files.file) {
return res.status(400).json({ error: 'No file' })
}
const file = req.files.file
// Validate file size (5MB max)
if (file.size > 5 * 1024 * 1024) {
return res.status(400).json({ error: 'File too large' })
}
// Validate MIME type
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.mimetype)) {
return res.status(400).json({ error: 'Invalid file type' })
}
// Process file...
})Routing is handled by the @refkinscallv/express-routing package. All route files live in app/routes/ and are loaded via app/routes/register.route.js.
const Routes = require('@refkinscallv/express-routing')Every route handler receives a single context object { req, res, next, error } — all standard Express objects. There are two ways to register a handler:
1. Inline destructured function (recommended for simple routes)
Routes.get('users', ({ req, res }) => {
res.json({ users: [] })
})
// Access all context properties as needed
Routes.get('users/:id', ({ req, res, next }) => {
const { id } = req.params
res.json({ id })
})Important: Handler functions always receive
{ req, res, next, error }as a single object — never as positional arguments. Always use destructuring({ req, res }), not(req, res).
2. Controller array [Class, 'method'] (recommended for controller-based routes)
const UserController = require('@app/http/controllers/user.controller')
Routes.get('users', [UserController, 'index'])
Routes.post('users', [UserController, 'store'])
Routes.put('users/:id', [UserController, 'update'])
Routes.delete('users/:id', [UserController, 'destroy'])Resolves both static and instance methods. Works with classes that extend BaseController.
Controller methods must also use the destructured signature:
module.exports = class UserController extends BaseController {
// With BaseController.handle() — recommended (auto error forwarding)
static index = BaseController.handle(async ({ req, res }) => {
return BaseController.json(res, await UserService.getAll())
})
// Without handle() — manual (must use { req, res })
static async show({ req, res }) {
const user = await UserService.findById(req.params.id)
return BaseController.json(res, user)
}
}Routes.get('path', handler)
Routes.post('path', handler)
Routes.put('path', handler)
Routes.delete('path', handler)
Routes.patch('path', handler)
Routes.options('path', handler)
Routes.head('path', handler)Multi-method route — Routes.add(methods, path, handler, middlewares):
// Accept both GET and POST on the same path
Routes.add(['get', 'post'], 'search', [SearchController, 'handle'])// URL parameter — req.params.id
Routes.get('users/:id', ({ req, res }) => {
const { id } = req.params
res.json({ id })
})
// Multiple params
Routes.get('posts/:postId/comments/:commentId', ({ req, res }) => {
const { postId, commentId } = req.params
res.json({ postId, commentId })
})
// Query string — req.query
// GET /search?q=node&page=2&limit=10
Routes.get('search', ({ req, res }) => {
const { q, page = 1, limit = 10 } = req.query
res.json({ q, page, limit })
})Pass middlewares as the third argument (array of plain Express functions):
const AuthMiddleware = require('@app/http/middlewares/auth.middleware')
const RateLimit = require('@core/helpers/rateLimit.helper')
// Single middleware
Routes.get('profile', [UserController, 'profile'], [AuthMiddleware.authenticate])
// Multiple middlewares — executed left to right
Routes.post('posts', [PostController, 'store'], [
RateLimit.strict(),
AuthMiddleware.authenticate,
AuthMiddleware.role('editor', 'admin'),
])
// Role gate chained after authenticate
Routes.delete('users/:id', [UserController, 'destroy'], [
AuthMiddleware.authenticate,
AuthMiddleware.role('admin'),
])
// Permission gate
Routes.patch('posts/:id', [PostController, 'update'], [
AuthMiddleware.authenticate,
AuthMiddleware.can('post:edit'),
])
// Optional auth — req.user set if token present, null otherwise
Routes.get('feed', [FeedController, 'index'], [AuthMiddleware.optional])Group routes under a shared path prefix:
// Simple prefix group
Routes.group('api', () => {
Routes.get('users', [UserController, 'index']) // → GET /api/users
Routes.post('users', [UserController, 'store']) // → POST /api/users
})
// Nested groups
Routes.group('api', () => {
Routes.group('v1', () => {
Routes.get('users', [UserController, 'index']) // → GET /api/v1/users
})
Routes.group('v2', () => {
Routes.get('users', [UserController, 'indexV2']) // → GET /api/v2/users
})
})
// Group with shared middlewares (plain functions allowed here)
Routes.group('admin', () => {
Routes.get('users', [UserController, 'index'])
Routes.get('settings', [SettingsController, 'index'])
}, [AuthMiddleware.authenticate, AuthMiddleware.role('admin')])Apply middlewares to a block of routes without repeating them per-route.
Scoped mode (callback — accepts plain functions):
Routes.middleware([AuthMiddleware.authenticate], () => {
// All routes inside here require authentication
Routes.get('profile', [UserController, 'profile'])
Routes.put('profile', [UserController, 'updateProfile'])
Routes.middleware([AuthMiddleware.role('admin')], () => {
// Nested — requires auth + admin role
Routes.get('admin/users', [AdminController, 'users'])
Routes.delete('admin/users/:id', [AdminController, 'deleteUser'])
})
})Chaining mode (no callback — handler class must implement handle()):
// Note: chaining mode requires middlewares to have a handle() method
Routes.middleware([AuthMiddlewareClass])
.get('dashboard', [DashboardController, 'index'])For most cases, the scoped mode with callback is recommended as it works with all standard Express middleware functions.
Auto-registers all methods of a controller class based on naming conventions:
Routes.controller('users', UserController)Naming convention:
| Method Name | HTTP Method | Path |
|---|---|---|
index |
GET | /users |
store |
GET | /users/store |
post_store |
POST | /users (prefix stripped) |
put_update |
PUT | /users |
delete_destroy |
DELETE | /users |
getUserById |
GET | /users/get-user-by-id |
Methods prefixed with post_, put_, delete_, patch_, options_, head_ use that HTTP method. All others default to GET.
With per-method middlewares (3rd argument):
Routes.controller('users', UserController, {
post_store: [AuthMiddleware.authenticate, AuthMiddleware.role('admin')],
delete_destroy: [AuthMiddleware.authenticate, AuthMiddleware.role('admin')],
})Register a custom error handler applied after all routes:
Routes.errorHandler(({ req, res, next, error }) => {
res.status(error.status || 500).json({
status: false,
code: error.status || 500,
message: error.message || 'Internal Server Error',
})
})
// Or with [Controller, 'method'] format
Routes.errorHandler([ErrorController, 'handle'])Intercept all requests and return a 503 response:
// Enable maintenance — all routes return 503
Routes.maintenance(true)
// Enable with custom handler
Routes.maintenance(true, ({ res }) => {
res.status(503).json({ message: 'Down for maintenance. Back soon!' })
})
// Disable maintenance
Routes.maintenance(false)
// With environment flag
Routes.maintenance(process.env.MAINTENANCE_MODE === 'true')// app/routes/api.route.js
'use strict'
const Routes = require('@refkinscallv/express-routing')
const AuthMiddleware = require('@app/http/middlewares/auth.middleware')
const RateLimit = require('@core/helpers/rateLimit.helper')
const AuthController = require('@app/http/controllers/auth.controller')
const UserController = require('@app/http/controllers/user.controller')
const PostController = require('@app/http/controllers/post.controller')
Routes.group('api', () => {
// ── Auth (public, rate-limited) ─────────────────────────────
Routes.post('auth/login', [AuthController, 'login'], [RateLimit.strict()])
Routes.post('auth/register', [AuthController, 'register'], [RateLimit.strict()])
Routes.post('auth/refresh', [AuthController, 'refresh'], [RateLimit.strict(10)])
// ── Public routes ───────────────────────────────────────────
Routes.get('posts', [PostController, 'index'])
Routes.get('posts/:id', [PostController, 'show'])
// ── Authenticated routes (scoped) ───────────────────────────
Routes.middleware([AuthMiddleware.authenticate], () => {
Routes.get('profile', [UserController, 'profile'])
Routes.put('profile', [UserController, 'updateProfile'])
Routes.post('auth/logout', [AuthController, 'logout'])
Routes.post('posts', [PostController, 'store'], [AuthMiddleware.can('post:create')])
Routes.put('posts/:id', [PostController, 'update'], [AuthMiddleware.can('post:edit')])
Routes.delete('posts/:id', [PostController, 'destroy'], [AuthMiddleware.can('post:delete')])
// ── Admin only ──────────────────────────────────────────
Routes.group('admin', () => {
Routes.controller('users', UserController)
}, [AuthMiddleware.role('admin')])
})
})Routes are loaded by the framework in this order:
app/routes/register.route.js— imports all route files (you add new files here)Routes.apply(router)— called byexpress.core.js, registers all routes on the Express routerErrorHandler— registered after routes byerrorHandler.core.js
// app/routes/register.route.js
'use strict'
require('@app/routes/web.route')
require('@app/routes/api.route')
// require('@app/routes/admin.route') ← add new route files hereAll configuration in app/config.js.
module.exports = {
app: {
/* app settings */
},
server: {
/* server settings */
},
express: {
cors: {
/* CORS options */
},
static: {
/* static files */
},
view: {
/* view engine */
},
fileupload: {
/* file upload options */
},
},
socket: {
/* Socket.IO options */
},
database: {
/* database settings */
},
jwt: {
/* JWT settings */
},
mailer: {
/* email settings */
},
}const config = require('@app/config')
console.log(config.app.port) // 3025
console.log(config.database.host) // localhost
console.log(config.jwt.expiresIn) // 7d- Error Handling: Always use try-catch blocks and log errors
- Validation: Validate user inputs before processing
- Security: Use JWT for authentication, validate file uploads
- Logging: Use appropriate log levels (info, warn, error, debug)
- Database: Use transactions for complex operations
- File Uploads: Validate file types and sizes
- Environment: Use different configs for development/production
// app/routes/auth.route.js
const Routes = require('@refkinscallv/express-routing')
const Database = require('@core/database.core')
const JWT = require('@core/jwt.core')
const Mailer = require('@core/mailer.core')
const bcrypt = require('bcrypt')
Routes.post('/register', async ({ req, res }) => {
try {
const { email, password, name } = req.body
// Validate input
if (!email || !password || !name) {
return res.status(400).json({
success: false,
message: 'All fields required',
})
}
// Get User model
const User = Database.getModel('User')
// Check if user exists
const existing = await User.findOne({ where: { email } })
if (existing) {
return res.status(400).json({
success: false,
message: 'Email already registered',
})
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10)
// Create user
const user = await User.create({
email,
password: hashedPassword,
name,
})
// Generate JWT
const token = JWT.sign({ userId: user.id })
// Send welcome email
await Mailer.send(email, 'Welcome!', 'welcome', { name })
res.json({
success: true,
token,
user: {
id: user.id,
name: user.name,
email: user.email,
},
})
} catch (err) {
Logger.set(err, 'auth')
res.status(500).json({
success: false,
message: 'Registration failed',
})
}
})Previously, config.server.options was passed directly to http.createServer(). These options (poweredBy, maxHeaderSize, etc.) are NOT valid Node.js HTTP server options. They are now discarded. Server-level timeouts (keepAliveTimeout, requestTimeout, headersTimeout) must be applied via Server.applyOptions(server) (called automatically by Boot).
// Before (v1.0.5): returned 400
// After (v2.0.0): returns 422 (correct HTTP status for validation errors)
BaseController.validationError(res, validation) // → 422 Unprocessable EntityThe Database.close() method is asynchronous and returns a Promise:
// Before (v1.0.0)
Database.close()
// After (v1.0.5+)
await Database.close()Framework Version: 3.0.2
Last Updated: 2026-05-25
For more examples and detailed guides, see the README.md.