Long time reader first time question asker. My question is about dynamically accessing function names in express ^4.17.1. I have poured over both the internet and the express documentation with no luck.
I currently have input validation set up on all of my express middleware functions that ensures I'm getting the right information in the right format. When bad or incomplete information is given to a particular middleware I throw a nicely formatted error to the global error handler and I include the route that errored, the file in which the error occured, and the exact middleware in which it occured.
I've been able to dynamically retrieve both the route and the file name, but I'm having issues finding a way around hard coding in the name of the middleware. I can access the route stack and get the names of ALL middleware in the route, but there doesn't seem to be a way to determine which one you're in aside from keeping that list on res.locals and changing the array as you go (which isn't ideal). Here is a simple example of what I'm talking about.
const path = require("path");
const currFile = path.basename(__filename);
module.exports = {
getStudentAssets(req, res, next) {
const { studentName } = req.body;
if (typeof studentName !== "string") {
const iWouldLoveToGetThisDynamically = "getStudentAssets";
const error = {
status: 400,
log: `ERROR on route ${req.originalUrl} in ${currFile} ${iWouldLoveToGetThisDynamically} middleware`,
};
return next(error);
}
// do random stuff
},
};
I have a feeling there is a way to track which layer you're currently on as accessing req.route.stack looks something like this
[
Layer {
handle: [Function: getStudentAssets],
name: 'getStudentAssets',
params: undefined,
path: undefined,
keys: [],
regexp: /^\/?$/i { fast_star: false, fast_slash: false },
method: 'get'
},
Layer {
// other middleware
}
]
There has to be a way to identify which layer you're currently on, other than keeping a counter stored separately on res.locals that you update every middleware. If you've read this far thank you!
So I figured it out! What I'm doing is basically just rebuilding the trace so all I had to do was access error.stack and it had all the information I could possibly want.
Related
I am working on building a blog API for a practice project, but am using the data from an external API. (There is no authorization required, I am using the JSON data at permission of the developer)
The idea is that the user can enter multiple topic parameters into my API. Then, I make individual requests to the external API for the requested info.
For each topic query, I would like to:
Get the appropriate data from the external API based on the params entered (using a GET request to the URL)
Add the response data to my own array that will be displayed at the end.
Check if each object already exists in the array (to avoid duplicates).
res.send the array.
My main problem I think has to do with understanding the scope and also promises in Axios. I have tried to read up on the concept of promise based requests but I can't seem to understand how to apply this to my code.
I know my code is an overall mess, but if anybody could explain how I can extract the data from the Axios function, I think it could help me get the ball rolling again.
Sorry if this is a super low-level or obvious question - I am self-taught and am still very much a newbie!~ (my code is a pretty big mess right now haha)
Here is a screenshot of the bit of code I need to fix:
router.get('/:tagQuery', function(req, res){
const tagString = req.params.tagQuery;
const tagArray = tagString.split(',');
router.get('/:tag', function(req, res){
const tagString = req.params.tag;
const tagArray = queryString.split(',');
const displayPosts = tagArray.map(function(topic){
const baseUrl = "https://info.io/api/blog/posts";
return axios
.get(baseUrl, {
params: {
tag: tag
}
})
.then(function(response) {
const responseData = response.data.posts;
if (tag === (tagArray[0])){
const responseData = response.data.posts;
displayPosts.push(responseData);
} else {
responseData.forEach(function(post){
// I will write function to check if post already exists in responseData array. Else, add to array
}); // End if/then
})
.catch(function(err) {
console.log(err.message);
}); // End Axios
}); // End Map Function
res.send(displayPosts);
});
Node.js is a single thread non-blocking, and according to your code you will respond with the result before you fetching the data.
you are using .map which will fetch n queries.
use Promise.all to fetch all the requests || Promise.allsettled.
after that inside the .then of Promise.all || promise.allsettled, map your result.
after that respond with the mapped data to the user
router.get('/:tag', function (req, res) {
const tagString = req.params.tag;
const tagArray = queryString.split(',');
const baseUrl = "https://info.io/api/blog/posts";
const topicsPromises=tagArray.map((tobic)=>{
return axios
.get(baseUrl, {
params: {
tag: tag
}
})
});
Promise.all(topicsPromises).then(topicsArr=>{
//all the data have been fetched successfully
// loop through the array and handle your business logic for each topic
//send the required data to the user using res.send()
}).catch(err=>{
// error while fetching the data
});
});
your code will be something like this.
note: read first in promise.all and how it is working.
I'm working on a simple login for a web application, and can't seem handle .getValidationResult() correctly. I've spent quite a bit of time pouring over the npm documentation for express-validator, trying to find an answer in tutorials, and looking on sites like Stack Overflow without managing to find the answer to my question. Perhaps I just don't know the right question to ask.
I want to ensure that
the user submitted something that has the form of an email address,
that the password isn't empty. I then want to
sanitize the email before interacting with the DB later on, then
check to see if any of the first 3 procedures failed. If there were failures, return the user to the login page.
My question is what is the correct way to use express-validator's .getValidationResult()?
Here's the offending piece of code:
export let postLogin = (req: Request, res: Response, next: NextFunction) => {
req.assert("email", "Email is not valid").isEmail();
req.assert("password", "Password cannot be blank").notEmpty();
req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
req.getValidationResult().then(function(result){
if (result != undefined) {
console.log(result.array();
return res.redirect("/login");
}
});
//do other login related stuff
}
I'm guessing that something simple is causing my error here, but I can't seem to find what it is.
It returns a promise for an object called Validation Object. This object contains information about the errors that your application has had.
The explanation.
Runs all validations and returns a validation result object for the
errors gathered, for both sync and async validators.
All it does is returning errors if there is one. Here is some example code returned by that function.
//The error object
{
"msg": "The error message",
"param": "param.name.with.index[0]",
"value": "param value",
// Location of the param that generated this error.
// It's either body, query, params, cookies or headers.
"location": "body",
// nestedErrors only exist when using the oneOf function
"nestedErrors": [{ ... }]
}
The function returns isEmpty() when there is no errors to display.
The function returns .array([options]) if there are any errors. Errors are located in [options] array.
Check out this link for the example code of what it might return.
UPDATE
You can also just use it like this, which is easier.
Please note that this is new API as of v4.0.0 release of express-validator.
const { check, validationResult } = require('express-validator/check');
//go to a link
app.get('/myURL', (req, res, next) => {
// Get the validation result\
const errors = validationResult(req).throw();
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors }); //err.mapped()
});
I have a generic SendMail route which I want to create multiple remote methods to handle multiple request templates. Any ideas on how to return a Email_Type from the remote method back to the base route. I know I could add a default with a code in it, but would like a more elegant solution.
Mail.genericSendMail = function genericEmail(response, callback) {
console.log(response);
let templateId=0;
//PROBLEM: HOW TO KNOW WHICH REMOTE WAS USED
switch (response.emailType) {
case "Template-1":
templateId= 1234;
break;
case "Template-2":
tempalteId = 456;
break;
default:
templateId = 789l
} //switch
console.log(templateId);
};
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate1",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {firstName:"", lastName:"",emailAddress:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate2",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {emailAddress:"", promoCode:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
There are a couple of different ways to do this. Since it happens to be a POST request, I usually go with attaching data to the body using a before remote hook.
Let's say you have a model method for logging in users.
Say we have a multi realm platform, so we need to know what platform we are logging in. If you don't use realms or don't know what they are, don't worry. This just shows you how to populate the data to the model method.
User.login = function(data, cb) {
if (data.realm == 'platform1) {
return logUserIntoPlatform1(data, cb);
}
return logUserIntoDefaultPlatform(data, cb);
}
Now let's say you don't want the client/frontend to send the realm and you don't want to do the lookup for realm in the model. We can add a beforeRemote hook like so:
User.beforeRemote('login', function (context, user, next) {
context.args.data.realm = lookUpRealmSync(context); // 1
next();
});
This will be called before the login method. Note the next() call: this is how you could do error detection before actually hitting the model method. Something like next({ status: 422, message: 'Parameter missing: password }); would return an error and not execute the User.login method.
You may have to look carefully at your context object (i.e. the line marked with 1 may not work exactly as I've shown for you).
If you want to read more about this stuff, I LoopBack's docs are pretty good. It seems they've been updated since I've last used them so I can't link you to the more useful pages. I found the remote method documentation here though.
Edit: I took a closer look at your question. You should be able to retrieve the path from the context object and pass data accordingly. I'm not going to try to code that since I don't know where it would actually be within the object.
i'm fairly new to node.js so this could potentially a total noob question. Anyway. I discovered the mean.io Project. In the official article-example on Github, there is the following method in the article-controller.
exports.update = function(req, res) {
var article = req.article;
article = _.extend(article, req.body);
article.save(function(err) {
if (err) {
return res.jsonp(500, {
error: 'Cannot update the article'
});
}
res.jsonp(article);
});
};
With a corresponding route
module.exports = function(Articles, app, auth) {
app.route('/articles')
.get(articles.all)
.post(auth.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.show)
.put(auth.requiresLogin, hasAuthorization, articles.update)
.delete(auth.requiresLogin, hasAuthorization, articles.destroy);
// Finish with setting up the articleId param
app.param('articleId', articles.article);
};
So I'm confused. When and where does the route pass the req/res parameters to the articles.update, or any other articles function? Is there some hidden mechanism in node/express/mean I've missed out?
Thanks in advance.
app.route('/articles/:articleId')
.get(articles.show);
This means express will invoke articles.show method with request and response as first two parameters when a GET request comes with matching path
.
I am developping a RESTful server in node.js, using Express as framework, and Winston, for the moment, as logger module.
This server will handle a big amount of simultaneous request, and it would be very useful to me to be able to track the log entries for each specific request, using something like a 'request ID'. The straight solution is just to add this ID as another piece of logging information each time I want to make a log entry, but it will mean to pass the 'request ID' to each method used by the server.
I would like to know if there is any node.js/javascript module or technique that would allow me to do this in an easier way, without carrying around the request ID for each specific request.
If you auto-increment, your later log analytics won't be able to uniquely identify requests, because different instances will generate colliding IDs, and restarting the app will automatically cause ID collisions.
Here's another possible solution.
Install cuid:
npm install --save cuid
Then in your main app file:
var cuid = require('cuid');
var requestId = function requestId(req, res, next) {
req.requestId = cuid();
next();
};
// Then, at the top of your middleware:
app.use(requestId);
Now you'll get a friendly request ID that is unlikely to collide, and you'll be able to uniquely identify your requests for your log analytics and debugging, even across multiple instances, and server restarts.
You can use req object that does comes with every request in express.
So the first route you would do in your application would be:
var logIdIterator = 0;
app.all('*', function(req, res, next) {
req.log = {
id: ++logIdIterator
}
return next();
});
And then anywhere within express, you can access that id in req object: req.log.id;
You will still need to pass some data into functions that do want to create some logs. In fact you might have logging function within req.log object, so that way it will be guaranteed that logging will happen only when there is access to req.log object.
I was struggling search for a solution for this problem.
The thing I didn't like it about solutions suggested here was that they imply to share the req object among all the functions along the project.
I found out a solution mixing your approach (creating an uuid per request) and with a library (continuation-local-storage) that allows sharing namespaces among modules.
You can find the explanation in this other answer: https://stackoverflow.com/a/47261545/5710581
If you want more info, I wrote down all these ideas and all the code in a post, in order to explain everything in one place:
Express.js: Logging info with global unique request ID – Node.js
You shouldn't be using Global Variables.
What I like to do is to populate a META object before each request.
I use a UUID generator (https://github.com/kelektiv/node-uuid) to ID a request
Here's an example
app.all('*', function(req, res, next) {
req.meta = {
ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress,
timestamp: uuid(),
user_agent: req.headers['user-agent'],
body: req.body,
}
return next();
})
As mentioned by #moka , Using the request ID in each request is the crux of solving the problem. Another way of abstracting all these is by making use of http-context and uuid
So set a UUID in the httpContext before all your middlewares (set as an application middleware and not as a router middlware). now you can get the uuid anywhere in your code and log it.
Here is a sample implementation I have used
You can get the complete reference here uuid in request
const uuid = require('node-uuid');
const httpContext = require('express-http-context');
....
this.expressApp.use(httpContext.middleware);
this.expressApp.use((req, res, next) => {
httpContext.set('reqId', uuid.v4());
next();
});
Now I have used the reqId set here in my custom pino logger'
public infoLogService (fileName): pino.Logger {
return pino({
level: 'info',
name: this.appService.getApp_name(),
messageKey: 'XXX-Logs',
base: {pid: process.pid, hostname: os.hostname,
timestamp: this.getTimeStamp(),
appName: this.appService.getApp_name(),
fileName: fileName,
request_id: **isNullOrUndefined(httpContext.get('reqId'))** ? 'Not an actual request ' : httpContext.get('reqId')
},
enabled: true,
useLevelLabels: true,
});
}
If the reqId is null it means that the loggers have been inserted in code that is used before starting the express App. Hope you can use this as an alternate solution