Unhandled Promise Rejection Error in node js - javascript

am getting error while executing combined queries
Am using, sequelize and mysql
Here is my code:
const makeAuthenticate = async (req, res) => {
const newOtp = Math.floor(100000 + Math.random() * 900000);
try {
const {phone} = req.body;
const [user, created] = await db.User.findOrCreate({
where: { phone: phone },
})
.then(e => {
db.User.update({ otp: newOtp }, {
where: {
phone: phone
}})
.then(f => res.status(200).json({
otp: newOtp
}))
.catch(err => res.status(500).json({error: err})) // Error comes from here !
})
.catch(err => res.status(500).json({error: err}))
} catch (error) {
return res.status(500).json({error: error.message})
}
}
And here is what am doing with the code:
Basically, am doing both signup and signin in single query, in other words at first using findOrCreate - i find the user or i will create a new user, and then i need to send one time password (which is temp 6 digit numerics") to the phone number which they use on the process.
So, am updating the otp to my users table and then i need to add up the call to send SMS to the user ( i havent done that yet ) so as of now am at the stage of findOrCreate and make another update call to the specific user.
By this am getting an error as follows:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Try something like this
const makeAuthenticate = async (req, res) => {
const newOtp = Math.floor(100000 + Math.random() * 900000)
const {phone} = req.body
try {
const [user, created] = await db.User.findOrCreate({
where: { phone: phone }
})
const updatedRes = await db.User.update({ otp: newOtp }, { where: { phone: phone }})
res.status(200).json({
otp: newOtp
})
} catch(e) {
res.status(500).json({error: err})
}
}
There is no need to nest promises and .then() you can just use await since Sequelize uses promises.
The main issue with your code is that you use .catch() inside try catchand they both are trying to send a status on failure. So most likely your issue is somewhere else but this was a side effect, when you run my code you should see your real issue if it exist.

Related

Nestjs Testing in signup BadRequestException: email in use error

user.service.ts
async findWithMail(email:string):Promise<any> {
return this.userRepository.findOne({email});
}
auth.service.ts
async signup(email:string,password:string,name?:string,surname?:string,phone:string){
if(email) {
const users = await this.userService.findWithMail(email);
if(users) {
throw new BadRequestException('email in use');
}
}
if(!password) return {error:"password must be!"};
const salt = randomBytes(8).toString('hex');
const hash = (await scrypt(password,salt,32)) as Buffer;
const result = salt + '.' +hash.toString('hex');
password = result;
const user = await
this.userService.create(email,password,name,surname,phone);
return user;
}
auth.service.spec.ts
let service:AuthService;
let fakeUsersService: Partial<UserService>;
describe('Auth Service',()=>{
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) => Promise.resolve({email,password} as User),
}
const module = await Test.createTestingModule({
providers:[AuthService,{
provide:UserService,
useValue:fakeUsersService
}],
}).compile();
service = module.get(AuthService);
});
it('can create an instance of auth service',async()=> {
expect(service).toBeDefined();
})
it('throws an error if user signs up with email that is in use', async () => {
await service.signup('asdf#asdf.com', 'asdf')
});
})
When ı try to run my test its give me error even this email is not in database its give error: BadRequestException: email in use. I couldnt figure out how to solve problem
You can use isExists method instead of findOne.
Also you can add extra check for your findWithMail method. Check the length of db request result. Like if (dbReqResult.length === 0) return false; else true
please put your attention on your mocked user service, especially on findWithEmail function, this part
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) =>
Promise.resolve({email,password} as User),
}
...
try to resolve the promise to be null not [] (empty array) or change your if(users) on your auth.service to be if(users.length > 0), why? it because empty array means to be thruthy value so when run through this process on your auth.service
if(email) {
const users = await this.userService.findWithMail(email);
// on this part below
if(users) {
throw new BadRequestException('email in use');
}
}
the 'users' executed to be truthy value so it will invoke the error. I hope my explanation will help you, thank you

MongooseError: Query was already executed:

I'm trying to update the document but the error says the query has already been executed.
MongooseError: Query was already executed: footballs.updateOne({ date: 'January 4' }, {})
app.post('/api/bookslot', async (req, res) => {
console.log(req.body);
try {
const token = req.headers['x-access-token'];
const decoded = jwt.verify(token, 'secret123');
const email = decoded.email;
const user = await UserModel.findOne({ email: email });
let sportname = req.body.selectedSport.toLowerCase();
const time = req.body.slotTime;
const seats = req.body.availableSeats - 1;
if (!sportname.endsWith('s')) {
sportname = sportname.concat('s');
}
const NewSlotModel = mongoose.model(sportname, slotSchema);
var update = {};
update[time] = seats - 1;
console.log(update);
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update },
function (err, success) {
if (err) return handleError(err);
}
);
return res.json({ status: 'ok' });
} catch (e) {
console.log(e);
res.json({ status: 'error' });
}
});
where am I going wrong?
You are using both async/await and callbacks in your code, causing mongoose to throw an error.
The actual effect of using them both is exactly the error type that you are receiving:
Query was already executed
Mongoose v6 does not allow duplicate queries.
Mongoose no longer allows executing the same query object twice. If
you do, you'll get a Query was already executed error. Executing the
same query instance twice is typically indicative of mixing callbacks
and promises, but if you need to execute the same query twice, you can
call Query#clone() to clone the query and re-execute it. See gh-7398
Duplicate Query Execution
To fix the issue, just remove the third argument from the await
NewSlotModel.updateOne
Making it:
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update }
);
Mongoose v6. Don't support callbacks any longer.. check the image.
const productCount = await Product.countDocuments((count) => count) BAD
const productCount = await Product.countDocuments(); GOOD

Using promises in async function

I'm trying to listen for a Stripe webhook call, then carry out some actions such as sending an email. My site is on Netlify and I've adapted some code I found in a tutorial:
This works locally, but not when I run it as a Netlify function (basically a lambda). Basically, the part from "client.getSpace.." doesn't appear to run at all. I suspect this is something to do with using these .then promises within an async function, but I'm not sure.
require('dotenv').config();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const contentful = require('contentful-management');
const client = contentful.createClient({
accessToken:
process.env.CONTENTFUL_CONTENT_MANAGEMENT_ACCESS_TOKEN
});
var postmark = require("postmark");
var serverToken = process.env.POSTMARK_SERVER_TOKEN;
var postmark_client = new postmark.ServerClient(serverToken);
exports.handler = async ({ body, headers }) => {
try {
const stripeEvent = stripe.webhooks.constructEvent(
body,
headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
if (stripeEvent.type === 'checkout.session.completed') {
console.log('confirm checkout session completed');
const eventObject = stripeEvent.data.object;
const entryId = eventObject.client_reference_id;
let email = "";
let secret = "";
client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
.then(space => space.getEnvironment('master'))
.then(environment => environment.getEntry(entryId))
.then(entry => {
entry.fields.paymentStatus['en-GB'] = 'Paid';
email = entry.fields.email['en-GB'];
return entry.update();
})
.then(entry => entry.publish())
.then(entry => postmark_client.sendEmailWithTemplate({
"From": "x#test.com",
"To": email,
"TemplateId": 12345678,
"TemplateModel": {
"abc": "xyz"
}
}))
.catch(console.error)
}
return {
statusCode: 200,
body: JSON.stringify({ received: true }),
};
} catch (err) {
console.log(`Stripe webhook failed with ${err}`);
return {
statusCode: 400,
body: `Webhook Error: ${err.message}`,
};
}
};
For what it's worth to you and anyone else who comes across this question. I had a similar issue using NextJS on Vercel. I rewrote the .then syntax using async/await and the problem seems to be solved, so far. I'm no expert, but I think in this case you would begin by replacing
client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
.then(space => space.getEnvironment('master'))
with something like
const send = await client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
const getEnvironment = await space.getEnvironment('master')
so on and so forth. I'm not sure how you would rewrite everything else, or if this will even help, but it put me back on track.

Issues with scope in try/catch while using async/await

My issue is that (seemingly) things are going out of scope, or the scope is being polluted when I enter my catch block in the function below:
export const getOne = model => async (req, res, next) => {
let id = req.params.id
let userId = req.user
try {
let item = await model.findOne({ _id: id, createdBy: userId }).exec()
if (!item) {
throw new Error('Item not found!')
} else {
res.status(200).json({ data: item }) // works perfectly
}
} catch (e) {
res.status(400).json({ error: e }) // TypeError: res.status(...).json is not a function
// also TypeError: next is not a function
// next(e)
}
}
Interestingly enough, using res.status(...).end() in the catch block works just fine, but it bothers me that I am not able to send any detail back with the response. According to the Express Documentation for res.send() and res.json I should be able to chain off of .status(), which, also interestingly enough, works just fine in the try statement above if things are successful - res.status(200).json(...) works perfectly.
Also, I tried abstracting the error handling to middleware, as suggested on the Express documentation, and through closures, I should still have access to next in the catch statement, right? Why is that coming back as not a function?
Why does res.status(...).json(...) work in my try but not catch block?
Why is next no longer a function in the catch block?
Thanks in advance!
Edit
This is failing in unit tests, the following code produces the errors described above:
describe('getOne', async () => {
// this test passes
test('finds by authenticated user and id', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const list = await List.create({ name: 'list', createdBy: user })
const req = {
params: {
id: list._id
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(200)
return this
},
json(result) {
expect(result.data._id.toString()).toBe(list._id.toString())
}
}
await getOne(List)(req, res)
})
// this test fails
test('400 if no doc was found', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const req = {
params: {
id: mongoose.Types.ObjectId()
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(400)
return this
},
end() {
expect(true).toBe(true)
}
}
await getOne(List)(req, res)
})
})
Why does res.status(...).json(...) work in my try but not catch block?
Seems like you're passing a non-express object that only has status & end methods when running using the unit testing. That's why it fails to find the json method

Node express app calling mssql is saying that Connection is closed

I have another app which uses express and routes but this new app i was slimming it down. I know the connection string stuff is correct
script.getQuestions(connection);
script.getQuestions = function(connection,req, res){
console.log(connection);
}
I have read that some people said online to change to use a promise for async fixes this... problem is that with my function having req and res i don't know how to pass those in when i even try to refactor with a promise
"ConnectionError: Connection is closed"
"(module.js:487:32) code: 'ECONNCLOSED', name: 'ConnectionError' }"
What I call up (script) is
var sql = require('mssql');
exports.getQuestions = function(connection, req,res){
console.log(connection);
var request = new sql.Request(connection);
var query = 'select * from Question'
request.query(query).then(function(resultset){
res.json(resultset.recordset);
}).catch(function(err){
console.log(err);
//res.json(err)
})
}
it's a bit hard to understand what you're doing there. But here is an promise example to use mssql
const sql = require('mssql')
sql.connect(config).then(pool => {
// Query
return pool.request()
.input('input_parameter', sql.Int, value)
.query('select * from mytable where id = #input_parameter')
}).then(result => {
console.dir(result)
// Stored procedure
return pool.request()
.input('input_parameter', sql.Int, value)
.output('output_parameter', sql.VarChar(50))
.execute('procedure_name')
}).then(result => {
console.dir(result)
}).catch(err => {
// ... error checks
})
sql.on('error', err => {
// ... error handler
})
source: https://www.npmjs.com/package/mssql#promises

Categories

Resources