How do I send data from express => html - html

So say I have some form data in express right, and I'm checking it against a db. Say it doesn't find the db, so I have an if then statement for that. How would I edit a p element in a separate html file to say 'Account not found!' upon this if statement being activated? My code would be something like this..
let userData = keyv.get(username)
if (!userData) {
console.log('couldn't find user')
//edit p element with id status
}
else {
...
}
Thanks.

You need to use a templating engine and pass the userData variable when rendering the view.
You can do something like
app.get('/:id', function(req, res) {
// try to get user data
res.render('pages/index', {userData});
});
userData will be accessible from the view. I recommend you use EJS for this, although there are other engines like Pug, Handlebars, etc
EDIT:
I misunderstood your question, but the answer is the same except you render different data. If the user isn't found then you can render an error message instead.
app.get('/:id', function(req, res) {
// try to get user data. do the following if the user isn't found
res.render('pages/index', {
error: 'User not found'
});
});

Related

Add custom key in fetched JSON

I am making simple blog website in React and when I fetch posts I am getting user id, who posted it. with this id I am making another axios where I get user with given id and then I assign author key to my fetched posts JSON like this:
export const getPosts = async () => {
try {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
for (const item of data) {
let user = await getUser(item["userId"]);
item["author"] = user.username;
}
return data;
} catch (err) {
toast.error(err.message);
}
};
export const getUser = async (id) => {
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users/" + id
);
return response.data;
} catch (err) {
toast.error(err.message);
}
};
This method causes 5-10 seconds delay to display posts. I am searching for faster and simpler way to display username to every post.
U also can tell me if there is better way to fetch data and display it.
Thanks!
You have done it the exact right way if there is no other endpoint available to fetch multiple post author information at a time.
Well, this is meant to be an answer, but I'd start with a question.
Do you have access to the maintainer or developer handling the Restful API endpoint you are trying to get from?
If yes?
Tell them to simply include the author information on the response for fetching the post(s)
Or simply provide you with an endpoint that allows you to get author information for many posts at a time.
If No
Go over the documentation provided (if any) and see if there is any endpoint that allows you to fetch author information for multiple posts with a single request.
If none of the above option seems to be a way, kindly remember to block the UI When fetching the resources.
You can also fetch for few authors first and display while fetching for more in the background or on request for more by user, that way you would give a better user experience.
Happy hacking and coding 😜

How to get around previously declared json body-parser in Nodebb?

I am writing a private plugin for nodebb (open forum software). In the nodebb's webserver.js file there is a line that seems to be hogging all incoming json data.
app.use(bodyParser.json(jsonOpts));
I am trying to convert all incoming json data for one of my end-points into raw data. However the challenge is I cannot remove or modify the line above.
The following code works ONLY if I temporarily remove the line above.
var rawBodySaver = function (req, res, buf, encoding) {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || 'utf8');
}
}
app.use(bodyParser.json({ verify: rawBodySaver }));
However as soon as I put the app.use(bodyParser.json(jsonOpts)); middleware back into the webserver.js file it stops working. So it seems like body-parser only processes the first parser that matches the incoming data type and then skips all the rest?
How can I get around that? I could not find any information in their official documentation.
Any help is greatly appreciated.
** Update **
The problem I am trying to solve is to correctly handle an incoming stripe webhook event. In the official stripe documentation they suggested I do the following:
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}),
(request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig,
endpointSecret);
} catch (err) {
return response.status(400).send(Webhook Error:
${err.message});
}
Both methods, the original at the top of this post and the official stripe recommended way, construct the stripe event correctly but only if I remove the middleware in webserver. So my understanding now is that you cannot have multiple middleware to handle the same incoming data. I don't have much wiggle room when it comes to the first middleware except for being able to modify the argument (jsonOpts) that is being passed to it and comes from a .json file. I tried adding a verify field but I couldn't figure out how to add a function as its value. I hope this makes sense and sorry for not stating what problem I am trying to solve initially.
The only solution I can find without modifying the NodeBB code is to insert your middleware in a convenient hook (that will be later than you want) and then hack into the layer list in the app router to move that middleware earlier in the app layer list to get it in front of the things you want to be in front of.
This is a hack so if Express changes their internal implementation at some future time, then this could break. But, if they ever changed this part of the implementation, it would likely only be in a major revision (as in Express 4 ==> Express 5) and you could just adapt the code to fit the new scheme or perhaps NodeBB will have given you an appropriate hook by then.
The basic concept is as follows:
Get the router you need to modify. It appears it's the app router you want for NodeBB.
Insert your middleware/route as you normally would to allow Express to do all the normal setup for your middleware/route and insert it in the internal Layer list in the app router.
Then, reach into the list, take it off the end of the list (where it was just added) and insert it earlier in the list.
Figure out where to put it earlier in the list. You probably don't want it at the very start of the list because that would put it after some helpful system middleware that makes things like query parameter parsing work. So, the code looks for the first middleware that has a name we don't recognize from the built-in names we know and insert it right after that.
Here's the code for a function to insert your middleware.
function getAppRouter(app) {
// History:
// Express 4.x throws when accessing app.router and the router is on app._router
// But, the router is lazy initialized with app.lazyrouter()
// Express 5.x again supports app.router
// And, it handles the lazy construction of the router for you
let router;
try {
router = app.router; // Works for Express 5.x, Express 4.x will throw when accessing
} catch(e) {}
if (!router) {
// Express 4.x
if (typeof app.lazyrouter === "function") {
// make sure router has been created
app.lazyrouter();
}
router = app._router;
}
if (!router) {
throw new Error("Couldn't find app router");
}
return router;
}
// insert a method on the app router near the front of the list
function insertAppMethod(app, method, path, fn) {
let router = getAppRouter(app);
let stack = router.stack;
// allow function to be called with no path
// as insertAppMethod(app, metod, fn);
if (typeof path === "function") {
fn = path;
path = null;
}
// add the handler to the end of the list
if (path) {
app[method](path, fn);
} else {
app[method](fn);
}
// now remove it from the stack
let layerObj = stack.pop();
// now insert it near the front of the stack,
// but after a couple pre-built middleware's installed by Express itself
let skips = new Set(["query", "expressInit"]);
for (let i = 0; i < stack.length; i++) {
if (!skips.has(stack[i].name)) {
// insert it here before this item
stack.splice(i, 0, layerObj);
break;
}
}
}
You would then use this to insert your method like this from any NodeBB hook that provides you the app object sometime during startup. It will create your /webhook route handler and then insert it earlier in the layer list (before the other body-parser middleware).
let rawMiddleware = bodyParser.raw({type: 'application/json'});
insertAppMethod(app, 'post', '/webhook', (request, response, next) => {
rawMiddleware(request, response, (err) => {
if (err) {
next(err);
return;
}
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
// you need to either call next() or send a response here
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
});
});
The bodyParser.json() middleware does the following:
Check the response type of an incoming request to see if it is application/json.
If it is that type, then read the body from the incoming stream to get all the data from the stream.
When it has all the data from the stream, parse it as JSON and put the result into req.body so follow-on request handlers can access the already-read and already-parsed data there.
Because it reads the data from the stream, there is no longer any more data in the stream. Unless it saves the raw data somewhere (I haven't looked to see if it does), then the original RAW data is gone - it's been read from the stream already. This is why you can't have multiple different middleware all trying to process the same request body. Whichever one goes first reads the data from the incoming stream and then the original data is no longer there in the stream.
To help you find a solution, we need to know what end-problem you're really trying to solve? You will not be able to have two middlewares both looking for the same content-type and both reading the request body. You could replace bodyParser.json() that does both what it does now and does something else for your purpose in the same middleware, but not in separate middleware.

How to put the results of MySQL database in a variable in Node.js?

I have a problem in my node.js application. I'm connecting to the database and getting the data
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
});
console.log(users[currentUser]);
When I try to console.log the result[0] from inside the function, it returns this:
RowDataPacket {
n: 11,
id: 'VKhDKmXF1s',
name: 'user3',
status: 'online',
socketID: 'JbZLNjKQK15ZkzTXAAAB',
level: 0,
xp: 0,
reg_date: 2018-07-16T20:37:45.000Z }
I want to put that result from MySQL into users.example or something like that, but when I try the code it returns undefined. So I tried console.log(users[currentUser].id) as an example and it shows an error
TypeError: Cannot read property 'id' of undefined
So how do I put the data from the result inside my variable users[currentUser]?
So how do I put the data from the result inside my variable users[currentUser]?
That's happening. The problem is how it is being tested.
The correct test is with the console.log inside the callback function(err, result)
function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Issue: users[currentUser] is still undefined outside that function
Well, yes and no. It is undefined in code that executes before the callback is fired.
And how do I fix that?
Anything that needs the result of the query must be executed from within the callback function, because that's the only code location where you know for certain that the data exists.
Well, I need that data outside in a global variable.
You can stick the query data in a global, but that doesn't solve the timing issue
of only accessing that global when it is defined and contains current data. That will cause lots of frustration.
If you don't want to call one or more specific functions to process the query data
within the callback, an alternative is to use a nodejs EventEmitter to coordinate the data production and data consumption.
Yet another alternative is to not use a callback function, and use Promise and/or async/await, both of which are supported by modern nodejs. This alternative doesn't involve global variables, but provides different ways to code the fact that some operations need to wait for the results of others.
connection.query is an async call. The console.log is called before the query fetches the data from db.
Because you're using a callback, your console.log is happening before the result comes back from the database. Additionally, you have a type in your code user -> users.
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Side note: research "SQL Injection" before you use this code. The way you're building your query opens you up for anyone to access your database. Using a library like squel.js or knex.js will help you avoid this.
For an explanation of why things happen in the order they do, take a look at the JavaScript event loop.

Customize Loopback response after save

I have a loopback 2.x app, in which I have a model Conversation and a model Message, with a relationship "Conversation has many messages". I want to customize the response for POST conversations/:id/messages with a json response different than the default, say {status: 'success'}. I tried to use remote hook for the method __create__messages, but it did not work:
Conversation.afterRemote('__create__messages', function(ctx, next) {
ctx.result.data = {
success: 'yes'
};
next();
});
This still returns the default response. How can I return a custom json for a remote method? I have seen examples only for all models, or for all methods: multiple models, multiple methods
Maybe you can try a version of following code below. Also, I think you are meaning to to manipulate data before the method finishes, not after. If you wait, the response will already be created, preventing your intended goal. Let me know if this works (replace with methods that will work for your use case).
Conversation.observe('before save', function(context, next) {
var instance = context.instance || context.data;
if (!instance) return next();
// Your code here
next();
});

Run multiple functions based on a SINGLE form submission (method="post") using Node-express

I am looking to perform multiple actions upon receiving HTML(or EJS) form content using the POST method. I am using Node express, mongoose & mongoDB. Each of the below POST responses work individually but i am unsure how to proceed in updating multiple databases based on ONE SINGLE form submission.
// insert into passport db
app.post('/signup', passport.authenticate('local-signup',
{
successRedirect : '/index', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
//insert into my database here
[the content of in the second function is unimportant as that is working fine and has been stripped down for simplification.]
app.post('/signup', function( req, res )
{
new UserDB(
{
user_id : req.body.content,
first_name : req.body.fname,
}).save( function( err, mySite, count )
{
res.redirect( '/index' );
});
});
I have tried redirecting but the form content is not accessible after the redirect so only the first function stores the data (ie. only 1 database is filled).
How would i run both functions within
app.post('/signup',.....
{
...
});
?
Thanks in advance!
You can do this by making one function the callback of the other. This is easy because each function maintains the same Connect middleware signature, function(req, res, next), where req and res are the request and response objects created and manipulated by the application, and next is the next function to call at the end of the current function's execution.
According to the official documentation, passport.authenticate() is a normal piece of middleware. All you need to do is specify the middleware you want to be called next. Express queues middleware functions in the order in which you pass them into app.post. You can do something like this:
app.post('/signup', passport.authenticate('local-signup', {
failureRedirect : '/signup',
failureFlash : true
}),
function(req, res) {
new UserDB({
user_id : req.body.content,
first_name : req.body.fname,
}).save(function(err, mySite, count) {
res.redirect('/index');
});
});
Middleware is an extremely powerful feature of the Express framework and possibly the single most important one to master. This guide would be a great next step if you want to learn more.