I have one file with constant definitions (common.js):
var Errors = Object.freeze({
SUCCESS: {code: 0, message: 'Success'},
NOT_ENOUGH_ARGUMENTS: {code: 1, message: 'Not enough arguments provided'},
NOT_ALLOWED_ARGUMENT: {code: 2, message: 'Argument value is not allowed'}
});
module.exports = Errors;
And another file that uses this one (profile.js):
var Errors = require('../../common');
module.exports.profileCreate = function (req, res) {
var name = req.query.name;
var email = req.query.email;
if (!name || !email) {
res
.status(403)
.json({error: Errors.NOT_ENOUGH_ARGUMENTS});
return;
}
// ...
}
It looks to me that module.exports in first file and var Errors=require() in second one are having excessive syntax. Moreover I don't know what to do if I'd like to make some more enum constants instead of single Errors object.
What I have to do in order to use my enum object in other files of the project? Should I write down a bunch of exports for every enum object in the future, like:
module.exports.Errors = Object.freeze({ ... });
module.exports.Result = Object.freeze({ ... });
// ...etc
Related
I have some issue writing typescripe code for a firebase cloud function.
I assume it is mainly a syntax problem.
The code is below, but the relevant part is the what concerns the call of the myLocalFunc function. The rest is only here to provide some context. The part reading:
response:Response<any>
in:
const myLocalFunc = (mail:string, flag:boolean, response:Response<any>) => {...}
is wrong. Because I get this error message:
error TS2315: Type 'Response' is not generic.
What is the proper syntax?
const myLocalFunc = (mail:string, flag:boolean, response:Response<any>) => {
admin.auth().getUserByEmail(mail)
.then(function(userRecord) {
// Do some useful work.
const data = {
boolField: flag,
};
const refStr = "/Users/"+userRecord.uid;
admin.database().ref(refStr).set(data);
})
.catch(function(error) {
console.log("Error fetching user data:", error);
let rspMsg = "This user (";
rspMsg += mail;
rspMsg += ") does not exists.";
response.send(rspMsg);
});
};
exports.myFunc = functions.https.onRequest(function(req, resp) {
resp.set("Access-Control-Allow-Origin", "*");
resp.set("Access-Control-Allow-Methods", "GET, POST");
corsHandler(req, resp, async () => {
const from = String(req.body.from);
const idToken = String(req.body.token);
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
const uid = decodedToken.uid;
const refStr = "/Users/"+uid;
const ref = admin.database().ref(refStr);
ref.on("value", function(snapshot) {
console.log("snapshot.val():", snapshot.val());
if (snapshot.val() !== undefined) {
const snv = snapshot.val();
if (snv.adminRights !== undefined) {
if (snv.adminRights) {
// Only if we reach this point,
// can we perform the operation next line.
myLocalFunc(from, true, resp);
}
}
}
}, function(errorObject) {
console.log("The read failed: " + errorObject);
});
}).catch(function(error) {
// Handle error
functions.logger.log("(FL)error:", error);
console.log("(CL)error:", error);
});
}); // End corsHandler.
});
Note:
I got the idea of trying Response<any> for the type (without much conviction) after reading some some documentation for functions.https.onRequest.
If I change the code to:
const myLocalFunc = (mail:string, flag:boolean, response) => {...}
which is in fact what I started with.
I get this error:
error TS7006: Parameter 'response' implicitly has an 'any' type.
If I try to change the code to this:
const myLocalFunc = (mail:string, flag:boolean, response:Response) => {...}
I get these two errors:
37:18 - error TS2339: Property 'send' does not exist on type 'Response'.
37 response.send(rspMsg); // This works.
~~~~
79:46 - error TS2345: Argument of type 'Response<any>' is not assignable to parameter of type 'Response'.
Type 'Response<any>' is missing the following properties from type 'Response': headers, ok, redirected, statusText, and 9 more.
79 myLocalFunc(from, true, resp);
The req and res parameters in onRequest() are Request and Response objects from Express. When not importing Response from Express, it's another interface which also is not generic.
import {Response} from "express";
const myLocalFunc = (mail: string, flag: boolean, response: Response) => {...}
I'm trying to build a route middleware function to validate a form, but I got a little confused about how should I get errors.
How is validationErrors populated and how should I access it inside route function? The examples I found at the docs and other sites did not helped me
route:
use strict';
const express = require('express');
const router = express.Router();
const User = require('../back/api/models/UserModel');
const Helper = require('./handlerInputs.js');
const bcrypt = require('bcrypt');
router.post('/registrar', [Helper.validaRegistro], function (req, res, next) {
const errors = validationResult(req).throw();
if (errors) {
return res.status(422).json({ errors: errors });
}
[... user register code .... ]
});
handler:
'use strict'
const { check, validationResults } = require('express-validator');
exports.validaRegistro = function(req, res, next){
check(req.body.nome)
.not().isEmpty()
.withMessage('Nome é obrigatório')
.isLength({min: 3, max: 20})
.withMessage('Nome deve ter entre 3 e 20 caracteres')
.isAlpha('Nome deve ser literal');
check(req.body.email)
.normalizeEmail()
.isEmail()
.withMessage('Email inválido');
optPwd = {
checkNull: false,
checkFalsy: false
}
check(req.body.password)
.exists(optPwd)
.withMessage('Senha é obrigatória');
check(req.body.password === req.body.passordconf)
.exists()
.withMessage('Confirme a senha')
.custom((value, { req }) => value === req.body.password)
.withMessage('Senhas não são iguais')
.custom((value, { req }) => value.length >= 8)
const result = req.getValidationResults();
const erros = req.ValidationErrors;
if(erros){
console.log(erros);
}
????
}
What you can do is, Just write validation logic inside middleware itself rather than writing the same thing again and again on the different controller.
Another best way to create common logic is to put validation rules on different files and put handling validation logic in a different file.
Please follow this URL, I have implemented the same thing with efficient way.
https://github.com/narayansharma91/node_quick_start_with_knex
if(erros){
const status = 422;
res.status(status).json({
success: false,
status,
errors: errors.array(),
});
}
In Sequelize (REST API on EXPRESS) only in POST (CREATE) request, i calling API Route which call function from controller where I use: creating instance with associations, model + association + route look fine, but i got error:
Error: Both replacements and bind cannot be set at the same time
I cant solve this, a spent a lot of time about this error, yet.
Please can anybody help me with this?
I tried:
middleware error handler with return async, same result.
Remove "freezeTableName", nothing changed.
config.js:
const AddressModel = require('../model/address.model');
const CompanyModel = require('../model/company.model');
const Address = AddressModel(sequelize, Sequelize);
const Company = CompanyModel(sequelize, Sequelize);
Company.belongsTo(Address);
....
router.js:
module.exports = function (app) {
let company = require('../controllers/company.controller.js');
// Create a new Customer
app.post('/api/company', company.createCompany);
....
company.controller.js:
const db = require('../config/db.config.js');
const Company = db.Company;
const Address = db.Address;
// Post a Company
exports.createCompany = async (req, res) => {
const transaction = await db.sequelize.transaction();
try {
let company = await Company.create({
name: req.body.name,
Address: {
whole_address: 'TEST ADDRESS FIELD',
ruian_adm_code: 126252
}
}, {
include: [Address],
transaction
});
await transaction.commit();
if (company) {
res.json({
success: 1,
data: company,
message: 'Company created.'
});
}
} catch (ex) {
await transaction.rollback();
res.json({success: 0, message: ex});
}
};
....
RETURN = Error: Both replacements and bind cannot be set at the same time
I am developing a RESTful API using NodeJS and Express.
I noticed that incoming requests sometimes lack of some expected variables, which cause the program to crash, saying it couldn't set the value of a variable, to an 'undefined' value - as no value arrived with the request.
Example:
The application is expecting variableY, but instead variableX is being sent:
formData: { variableX: 'valueX' }
The program is expecting to receive variableY, with the following code:
const checkVariables = Joi.validate({
variableY: req.body.variableY,
}, schema);
The application crashes with the following error:
TypeError: Cannot read property 'variableY' of undefined
I thought about a few ways to handle that, including declaration of variables upon application initiation and using them along, using try-catch.
Another way will be to use if-else, if-chaining, or case-switch, but as you understood of course I am looking for the cleanest way to achieve that.
Any ideas?
Thank you.
** EDIT **
Progressed and managed to achieve the result using the object only. Once trying to reach any of it's inner fields the error will be thrown anyway, example:
if(req.body.variableY == undefined){console.log('The expected variable is undefined');} //true
When the validation addresses a field inside the 'undefined' object:
if(req.body.variableY.dataId == undefined){console.log('The expected variable is undefined');} //crashes
The following error is being thrown again:
TypeError: Cannot read property 'variableX' of undefined
After doing some more digging around, found this Stackoverflow thread:
How to check if object property exists with a variable holding the property name?
Tried using hasOwnProperty, but the same kind of error is being thrown:
TypeError: Cannot read property 'hasOwnProperty' of undefined
Tried wrapping variable declaration using try-catch, still didn't work:
try{
var variableX = req.body.variableX
var variableXDataId = req.body.variableX.dataId
}
catch(e){
res.status(400).send('Wrong request error: Please check your request variables and try again');
}
As this is a really basic validation that should be addressed by most of the RESTful APIs (validating that you get the expected incoming variables inside the request, so the program won't crash by having errors it can't handle - what is the common solution for such problems (expected / unexpected request validation)?
Thank you.
You can take another approach, check req.body before you reach checkVariables:
let body = req.body;
// data - your req.body
// requiredKeys - is an array of strings , [ key1, key2 ... keyN] | string[]
const setKeys = ( data, requiredKeys )=>{
if( !typeof requiredKeys.length ){
requiredKeys = [];
}
if(requiredKeys.length) requiredKeys.forEach( k =>{
k = k.replace(/\+/g,'/');
let keysList = [];
if( /\/+/g.test(k)){
keysList = k.split('/');
}else{
keysList = [k];
}
let [firstKey, ...rest] = keysList;
if( typeof data[firstKey] === 'undefined' ){
data[firstKey] = {};
}
if( rest.length ){
data[firstKey] = setKeys(data[firstKey], [rest.join('/')] );
}
})
return data;
}
let checkedData= setKeys(body, ['variableT','variableP/noname/emptyObj','custom/object/does/not/exist/but/it/will/be/created/here']);
const checkVariables = Joi.validate(checkedData, schema);
UPDATE
Below you will find an working example on how things should work during a /(let's say /usersStatus/:id ) request:
const express = require('express')
const app = express()
const port = 3000
const setKeys = (data, requiredKeys) => {
if (!typeof requiredKeys.length) {
requiredKeys = [];
}
if (requiredKeys.length) requiredKeys.forEach(k => {
k = k.replace(/\+/g, '/');
let keysList = [];
if (/\/+/g.test(k)) {
keysList = k.split('/');
} else {
keysList = [k];
}
let [firstKey, ...rest] = keysList;
if (typeof data[firstKey] === 'undefined') {
data[firstKey] = {};
}
if (rest.length) {
data[firstKey] = setKeys(data[firstKey], [rest.join('/')]);
}
})
return data;
}
/**
* Mock some data
*/
const getUserData = (req, res, next) => {
if (typeof req.body === 'undefined') {
req.body = {};
}
req.body = {
variableY: {
someName: 23
},
variableZ: {
name: 3,
type: {
id: 5,
typeName: 'something',
tags: ['a', 'b', 'c']
}
}
};
console.log('Middleware 1 getUserData');
next();
}
/**
* 1. Setup our middleware for checking keys
* "requiredKeys" is an array of strings
*/
const middlewareSetKeys = (requiredKeys, wrappedMiddleware) => {
return (req, res, next) => {
console.log('Middleware 2 middlewareSetKeys');
if (typeof req.body === "undefined") {
console.log('Leaving Middleware 2 since we don\'t have req.body');
next();
}
/**
* Update "req.body" with keys that we want to have available
* in our next middleware
*/
req.body = setKeys(req.body, requiredKeys);
if (typeof wrappedMiddleware === 'function') {
return wrappedMiddleware.call(this, req, res, next);
} else {
next();
}
}
}
/**
* 2. Let's assume a "user status" situation
* 2.1. We need userInfo from database
* 2.2. Some info won't be retrieved, unless the user accesed some parts of the website to trigger some mechanisms that allows those fields to be exposed, therefore the lack of keys
* 2.3. But we know those keys/objects, and we still want to be present so our code won't crash.
*/
// lets call our getUserData
app.get(
'/', // this path is for some userInfo
getUserData, // this returns userInfo and appends it to `req.data`
middlewareSetKeys([
'userActivity/daily/jobs', // these won't exist in getUserData because the user is lazy and he didn't apply for any JOBS
'userStatus/active/two-weeks-ago', // these won't exist in getUserData because the user joined two days ago. BUT WE STILL NEED IT coz reazons.
]), // We set our desired-later-to-use keys
(req, res, next) => {
/**
* 3. Now our req.body will have our keys
* even if they didn't exist in the getUserData middleware
*/
console.log('Middleware 3 Your middleware');
console.log(req.body);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(req.body, null, 2))
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
you can use express validator https://www.npmjs.com/package/express-validator
to validate incoming request.Then add this to your controller where a,b,c ,d are parameters you want to valaidate
const nonEmptyFields = ['a', 'b', 'c', 'd'];
nonEmptyFields.forEach(field => req.assert(field, `${field} cannot be blank`).notEmpty());
const errors = req.validationErrors();
if (errors) {
return res.status(400).send(errors);
}
for validating a field inside a field you can try doing this
typeof(req.body && req.body.name !== undefined)
A solution will be to set a default empty object to replace undefined at a parent level:
// checking for body.variableX.variableZ with object destructuring ES6
const {body = {}} = request;
const {variableX = {}, variableY} = body;
const {variableZ} = variableX.variableZ;
// or prior ES6
var body = request.body || {};
var variableX = body.variableX || {};
var variableY = variableX.variableY;
// or in a statement
var variableY = request.body && request.body.variableX ? request.body.variableX.variableY : undefined;
Based on that you can create your own function like getValue(request, 'body.variableX.variableY') to return null if any parent or the end value is undefined:
// asumes the value in the path is either object or undefined
function getValue(rootObj, path = '') {
const parts = key.split('.');
let value = rootObj || {};
let part;
while ((part = parts.shift()) && value !== null) {
value = value[part] || null;
}
return value;
};
Is there any way that I can change the default error output? Say I'm going to change the rest error output:
{
"code": "InvalidArgumentError",
"message": "blah blah..."
}
to:
{
"code": 10001,
"message": "blah blah",
"extraMsg": "blah blah"
}
Here are some of my ideas:
Listen to the error events.
It seems like not all the RestError have emitted extra events (like NotFound, MethodNotAllowed, VersionNotAllowed... do). So I can't catch all the errors to rewrite them.
Listen to an event before response data sent.
I look through the official documents and have found nothing relative.
Modify the implementation of the RestError class.
Well it's obviously not a good approach.
Any other ideas?
Finally I provide a customized JSON formatter to get what I want:
var server = restify.createServer( {
formatters: {
'application/json': function customizedFormatJSON( req, res, body ) {
// Copied from restify/lib/formatters/json.js
if ( body instanceof Error ) {
// snoop for RestError or HttpError, but don't rely on
// instanceof
res.statusCode = body.statusCode || 500;
if ( body.body ) {
body = {
code: 10001,
scode: body.body.code,
msg: body.body.message
};
} else {
body = {
code: 10001,
msg: body.message
};
}
} else if ( Buffer.isBuffer( body ) ) {
body = body.toString( 'base64' );
}
var data = JSON.stringify( body );
res.setHeader( 'Content-Length', Buffer.byteLength( data ) );
return data;
}
}
} );
While the answers above might work, the easiest way to add a custom field to the error body is to call the restify error constructor with an object (hash) instead of a string. The object has to contain the body key which is what you will see in the browser.
For example:
return next(new restify.InvalidArgumentError({body: {field: 'password', message: 'Password has to be at least 6 characters long'}}));
or
return next(new restify.UnauthorizedError({body: {foo: 'bar', name: 'john doe', message: 'whatever error message'}}));
Restify offer many ways to implement error management : http://mcavage.github.io/node-restify/#Error-handling
Why don't you create a new error type "myError" just like sample code :
var restify = require('restify');
var util = require('util');
function MyError(message) {
restify.RestError.call(this, {
restCode : 'MyError',
statusCode : 418,
message : message,
constructorOpt: MyError
});
this.name = 'MyError';
}
util.inherits(MyError, restify.RestError);
For common errors I think that overloading methods is not such a bad idea... (I don't speak about modifying restify, just overloading functions using prototype)
(edited)
I was able to provide additional data adding a property to the body object.
Notice the this.body.errors = errors line
var restify = require('restify');
var util = require('util');
function ValidationError(message, errors) {
restify.RestError.call(this, {
restCode: 'ValidationError',
statusCode: 400,
message: message,
constructorOpt: ValidationError
});
this.name = 'ValidationError';
this.body.errors = errors; //<---
}
util.inherits(ValidationError, restify.RestError);
`
You can use restify-errors-options
Your example simply becomes:
const restify = require('restify');
const errors = require('restify-errors');
const errorsOptions = require('restify-errors-options');
errorsOptions.add('extraMsg');
const err = new errors.BadRequestError({extraMsg: 'whatever you want'});
err.toJSON();
//=> {code: 'BadRequest', message: '', extraMsg: 'whatever you want'}
Please also note that the solution provided was only tested on restify 5.x
Follow this issue for more information.