Scope of express response - javascript

I know this is a scope issue, but am not quite sure what I'm missing here.
I have a simple endpoint where, if a user is found I return a simple 'no user found message. Here is the function:
export const getOneUser = (req, res) => {
const { id } = req.params;
const foundUser = users.find((user) => user.id == id)
checkUserExists(foundUser)
res.send(foundUser)
}
I've replace my standard guard with a helper function. so instead of:
if(!userfound) res.send('No user was found')
with the following function:
const checkUserExists = (x) => {
if(!x) res.send('No user found')
}
Since I am constantly using this guard I thought this might be a helpful function to write.
The problem is I'm getting the following error, no matter what I do, event if I import { respons } from 'express' and use that:
ReferenceError: res is not defined
at checkUserExists (file:///Users/cnak/Desktop/PROJECTS/TEST3/controllers/users.js:7:12)
at getOneUser (file:///Users/cnak/Desktop/PROJECTS/TEST3/controllers/users.js:18:5)
at Layer.handle [as handle_request] (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/layer.js:95:5)
at /Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/index.js:281:22
at param (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/index.js:360:14)
at param (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/index.js:371:14)
at Function.process_params (/Users/cnak/Desktop/PROJECTS/TEST3/node_modules/express/lib/router/index.js:416:3)
How do I create a helper function that can actually pass a response?
I've also tried to return the 'res.send('No user found')
Thanks

Because you are not defining res in your checkUserExists function.
const checkUserExists = (x, res) => {
if(!x) res.send('No user found')
}
and
...
checkUserExists(foundUser, res);
...

If you are still getting error after sending res in checkUserExists method. Then problem is not here. You need to check your route, that you are passing actual http res instance here or not. Can you please paste your code of index.js and router file.

Related

Error [object object] to JSON: Converting circular structure to JSON [duplicate]

I am using request package for node.js
Code :
var formData = ({first_name:firstname,last_name:lastname,user_name:username, email:email,password:password});
request.post({url:'http://localhost:8081/register', JSON: formData}, function(err, connection, body) {
exports.Register = function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
console.log("Request data " +JSON.stringify(req));
Here I am getting this error :
TypeError: Converting circular structure to JSON
Can anybody tell me what is the problem
JSON doesn't accept circular objects - objects which reference themselves. JSON.stringify() will throw an error if it comes across one of these.
The request (req) object is circular by nature - Node does that.
In this case, because you just need to log it to the console, you can use the console's native stringifying and avoid using JSON:
console.log("Request data:");
console.log(req);
I also ran into this issue. It was because I forgot to await for a promise.
Try using this npm package. This helped me decoding the res structure from my node while using passport-azure-ad for integrating login using Microsoft account
https://www.npmjs.com/package/circular-json
You can stringify your circular structure by doing:
const str = CircularJSON.stringify(obj);
then you can convert it onto JSON using JSON parser
JSON.parse(str)
I was able to get the values using this method, found at careerkarma.com
Output looks like this.
I just run this code in the debugger console. Pass your object to this function.
Copy paste the function also.
const replacerFunc = () => {
const visited = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (visited.has(value)) {
return;
}
visited.add(value);
}
return value;
};
};
JSON.stringify(circObj, replacerFunc());
I forgotten to use await keyword in async function.
with the given systax
blogRouter.put('/:id', async (request, response) => {
const updatedBlog = Blog.findByIdAndUpdate(
request.params.id,
request.body,
{ new: true }
);
response.status(201).json(updatedBlog);
});
Blog.findByIdAndUpdate should be used with the await keyword.
use this https://www.npmjs.com/package/json-stringify-safe
var stringify = require('json-stringify-safe');
var circularObj = {};
circularObj.circularRef = circularObj;
circularObj.list = [ circularObj, circularObj ];
console.log(stringify(circularObj, null, 2));
stringify(obj, serializer, indent, decycler)
It's because you don't an async response For example:
app.get(`${api}/users`, async (req, res) => {
const users = await User.find()
res.send(users);
})
This is because JavaScript structures that include circular references can't be serialized with a"plain" JSON.stringify.
https://www.npmjs.com/package/circular-json mentioned by #Dinesh is a good solution. But this npm package has been deprecated.
So use https://www.npmjs.com/package/flatted npm package directly from the creator of CircularJSON.
Simple usage. In your case, code as follows
import package
// ESM
import {parse, stringify} from 'flatted';
// CJS
const {parse, stringify} = require('flatted');
and
console.log("Request data " + stringify(req));
If you are sending reponse , Just use await before response
await res.json({data: req.data});
Came across this issue in my Node Api call when I missed to use await keyword in a async method in front of call returning Promise. I solved it by adding await keyword.
I was also getting the same error, in my case it was just because of not using await with Users.findById() which returns promise, so response.status().send()/response.send() was getting called before promise is settled (fulfilled or rejected)
Code Snippet
app.get(`${ROUTES.USERS}/:id`, async (request, response) => {
const _id = request.params.id;
try {
// was getting error when not used await
const user = await User.findById(_id);
if (!user) {
response.status(HTTP_STATUS_CODES.NOT_FOUND).send('no user found');
} else {
response.send(user);
}
} catch (e) {
response
.status(HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR)
.send('Something went wrong, try again after some time.');
}
});
For mongodb
so if you are getting errors while fetching data from MongoDB then the problem is async
previously
app.get('/users',(req,res,next)=>{
const user=chatUser.find({});
if(!user){
res.status(404).send({message:"there are no users"});
}
if(user){
res.json(user);
}
})
After
app.get('/users',async(req,res,next)=>{
const user=await chatUser.find({});
if(!user){
res.status(404).send({message:"there are no users"});
}
if(user){
res.json(user);
}
})
I came across this issue when not using async/await on a asynchronous function (api call). Hence adding them / using the promise handlers properly cleared the error.
I had a similar issue:-
const SampleFunction = async (resp,action) => {
try{
if(resp?.length > 0) {
let tempPolicy = JSON.parse(JSON.stringify(resp[0]));
do something
}
}catch(error){
console.error("consoleLogs.Utilities.XXX.YYY", error);
throw error;
}
.
.
I put await before JSON.parse(JSON.stringify(resp[0])).
This was required in my case as otherwise object was read only.
Both Object.create(resp[0]) and {...resp[0]} didn't suffice my need.
If an object has a different type of property like mentioned in the above image, JSON.stringify() will through an error.
Try this as well
console.log(JSON.parse(JSON.stringify(req.body)));
TypeError: Converting circular structure to JSON in nodejs:
This error can be seen on Arangodb when using it with Node.js, because storage is missing in your database. If the archive is created under your database, check in the Aurangobi web interface.

Passing parameters to Express middleware not working

I'm trying to create an input validation middleware using Express. My goal is to be able to pass 2 parameters to the middleware that validates client input. The problem is, after following multiple resources (including Express docs), my middleware seems to not be working.
// input validator middleware
export const validateInput = (schema: joi.ObjectSchema) => {
console.log('first console log');
return (req: Request, res: Response, next: NextFunction) => {
console.log('second console log');
const { error } = schema.validate(req.body);
if (error) {
const errors = error.details.map((err) => err.message);
next(new InvalidInput(errors));
}
next();
};
};
// middleware call
const commentSchema = joi
.object({
content: joi.string().alphanum().min(3).required(),
})
.options({ abortEarly: false });
export const validateCommentInput = () => {
validateInput(commentSchema);
};
After calling the middleware, I get to the "first console log", but never to the second, and my API just hangs there until I force stop. My solution otherwise would be to just pass req and next as parameters to a function validateInput(req, next, commentSchema);, but I'm not sure that's the proper way to do it. I also tried the async version with the same results.
Any help is greatly appreciated.
Your validateCommentInput function isn't returning the inner function.
The lack of curly braces in a lambda implies a return statement. However, using curly braces means you have to specify return.
So change this:
export const validateCommentInput = () => {
validateInput(commentSchema);
};
to this:
export const validateCommentInput = () => validateInput(commentSchema);

How to test a function which consumes a callback? Or how to defer the assertion?

I use Jest to test a function which generates a JSON Web Token. It seems that I can't assert the value since when I assert, the callback hasn't been executed yet.
const issueJWT = function issueJWT(req, res, next) {
jwt.sign(signUser, function (err, token) {
if (err) {
next(err);
return;
}
res.locals.token = token;
next();
});
};
This is my test, I mock the request and response, then assert the result:
test('Should return a JWT with proper value if nothing wrong happened', () => {
issueJWT(request, response, mockNext);
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
});
The error is:
TypeError: Cannot read property 'payload' of null
How to make it work?
According to my knowledge, I think the callback is at the task queue which
means it will be executed when nothing is in the event loop, right? I wanna find a way to defer my assertion, but don't know how...
Thanks for the tips, I use the done, now the test could pass, but the problem is, whenever there is a problem, the error message doesn't make any sense... Any problem to my solution?
test('Should return a JWT with proper value if nothing wrong happened', (done) => {
const callback = () => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
expect(tokenPayload).toHaveProperty('iss');
done();
};
issueJWT(request, response, callback);
});
The error is now:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Ok, so with some help from #felixKling getting me to actually read the docs, you need to do something like this:
test('Should return a JWT with proper value if nothing wrong happened', done => {
issueJWT(request, response, (e) => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
done();
});
});
I'm not on my dev box so I can't test this, but basically the idea is that you use the 'done' parameter to the test callback to signal that the test is waiting on async code. The test framework will basically wait for your test to call that callback before exiting.
In this case, your next() call from issueJWT is what we're waiting on firing before checking to see if the various objects were updated. If you were not using next() in your middleware, you'd likely need to mock whatever response method you're calling instead (e.g. response.end()) to do your tests.

Cloud Functions for Firebase - .once('value') returning nothing

I just started using Cloud Functions for Firebase. I'm stuck on retrieving data from the database.
I'm using .once('value') on a Firebase reference to convert it into a promise, but every time I run the function with Postman I get this error:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:357:11)
at ServerResponse.header (/usr/lib/node_modules/firebase-tools/node_modules/express/lib/response.js:725:10)
at ServerResponse.send (/usr/lib/node_modules/firebase-tools/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/usr/lib/node_modules/firebase-tools/node_modules/express/lib/response.js:256:15)
at Timeout.req.functionTimeout.setTimeout [as _onTimeout] (/usr/lib/node_modules/firebase-tools/node_modules/#google-cloud/functions-emulator/src/supervisor/supervisor.js:192:14)
at ontimeout (timers.js:380:14)
at tryOnTimeout (timers.js:244:5)
at Timer.listOnTimeout (timers.js:214:5)
Here is the first function which calls for the function to get that data:
exports.userPlaylistStatus = functions.https.onRequest(async (request, response) => {
const userId = request.body.userId;
// Wait to get the user reference
const userRef = await buildUserRef(userId);
response.send(userRef);
});
Here is the function I'm using .once('value') in. The promise is never returning a value. Even when I put a console.log('hi') inside the .then(), it is never logged.
async function buildUserRef(_userId: string) {
return await admin.database().ref(`/users/${_userId}`).once('value').then(
// this would return the user reference but I have it as .val() for now
res => {return res.val();},
err => {console.log(err)}
);
}

Automatically logging in a user with KoaJS and PassportJS

I'm trying to automatically log in a user with PassportJS.
This is my current code:
myRouter.get('/signin', function* (next) {
user = {...};
var res = this.res; // needed for the function below
this.req.login(user, function(err) {
if (err)
console.log('error logging in user - '+err);
return res.redirect('/'); // <--- line 439
});
});
But when I run it, I get the error:
error logging in user - TypeError: undefined is not a function
TypeError: undefined is not a function
at /srv/www/domain.com/app.js:439:32
at /srv/www/domain.com/node_modules/koa-passport/node_modules/passport/lib/http/request.js:49:48
at pass (/srv/www/domain.com/node_modules/koa-passport/node_modules/passport/lib/authenticator.js:293:14)
at Authenticator.serializeUser (/srv/www/domain.com/node_modules/koa-passport/node_modules/passport/lib/authenticator.js:295:5)
at Object.req.login.req.logIn (/srv/www/domain.com/node_modules/koa-passport/node_modules/passport/lib/http/request.js:48:29)
at Object.<anonymous> (/srv/www/domain.com/app.js:434:26)
at GeneratorFunctionPrototype.next (native)
at Object.dispatch (/srv/www/domain.com/node_modules/koa-router/lib/router.js:317:14)
at GeneratorFunctionPrototype.next (native)
at Object.<anonymous> (/srv/www/domain.com/node_modules/koa-common/node_modules/koa-mount/index.js:56:23)
A quick semi-derp moment and I realize to redirect in koa it does not use res but this, you must do the following:
var res = this; // needed for the next function
this.req.login(user, function(err) {
if (err)
console.log('error logging in user - '+err);
return res.redirect('/');
});
Your code is fine, it is just that res is called response, so just change
var res = this.res; in var res = this.response; and it will work fine. res does exist, but it is the Node http module response, not the Koa Response object, and therefore does not have any redirect method. The redirect is aliased to this, which is why you can use this.redirect, but it really is a Response method.
Take a look at http://koajs.com/#context for more details.
To avoid having to assign this, or response, you could just bind this to your function, I think it is cleaner in most cases:
myRouter.get('/signin', function* (next) {
user = {...};
this.req.login(user, function(err) {
if (err)
console.log('error logging in user - '+err);
return this.redirect('/'); // <--- line 439
}.bind(this));
});

Categories