Send a string from a express post route back to the client - javascript

I am trying to send back a string, once computed, as it happens back to the client from a post route on express
On the back end I have a post route:
router.post('/', async (req, res) => {
try {
// Here I do some computation on a succesion that generates a new string everytime
const foo = 'string that changes and I want to send to client'
} catch (error) {
res.status(500).send(error)
}
})
Then on the client I am using axios to send data to post
(async () => {
try {
await axios.post('/api/send/', { data })
} catch (error) {
console.log(error)
}
})()
Is there a way I could, after sending a post request, get data back from that route once it happens?
I tried to use res.send() from the post route but it just makes the functions on the back end fail.
Many thanks.

router.post('/', (req, res) => {
try {
// Here I do some computation on a succesion that generates a new string everytime
const foo = 'string that changes and I want to send to client'
res.send(foo);
} catch (error) {
res.status(500).send(error)
}
})
This should suffice. If not, you need to detail what your error message is.

Related

How to send data from NodeJS server side to the JS client side, only when data is ready?

On my website, when the user clicks on a button, some user's data will be stored in a database and after that I want the server to send notification data to the Javascript frontend file to change the UI.
Right now, the Js file (index.js) receives data right after the website loads (always false). I want it to be received only when the data is ready on the server.
I searched a lot but couldn't find an answer to my problem?
I appreciate any help :)
server.js
var requestValidation = false;
app.post("/", function(req, res){
var name = req.body.personName;
var email = req.body.personEmail;
var collabTopic = req.body.collabTopic;
const newUser = new User({ //mongoDB schema
name: name,
email: email,
collabTopic: collabTopic
});
newUser.save(function(err){ //adding data to mongoDB
if(!err){
requestValidation = true;
}
});
});
app.get("/succ", function(req, res){
res.json(requestValidation);
});
index.js
const url = "http://localhost:3000/succ";
const getData = async (url) => {
try {
const response = await fetch(url);
const json = await response.json();
console.log(json);
} catch (error) {
console.log(error);
}
};
getData(url);
I'm not sure this is completely the answer you're looking for, but it's definitely a tool/feature to consider as you rework your approach.
app.post("/", async (req, res) => {
let result = await INSERT MONGODB UPDATE OR INSERT FUNCTION;
res.render("YOUR TEMPLATE", result);
});
You probably can't plug and play this, but when you finish a MongoDB operation, it returns a json object with some details on whether or not there was success. For example, a MongoDB insert operation returns something like this (stored in the variable result that I created)
{ "acknowledged" : true, "insertedId" : ObjectId("5fd989674e6b9ceb8665c57d") }
and then you can pass this value on as you wish.
Edit: This is what tkausl referred to in a comment.
Here is an example if you want to pass the content of a txt file to the client with express and jquery:
in express:
app.get('/get', (req, res) => {
fs.readFile('test.txt', (err, data) => {
if (err) throw err;
return res.json(JSON.parse(data));
})
})
jquery in client side:
$.getJSON( "http://localhost:3000/get", function( data ) {
geojsondata1 = JSON.stringify(data)
}
now you can do anything you want with the variable data

How to attach user credentials to the request pipeline in expressjs?

I am trying to write a middleware that extracts the user model and attach it to the request pipeline.
I have already written a token extractor middleware and managed to attach the token to the request pipeline, but for some reason when I try to extract the user model, it works fine inside the middleware function yet inside my controller it returns as undefined.
Here's what I have tried:
utils/middleware.js
const tokenExtractor = async (request, response, next) => {
const authorization = await request.get('authorization');
if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
request.token = authorization.substring(7);
} else{
request.token = null;
}
next();
};
const userExtractor = async (request, response, next) => {
tokenExtractor(request, response, next);
if(request.token){
const decodedToken = jwt.verify(request.token, process.env.SECRET);
request.user = await User.findById(decodedToken.id);
console.log(request.user); // Works
next();
} else{
response.status(403).json({ error: 'no token received' });
}
};
Inside my controllers it breaks down:
controllers/blogs.js
blogRouter.post("/", async (request, response, next) => {
if (request.body.title && request.body.url) {
const token = request.token;
if (!token) {
return response.status(401).json({ error: 'invalid token' });
}
console.log(request.user); // undefined !
if(!request.user){
return response.status(401).json({ error: 'invalid user' });
}
const user = request.user;
const blog = new Blog({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes,
user: user._id,
});
await blog.save();
user.blogs = user.blogs.concat(blog._id);
await user.save();
response.status(201).json(blog);
}
response.status(400).end();
});
Both middleware are already attached to the express app.
EDIT:
I have fixed the issue by removing the call to tokenExtractor from userExtractor function, and then chaining the middleware to the router instead of calling them before everything.
I was using the tokenExtractor globaly, while the userExtractor locally to the blogsRouter. What was happening was that while the tokenExtractor was working fine, the blogRouters was being called before the userExtractor ever get called, hence why I was getting undefined.
app.js
// app.use(tokenExtractor);
app.use(requestLogger);
app.use(errorHandler);
// app.use(userExtractor);
app.use('/api/login', tokenExtractor, loginRouter);
app.use('/api/users', usersRouter);
app.use('/api/blogs', tokenExtractor, userExtractor, blogRouter); // chaining the extractors
It makes sense, let next() carry the (req, res, next) instances forward, as a pipe. No hacks are needed and you can stack as many middlewares as needed and even reuse values from one inside the other - if you can trust the order of the call stack.
You don't need to chain it. The callback argument for the next middleware function only needs to be specified as follows.
const tokenExtractor = (request, response, next) => {
const auth = request.get('authorization')
if (auth && auth.toLowerCase().startsWith('bearer ')) {
request.token = auth.substring(7)
next()
} else {
next()
}
}

express error handler not working with the given http status code and custom message

When I am entering localhost://5000/products/
Its showing me all the json objects
But when I am entering localhost://5000/products/(some random id's) It must show me the 404 error with my custom message but its not showing me that.
This is my code:
import express from 'express'
import asyncHandler from 'express-async-handler'
const router = express.Router()
import Product from '../models/productModel.js'
// #Desc Fetch all products
// #Desc GET /api/products
// #access Public
router.get(
'/',
asyncHandler(async (req, res) =>{
const products = await Product.find({})
res.json(products)
}))
// #Desc Fetch single products
// #Desc GET /api/products:id
// #access Public
router.get(
'/:id',
asyncHandler(async (req, res) =>{
const product = await Product.findById(req.params.id)
if(product) {
res.json(product)
} else {
res.status(404).json({ message: 'Product not found' })
}
}))
export default router
Your question is somewhat lacking in details, but I do believe I know what's happening. Your idea is that if you pass a non-existent id to the route and mongo does not find it, it will return a 404 - Not Found. Solid logic.
The reason you're not seeing that 404 is, most likely, because you are calling the route with something like /products/dflhdshfd. The problem with that is that findById only accepts strings that can be cast to an ObjectId, which, of course dflhdshfd cannot be. Hence when you call your route with /products/dflhdshfd, your app bombs and throws this error:
CastError: Cast to ObjectId failed for value "dflhdshfd" at path "_id" for model "product"
And your app does not return anything because you don't handle that error.
As a test, try calling the route like this: /products/601977b2d2e3cf756b85bc61 and you will see your 404, because 601977b2d2e3cf756b85bc61 can be cast to a valid ObjectId.
But more generally, you need to catch errors coming from Product.findById(req.params.id) by doing something like this:
const product = await Product.findById(req.params.id).catch(e => false);
if(product) {
res.json(product)
} else {
res.status(404).json({ message: 'Product not found' })
}
This way when your findById throws an error, it will return false to the product variable and your app will return a 404. (This is not a great way to handle errors and is simply meant to illustrate the point)
Added
Properly handling errors can be a daunting task in a large application, so I will address your specific case only while encouraging you to go over this article for a more complete picture.
The way I would handle this specific error is I would first create a small function to route all the caught errors to. Something simple like this:
const errorHandler = (err, res) => { //res is the response object
console.log(err);
res.status(400).json({msg: "Error has occurred"})
}
Then I would rewrite your code like this:
router.get(
'/:id',
(req, res) => {
Product.findById(req.params.id)
.then(product => {
if(product) {
res.json(product)
} else {
res.status(404).json({message: 'Product not found' })
}
})
.catch(e => errorHandler(e, res))
})
Whenever findById throws an error, it simply sends it over to errorHandler to deal with, while errorHandler dumps it to the console for you to troubleshoot and also send a response back to the user.
And that can be the template you adopt for ALL your queries (don't forget to always catch db query errors). Later you can, for example, add a logging feature to errorHandler that dumps errors into a file for you to later look at.
So this would be the basic approach I would take here.

How do I do a GET for Firebase.functions().httpsCallable?

How do I do a GET for Firebase.functions().httpsCallable?
I keep receiving a POST error 404 but this is a GET request to my server. Should I pass in nothing or there is something to change this httpsCallable to get function?
Client
let updateWorkshop = Firebase.functions().httpsCallable('api/update/workshop');
updateWorkshop({/* nothing */})
.then(res => {
console.log(res);
}, err => {
console.log(err);
})
Server
app.get('/v3/update/workshop', asyncMiddleware( async (req, res, next) => {
let results = await UPDATE_WORKSHOP_DATE.Run()
res.status(200).json({results: results})
}))
exports.api = FUNCTIONS.https.onRequest(app);
If you are just trying to ping your callable function endpoint, a GET won't work. As you can see from the protocol specification for callable functions, it uses a POST. If you use a GET, it's an error because you're not following the protocol.

Using chai to mock http requests

I'm testing a nodejs app written using express. For the unit testing I'm using chai and sinon. I have the following route in my API that I would like to test.
In my test, I'm simulating the get request with the following code:
chai.request(app)
.get('/downloads')
.send({ wmauth: {
_identity: {
cn: "username",
}
} })
.end((err, res) => {
res.status.should.be.equal(200);
res.body.should.be.a('object');
res.body.should.have.property('Items', []);
AWS.restore('DynamoDB.DocumentClient');
done();
However, I'm always getting the error "Cannot read property '_identity' of undefined". Because the object "wmauth" is not sent in the request, so it is undefined. I have tried to use the send method to try to include it in the request, but no luck. I guess I need to mock it somehow and send it into the request but have no idea how to do it. Could someone help me with this?
Below the method to test:
app.get('/downloads', async (req, res) => {
const created_by_cn = req.wmauth['_identity'].cn;
if(!created_by_cn) {
return res.status(400).json({
error: 'Mandatory parameters: created_by_cn',
});
}
try {
const data = await downloadService.getDownloads(created_by_cn);
return res.status(200).json(data);
}
catch(error){
res.status(500).json({error: error.message});
}
});
THanks
I guess you forgot to use req.body as in:
const created_by_cn = req.body.wmauth['_identity'].cn;
Hope can solve your issue
Since chai-http use superagent, so according to its doc, you need to use query() in order to pass query parameter in get request:
chai.request(app)
.get('/downloads')
.query({ wmauth: {_identity: {cn: "username"}}})
.end((err, res) => { ... });
Then in the express route you can find the parameters in req.query:
app.get('/downloads', function (req, res) {
const created_by_cn = req.query.wmauth._identity.cn;
...
})

Categories