nodejs condense a validation of json body request against regex - node.js

Is there a condensed way to do this validation on nodejs
//inbound express request
{ "myVal":"abcdefghij" }
/* first test if value exists,
then if it fails a regex return an error & exit function
if pass then continue
*/
// ... logic to get express.post(...=> {
if (( req.body.myVal == null ){
// no value, send error response and exit function
}else{
const inVar = req.body.myVal;
if ( inVar ){
const regex = /^([a-j0-9]){1,11}$/;
if(regex.test(inVar ) == false){
res.send({ error: "failed regex" });
res.end();
return;
}
... else continue

Here are a few options:
Use express-validator or a similar package to validate your endpoint params and/or body in the endpoint configuration. This nicely separates validation from the logic of the endpoint and is extensible to your other endpoints without having to maintain code in each one.
Check out the package here:
Express-validator
Specifically, what you might want is their custom validator:
Express-validator's custom validation docs
Example usage:
const { body } = require('express-validator');
const MY_REGEX = /^([a-j0-9]){1,11}$/;
app.post('/my-endpoint',
body('myVar').custom(value => {
if(!MY_REGEX.test(value)) Promise.reject("Error: invalid value for 'myVar.'")
}),
(req, res) => {
// Business logic only, no validation
},
);
You could even take the function that's inside .custom(), and put it in another file, import it here, and reuse it elsewhere.
If you want to do the validation in the handler, there are a few ways to make your code more brief/safe/nice.
Set your regexp variable in a different file or as a constant at the top of the file (descriptively) or in a different constants/utils file.
"Error gate" by returning after you find an error. Avoid nested if/else.
Is the error the same for missing value and incorrect value? If so, your regexp will error appropriately for undefined, empty string, and null anyway. consider combining them in the same if statement.
Example:
// imports
// ...
const MY_DESCRIPTIVE_REGEX_NAME = /^([a-j0-9]){1,11}$/;
//...
(req, res) => {
if(!MY_DESCRIPTIVE_REGEX_NAME.test(req.body.myVal) {
res.status(400);
return res.send({ error: "MyVal must be between 1 and 11 characters and contain only numbers and letters between 'a' and 'j'."});
}
// rest of logic, not in an if statement
}

Related

Node JS: How to catch the individual errors while reading files, in case multiple files are read on Promise.all?

I am having 10 different files and I need to read their content and merge it in one object (in NodeJS). I am successfully doing that with the code below:
const fs = require('fs');
const path = require('path');
const { promisify } = require("util");
const readFileAsync = promisify(fs.readFile);
let filePathArray = ['path/to/file/one', ... , 'path/to/file/ten'];
Promise.all(
filePathArray.map(filePath => {
return readFileAsync(filePath);
})
).then(responses => { //array of 10 reponses
let combinedFileContent = {};
responses.forEach((itemFileContent, index) => {
let tempContent = JSON.parse(itemFileContent);
//merge tempContent into combinedFileContent
}
});
But what I wonder is, how to catch if there is some error while trying to read the files? When reading a single file, this works like:
fs.readFile(singleFilePath, (singleFileErr, singleFileContent) => {
if (singleFileErr) {
//do something on error, while trying to read the file
}
});
So my question here is, how can I access to the error inn the first code snippet, which corresponds to singleFileErr from this second code snippet?
The issue I am facing is: in case some of the files does not exists, I want to check the error and to skip this file, but since I can not detect the error with current implementation, my whole block crashes and I am not able to merge the other 9 files because of this one. I want to use the error check I mentioned in the second snippet.
Check out the Promise.allSettled function, which will run every Promise passed to it, and will tell you at the end which ones succeeded and which ones failed.
Maybe try something like this:
in the map() callback, return a promise that resolves to null if the file is not found.
Introduce a middle stage in the promise chain filtering out null responses.
This would look something like this:
Promise.all(
filePathArray.map(filePath => {
return readFileAsync(filePath).catch(function(error){
if(isErrorFileDoesNotExist(error)) return null
throw error;
})
});
).then(responses => {
return responses.filter(response => response != null)
})
.then(filteredResponses => {
// .. do something
});
Would that work for you? Note this presupposes you are actually able to discriminate between missing file errors from other errors the promise returned by readFileAsync() may reject - presumably via the isErrorFileDoesNotExist() function in this snippet.

Compare API response against itself

I am trying to:
Poll a public API every 5 seconds
Store the resulting JSON in a variable
Store the next query to this same API in a second variable
Compare the first variable to the second
Print the second variable if it is different from the first
Else: Print the phrase: 'The objects are the same' if they haven't changed
Unfortunately, the comparison part appears to fail. I am realizing that this implementation is probably lacking the appropriate variable scoping but I can't put my finger on it. Any advice would be highly appreciated.
data: {
chatters: {
viewers: {
},
},
},
};
//prints out pretty JSON
function prettyJSON(obj) {
console.log(JSON.stringify(obj, null, 2));
}
// Gets Users from Twitch API endpoint via axios request
const getUsers = async () => {
try {
return await axios.get("http://tmi.twitch.tv/group/user/sixteenbitninja/chatters");
} catch (error) {
console.error(error);
}
};
//Intended to display
const displayViewers = async (previousResponse) => {
const usersInChannel = await getUsers();
if (usersInChannel.data.chatters.viewers === previousResponse){
console.log("The objects are the same");
} else {
if (usersInChannel.data.chatters) {
prettyJSON(usersInChannel.data.chatters.viewers);
const previousResponse = usersInChannel.data.chatters.viewers;
console.log(previousResponse);
intervalFunction(previousResponse);
}
}
};
// polls display function every 5 seconds
const interval = setInterval(function () {
// Calls Display Function
displayViewers()
}, 5000);```
The issue is that you are using equality operator === on objects. two objects are equal if they have the same reference. While you want to know if they are identical. Check this:
console.log({} === {})
For your usecase you might want to store stringified version of the previousResponse and compare it with stringified version of the new object (usersInChannel.data.chatters.viewers) like:
console.log(JSON.stringify({}) === JSON.stringify({}))
Note: There can be issues with this approach too, if the order of property changes in the response. In which case, you'd have to check individual properties within the response objects.
May be you can use npm packages like following
https://www.npmjs.com/package/#radarlabs/api-diff

How to get a specific item from JSON

I'm new to node.js and DialogFlow. I am using DynamoDB to store data and I'm creating skills on Google. I'm trying to write a code to retrieve a specific item on that table.
I've got it working to show all items where ID is equal = 1, but how would I make it so I can just get attribute 'name'?
My idea is a user provides an id then the code will retrieve name where id was 1 and store that as a variable and use agent.add('hello $(name)'); to display it back as speech to the user.
function readdata(agent){
let dbread = new aws.DynamoDB.DocumentClient();
const id = agent.parameters.id;
let read = function(){
var parameters = {
TableName:"Dynamodb",
Key:{
"id":id
}
};
dbread.get(parameters, function(err,data){
if(err){
console.log("error",JSON.stringify(data,null,2));
}else{
console.log("success",JSON.stringify(data,null,2));
}
});
agent.add(`hello ${name}`);
};
read();
}
Once you have the data back from the get() call, the data object will contain an Item attribute. The value of this attribute will be another object that contains the attribute/value pairs for this record, or be empty if the record isn't found.
The debugging you have in place that shows JSON.stringify(data) should show this.
Assuming you knew all the fields were there, you could do something like
const name = data.Item.name;
a more robust way using current JavaScript would be to make sure everything was assigned, otherwise return undefined at any point. So something like this would work
const name = data && data.Item && data.Item.name;
However - you will have a problem doing this with Dialogflow
You don't show which Dialogflow library you're using, but most of them require you to return a Promise to indicate that it needs to wait for asynchronous calls (such as the call to DynamoDB) to complete. You're using get() with a callback function instead of a Promise. So you need to do one of the following:
Wrap the call in a Promise
Since get() returns an AWS.Request you can use the promise() method of this to get a Promise that you can return and which has then portions that generate the response - similar to how you're doing your callbacks now.
Under this scheme, your call might look something like this (untested):
return dbread.get(parameters).promise()
.then( data => {
console.log("success",JSON.stringify(data,null,2));
const name = data && data.Item && data.Item.name;
if( name ){
agent.add( `Hello ${name}` );
} else {
agent.add( "I don't know who you are." );
}
})
.catch( err => {
console.log("error",JSON.stringify(data,null,2));
agent.add( "There was an error" );
});

After modifying express.js res.send function it won't emit any data

Basically what I'm trying to do is unify every response of my application just so they look like this
{
success: true,
error: "Error description",
errorCode: "SomeError",
data: {
...
}
}
Copying and pasting this structure everywhere as well as wrapper function looks quite ugly to me, so I've tried to modify send function so that it accepts 4 params instead of only 1 and then makes given structure.
I've found various examples on how to modify send function and came up with this middleware
app.use(function (req, res, next) {
const oldSend = res.send;
res.send = function (data = {}, success = true, error = '', errorCode = '') {
let response = {
data,
success,
error,
errorCode
};
oldSend.apply(res, response);
};
next();
});
so the res.send call looks like this
res.json(req.session.key != null, true, null, null);
Which works fine except it doesn't return any response to the client. Could you help me solve this? Is it possible to achieve expected behaviour?
You are using function.apply which expects a this and then an array of the function arguments (see MDN for function.prototype.apply). What you need to use instead is function.call (again see MDN for function.prototype.call).
However, a word of caution to be careful of overwriting a low level part of express (or any library) - you don't know what else might be calling this so you may get some unexpected results. You might like to check (for example) that the supplied parameter is of a type which you expect (e.g. an object) and pass anything unexpected (e.g. a Buffer) straight through to the original send.

code explanation nodejs expressjs mongoose

i feel a bit embarrassed, can you please kindly explain parts of the code?
For example, I have no idea, what is this part? where can I read more about it?
function parsePostStory(data) {
return {
name : data.name
}
}
What is req.body? Is it json req body?
Why do we declare empty array and why do we return it? Just for the clarity?
Is Story.create just a mongoose method?
The rest of the code is here:
router.post('/stories', function(req, res) {
var validation = validatePostStory(req.body);
if(validation.length > 0) {
return res.badRequestError(validation);
}
var story = parsePostStory(req.body);
Story.create(story, function(err, story) {
if(err) {
console.log(err.message);
return res.internalServerError();
} res.send(story);
});
});
function validatePostStory(data) {
var array = [];
if (!data.name || typeof data.name !== 'String') {
return array.push('name');
}
return array;
}
function parsePostStory(data) {
return {
name : data.name
}
}
Sorry once more for that kind of a question and thanks a ton.
I'm assuming you know how the request-response cycle works with HTTP requests and the client-server interactions with it. If not, Wikipedia Request-Response and Client-Server (Two link limit, otherwise I would have posted them as links)
A request sends a lot of information to the server. If you console.log the request in NodeJS, you will see that it contains a lot of information that isn't entirely relevant to what you need.
You're using Express as your web framework. In this case, req.body is the information that you are sending to the server from the client. Using req.body will make sure that you're not using the extra information passed in to the server from the client. Req.body is your code that you want. (Note: Req.body isn't natively supported by Express v4, you'll have to use something like body-parser) See Express docs for more details
Now, let's break up this code a bit. You essentially have 3 separate functions. Let's take a look at validatePostStory.
function validatePostStory(data) {
var array = [];
if (!data.name || typeof data.name !== 'String') {
return array.push('name');
}
return array;
}
This function is a validation function. It takes one argument - an object and returns an array. Effectively, what this is doing is checking if the name is a string or not - if not, return an array that has a length of 1. The following conditional checks length and returns a 400 if greater than 0
if(validation.length > 0) {
return res.badRequestError(validation);
}
I'm not entirely sure why this needs to be a separate function. Looks like you can probably just do this instead.
if (!req.body.name || typeof req.body.name !== 'String') {
return res.badRequestError(validation);
}
The following function function essentially converts the data so that mongodb/mongoose can store it in the proper format
function parsePostStory(data) {
return {
name : data.name
}
}
It's the same as saying:
var story = {name: req.body.name}
I would assume Story.create is a custom mongoose method yes.

Resources