Express based controller
It's a simple class for writting your ExpressJS controllers in the OOP-style.
Usage example
Javascript
const { Controller } = require('express-based-controller');
const keys = [
's3Alvx3JJsP',
'22AzxlevFs2',
'S2fs33kAj3l'
];
class MyConstroller extends Controller {
constructor() {
super();
this.actions = {};
this.actions.sayHello = {};
this.actions.sayHello.middlewares = [
(req, res, next) => {
req.message = req.query.name;
req.accessKey = req.query.key;
next();
}
];
this.actions.sayHello.accessControl = (req) => {
return keys.includes(req.accessKey);
};
// if you return object, it will be passed in res.json
// if you return string|number|boolean, ot will be passed in res.end
this.actions.sayHello.handler = (req, res) => {
return {
message: `Hello, ${req.message}!`
};
};
this.actions.sayHello.onError = (err, req, res) => {
return {
errors: true,
message: err.message
};
};
}
};
const myController = new MyController();
const actions = myController.compileActions();
// express app instance
const { app } = require('../app.js');
app.get('/hello', actions.sayHello);
Typescript
import { Controller, action } from 'express-based-controller';
import { app } from '../app.ts';
const keys = [
's3Alvx3JJsP',
'22AzxlevFs2',
'S2fs33kAj3l'
];
function ExtractQuery(req, res, next) {
req.message = req.query.name;
req.accessKey = req.query.key;
next();
}
export class MyController extends Controller {
// You can use action method decorator
@action({
middlewares: [ExtractQuery],
accessControl: (req) => keys.includes(req.accessKey),
onError: (err, res, res) => {
return {
errors: true,
message: err.message
};
}
})
async sayHello(req, res) {
return {
message: `Hello, ${req.message}!`
};
}
};
const myController = new MyController();
const actions = myController.compileActions();
app.get('/hello', actions.sayHello);
Global Middlewares
You can use global middlewares, access control functions and error handlers:
import auth from '../middlewares/auth';
import { Controller, action } from 'express-based-controller';
const keys = [
's3Alvx3JJsP',
'22AzxlevFs2',
'S2fs33kAj3l'
];
const checkKeys = req => keys.includes(req.key);
class SomeController extends Controller {
public middleware = [auth];
public accessControl = checkKeys;
public onError = (err, req, res) => {
return {
errors: true,
message: err.message
};
};
@action()
sayHello(req, res, next) {
return {
message: `hello, ${req.query.name || 'anonymous'}!`
};
},
@action()
sayBye(req, res, next) {
return {
message: `bye, ${req.query.name || 'anonymous'}!`
}
}
}
After compilation auth, accessControl and onError will be called together with every action (sayHello, sayBye).
Joi-validator
If you set the validator, req will be validated by Joi-schema:
local:
import { authenticate } from '../auth';
import { Controller } from 'express-based-controller';
class SomeController extends Controller {
@action({
validator: Joi.object({
body: Joi.object().keys({
username: Joi.string().required(),
password: Joi.string().required()
})
}).unknown(true),
onError: (err, req, res) => {
return {
errors: true,
message: err.message
};
}
})
async authenticate(req, res) {
if (authenticate(req.body)) {
return {
message: `Hello, ${req.body.username}!`
}
} else {
throw new Error('Wrong credentials!');
}
}
}
global:
import { Controller } from 'express-based-controller';
class SomeController extends Controller {
public validator = Joi.object({
body: Joi.object().keys({
message: Joi.string().required()
})
}).unknown(true),
@action()
sayHello(req, res, next) {
return {
message: `hello, ${req.body.message}`
};
},
}
Wrapping Exceptions
By default, Joi-validator throws native Joi ValidationError:
export interface JoiObject {
isJoi: boolean;
}
export interface ValidationError extends Error, JoiObject {
details: ValidationErrorItem[];
annotate(): string;
_object: any;
}
But you can override joiValidationFormatter method:
import { ValidationError as JoiValidationError } from 'joi';
class SomeController extends Controller {
public joiValidationFormatter(error: JoiValidationError) :any {
error.name = "Request Validation Error!";
return error;
}
}
Also, you can change access control error:
import { ValidationError as JoiValidationError } from 'joi';
class SomeController extends Controller {
public joiValidationFormatter(error: JoiValidationError) :any {
error.name = "Request Validation Error!";
return error;
},
public accessControlException() :any {
const error = new Error('Access denied!');
error.name = 'Access Error';
return error;
}
}
Compilation
compileActions public method compiles your actions, middlewares and access controll functions to a simple object, that has some middleware properties
For example:
import * as express from 'exoress';
import auth from '../middlewares/auth';
import { Controller, action } from 'express-based-controller';
const keys = [
's3Alvx3JJsP',
'22AzxlevFs2',
'S2fs33kAj3l'
];
class SomeController extends Controller {
public middlewares = [auth];
@action()
sayHello(req, res, next) {
return {
message: `hello, ${req.query.name || 'anonymous'}!`
};
},
@action()
sayBye(req, res, next) {
return {
message: `bye, ${req.query.name || 'anonymous'}!`
}
}
}
const compiledActions = new SomeController().compileActions();
const app = express();
app.listen(3000);
app.get('/hello', compiledActions.hello);
app.get('/bye', compiledActions.bye);
Middleware order into action after compilation:
- global middlewares
- action middlewares
- global access control func
- action access control func
- global request joi-validator,
- action request joi-validator,
- main action handler,
- action error handler,
- global error handler
Inheritance
import { authenticate } from '../auth';
import { Controller } from 'express-based-controller';
class AuthController extends Controller {
public middlewares = [authenticate];
public onError = (err, req, res, next) => {
return {
message: err.message
};
}
}
class SomeController extends AuthController {
@action()
sayHello(req, res, next) {
return {
message: `hello, ${req.query.name || 'anonymous'}!`
};
},
}
add middlewares:
import { authenticate } from '../auth';
import { Controller } from 'express-based-controller';
class AuthController extends Controller {
public middlewares = [authenticate];
public onError = (err, req, res, next) => {
return {
message: err.message
};
}
}
class SomeController extends AuthController {
constructor() {
super();
this.middlewares.push((req, res, next) => {
debug(req);
next();
});
}
@action()
sayHello(req, res, next) {
return {
message: `hello, ${req.query.name || 'anonymous'}!`
};
},
}