@huzapi/error-handler v1.0.2
Huz Api / Error Handler
Error Handler with multi-language/multi-tenancy support (Exception Driven Development)
- TDD (Test driven)
- Fully JsDoc
- index.d.ts included (built-in @types support)
Install to your project
npm i @huzapi/error-handler
Builders
Custom/Free Usage
const {ErrorInstance} = require('@huzapi/error-handler');
//custom errors with exception-name
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty', 411, 'Game').error();
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty', 411).error();
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty').error(); // status: 400
//custom errors without exception-name
ErrorInstance.anonymous(req, 'name is empty', 411).error(); //name: UnknownError
ErrorInstance.anonymous(req, 'name is empty').error(); // name: UnknownError, status: 400
Native ( With Try/Catch) Usage
const {ErrorInstance} = require('@huzapi/error-handler');
try {
//....
} catch (e) {
ErrorInstance.native(req, e, 'Game').error(); //if e has not status, status: 400
//or
ErrorInstance.native(req, e).error(); //if e has not status, status: 400
}
Multi-Language & Multi Tenancy Usage
const {ErrorInstance} = require('@huzapi/error-handler');
//without tenancy
ErrorInstance.appendDictionary(
{
"en": {
"BannedCountryError": "Country {{country}} is banned for this page!"
}
}, false
);
//with tenancy
ErrorInstance.appendDictionary(
{
"en": {
"BannedCountryError": {
"$default": "Country {{country}} is banned for this page!",
"project1": "Our Foobar game is banned at {{country}}"
}
}
}, true
);
ErrorInstance.i18n(req, 'BannedCountryError', {country: 'Germany'}, 403).error();
Action & Severity
Severities
Type | Usage |
---|---|
emergency | ErrorInstance.anonymous(req, 'Databese server down!').emergency(); |
alert | ErrorInstance.anonymous(req, 'disk usage is 90%').alert(); |
critical | ErrorInstance.anonymous(req, 'remote api does not work').critical(); |
error | ErrorInstance.anonymous(req, 'name is empty').error(); |
warning | ErrorInstance.anonymous(req, 'gender is empty').warning(); |
notice | ErrorInstance.anonymous(req, 'endpoint duration is too high').notice(); |
info | ErrorInstance.anonymous(req, 'endpoint duration is 30msec').info(); |
debug | ErrorInstance.anonymous(req, 'hello world').debug(); |
Actions
Note: Each action runs once (only 1, successive events will be ignored) at a context scope
Type Default Scope Info display emergency,alert,critical,error,warning,notice,info,debug console.log(...)
log emergency,alert,critical,error,warning,notice logs exception if ErrorInterception.actionLog
is setemergency,alert,critical sends email if ErrorInterception.actionMail
is setsms emergency,alert sends SMS if ErrorInterception.actionSms
is setraise emergency,alert,critical,error throw ...
Action x Severity Matrix
//you can set each action for each severity level and environment
//if you dont set, default scopes will be used
const {ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();
if (process.env.NODE_ENV === 'production') {
//parameters: display?: boolean, log?: boolean, raise?: boolean, mail?: boolean, sms?: boolean
interceptor.setSeverity('emergency', true, true, true, true, true);
///.... for all severity types
interceptor.setSeverity('debug', false, false, false, false, false);
} else {
//...
}
Easy Actions
You can call each action with by-pass severity-matrix above
Type Usage display ErrorInstance.anonymous(req, 'name is empty').display().error();
log ErrorInstance.anonymous(req, 'name is empty').log().error();
ErrorInstance.anonymous(req, 'name is empty').mail().error();
sms ErrorInstance.anonymous(req, 'name is empty').sms().error();
Action Callbacks
//you can define logger, mail sender, sms sender, display format and response-format
//if you dont set, default scopes will be used
const {ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();
// callback must be Promise
// If not set, no-log
interceptor.actionLog(async (err) => {
//You can decorate this part, err: ErrorInstance
await YourMongoLogger.add(err);
} );
// callback must be Promise
// If not set, console.log(Partial<ErrorInstance>)
interceptor.actionDisplay(async (err) => {
//You can decorate this part, err: ErrorInstance
console.log(err.message);
} );
// callback must be Promise
// If not set, no-mail-send
interceptor.actionMail(async (err) => {
//You can decorate this part, err: ErrorInstance
await YourMailSender.add(err);
} );
// callback must be Promise
// If not set, no-sms-send
interceptor.actionSms(async (err) => {
//You can decorate this part, err: ErrorInstance
await YourSmsSender.add(err);
} );
// callback must NOT be Promise
// If not set, api response is {status, name, message, track, holder}
interceptor.responseFormatter((err) => {
//You can decorate this part or you can convert to array etc, err: ErrorInstance
return [{code: err.name, message: err.message}];
} );
Multi-Language (I18N), Dictionary & Multi-Tenancy
Multi-Language and Dictionary
You must set dictionary with given format (below) You must set current user's language callback If current user's text has not defined at dictionary, first fixed key is taken from another language If current tenant's text has not defined at dictionary, first fixed key is taken from default tenant or another language
const {ErrorInstance, ErrorInterception} = require('@huzapi/error-handler'); const interceptor = new ErrorInterception();
//without tenancy /*Format {
[language: string]: {
[error-name: string]: string;
}
} */ ErrorInstance.appendDictionary( { "en": { "BannedCountryError": "Country {{country}} is banned for this page!", "anotherError": "Another message template" }, "another-language": { "BannedCountryError": "...... {{country}} ......", "anotherError": "......" } }, false );
// callback must NOT be Promise // callback must return string // If not set, first language key at dictionary will be taken interceptor.currentLanguage((err) => { //You can decorate this part, err: ErrorInstance, err.$req: Express.Request return err.$req._userLanguage; // it should be same format with language-code at dictionary which you set //or you can return static ==> return 'en'; } );
### Multi-Tenancy
> If you need to change error messages tenant by tenant, or if you want to specialize some error messages to some tenants
> You can set dictionary with tenant layer and write callback for current tenant
>
````javascript
const {ErrorInstance, ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();
//with tenancy
/*Format
{
[language: string]: {
[error-name: string]: {
[tenant-key]: string;
}
}
}
*/
ErrorInstance.appendDictionary(
{
"en": {
"$default": {
"BannedCountryError": "Country {{country}} is banned for this page!",
"anotherError": "Another message template"
},
"tenant66": {
"BannedCountryError": "...... {{country}} ......",
}
},
"another-language": {
"$default": {
"BannedCountryError": "...... {{country}} ......",
"anotherError": "......"
},
"tenant66": {
"BannedCountryError": "...... {{country}} ......",
}
}
}, true
);
// callback must NOT be Promise
// callback must return string
// If not set, default tenant at dictionary will be taken
interceptor.currentTenant((err) => {
//You can decorate this part, err: ErrorInstance, err.$req: Express.Request
return err.$req._tenantId; // it should be same format with tenant-key at dictionary which you set
//or you can return static, example ==> return 'master';
} );
Named Errors
You can define your named error classes, like Java, Php
- file: UserNotFoundError.js
const {ErrorInstance} = require('@huzapi/error-handler');
class UserNotFoundError extends ErrorInstance { constructor(req, id) { this.name = this.constructor.name; this.status = 404; this.severity = 'critical'; this.holder = 'User'; this.$req = req;
//if multi-language or dictionary usage
this.parameters = {id: id};
this.refreshMessage();//todo
//else
this.message = `User not found with id: ${id}`;
//call actions you need
// or you can check matrix if you apply your settings, @todo
this.display(); //your setting at this.getSeveritySetting().display;
this.log(); //your setting at this.getSeveritySetting().log;
this.mail(); //your setting at this.getSeveritySetting().mail;
this.sms(); //your setting at this.getSeveritySetting().sms;
//IMPORTANT you dont call severity methods,
// ie: emergency(), alert(), critical(), error(), warning(), notice(), info(), debug()
//IMPORTANT you dont call raise();
}
} module.exports = UserNotFoundError;
- file: another.js
````javascript
const {UserNotFoundError} = require('path-to/UserNotFoundError');
throw new UserNotFoundError(req, 4567);
HTTP Errors
You can use this package at express routing
const {ErrorInstance} = require('@huzapi/error-handler');
router.get('games', (req, res) => {
yourPromiseFunction(req, req.query.id).then(content => {
res.json(content).send();
}).catch(e => {
ErrorInstance.send(req, res, e, 'Game'); //response based on your responseFormatter interceptor
});
});
TODO
Dictionary, Multi-Language and Multi-Tenancy features were not developed yet :(