Throwing HTTP nest Exception results in Promise rejection - node.js

I'm trying to throw a http nest exception to make use of the default nest handler.
I Tried to throw the exception in the controller that calls the adminService.update function, and with great success it worked.
async update(update: DeepPartial<Admin>) {
const admin = await this.findOne({ id: update.id});
const adminName = await this.findOne({ username: update.username});
if (!adminName) {
throw new ConflictException('Username already in use');
}
admin.username = update.username;
admin.save();
}
Output when putting the call in the controller:
{
"statusCode": 409,
"error": "Conflict",
"message": "Username already in use"
}
The controller method.
#Put()
async update(#Body() updateDTO: UpdateDTO): Promise<void> {
throw new ConflictException('Username already in use');
this.adminService.update(updateDTO);
}
The error itself:
UnhandledPromiseRejectionWarning: Error: [object Object] at AdminService.<anonymous> (C:\Users\JBRETAS_EXT\Documents\mapa-digital\dist\admin\admin.service.js:55:19) at Generator.next (<anonymous>)

It seems that I was missing a return statement in my controller method.
#Put()
async update(#Body() updateDTO: UpdateDTO): Promise<void> {
return this.adminService.update(updateDTO);
}

Related

Nodejs Netsuite restless Error : (node:1128) UnhandledPromiseRejectionWarning: Unhandled promise rejection

I am using nodejs npm module netsuite-rest to connect to netsuite rest web services and I am getting this error : UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch bloc
let NetSuiteRestlet = require('netsuite-restlet');
const config = {
account: 'XXXXX',
username: 'XXXXX',
password: 'XXXX',
role: 'FOM PH Sales Manager'
};
const url = 'https://6218235.suitetalk.api.netsuite.com';
const parameters = {
internalid: 1054
};
let ns = new NetSuiteRestlet(config);
// Example using the get function
ns.get(parameters, url).then((out) => { console.log(out) });
It looks like your get request is failing, but is not being handled correctly.
Since you are using promises, you would simply chain on a .catch after the .then to catch the promise rejection.
For example:
ns.get(parameters, URL)
.then((out) => { console.log(out) })
.catch((error) => {
console.log(error) // and/or handle the error some other way
}
See here for more:
How to handle promise rejections: https://flaviocopes.com/javascript-promises-rejection/

Why am I having trouble displaying and deleting in Mongo using Prisma (React app)?

I use Node, Express, React, Mongo and Prisma to import a csv file in the database, display it on the frontend and delete all records in the db. It worked with one record and so I assumed it would work with the rest of the csv file (1000 records). But I get an error:
Invalid `prisma.movie.findMany()` invocation:
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: RawError { code: "unknown", message: "Command failed (CursorNotFound): cursor id 124425195753416376 not found)" } })
(node:2171) UnhandledPromiseRejectionWarning: Error:
Invalid `prisma.movie.deleteMany()` invocation:
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: RawError { code: "unknown", message: "Command failed (CursorNotFound): cursor id 4391617472265441923 not found)" } })
at cb (/Users/nwsursock/Sites/test-algot/backend/node_modules/#prisma/client/runtime/index.js:36378:17)
at runMicrotasks ()
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async exports.deleteRequest (/Users/nwsursock/Sites/test-algot/backend/src/controllers/movie.controller.js:49:3)
(node:2171) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
My code is rather simple. I'm using the Prisma API in REST endpoints.
const { PrismaClient } = require("#prisma/client");
const prisma = new PrismaClient();
exports.createRequest = async (req, res) => {
const movie = req.body.movie;
console.log("============> importing", movie);
const data = {
name: movie.Name,
genre: movie.Genre,
descr: movie.Description,
director: movie.Director,
actors: movie.Actors,
year: movie.Year,
runtime: Number(movie["Runtime (Minutes)"]),
rating: Number(movie.Rating),
votes: Number(movie.Votes),
revenue: Number(movie["Revenue (Millions)"]),
score: Number(movie.Metascore),
};
const result = await prisma.movie.create({ data });
console.log("============> imported", result);
res.status(201).json({ message: "Movie correctly added!" });
};
exports.readRequest = async (req, res) => {
try {
const movies = await prisma.movie.findMany();
res.status(200).json(movies);
} catch (e) {
console.log("======> Error:", e.message);
}
};
exports.deleteRequest = async (req, res) => {
await prisma.movie.deleteMany({});
res.status(202).json({ message: "Table deleted!" });
};
It's a version problem. You have to downgrade to Prisma 2.26. Above, the bug appears. https://github.com/prisma/prisma/issues/8389

ElasticSearch-js { body } is undefined

I am getting this error when running a AWS lambda function to push data into an Elasticsearch instance
I can get it to run if I manually remove the { body } from the node modules, but I can't find why it keeps erroring on that.
my code
client.helpers.bulk({
datasource: docs,
onDocument(doc) {
return {
index: { _index: index , _id: doc.id },
body: doc.body
}
},
onDrop(doc) {
console.log("failed to index ", doc.key);
},
retries: 5,
flushBytes: 1000000,
wait: 10000
})
error
{
"errorType": "Runtime.UnhandledPromiseRejection",
"errorMessage": "TypeError: Cannot destructure property 'body' of 'undefined' as it is undefined.",
"reason": {
"errorType": "TypeError",
"errorMessage": "Cannot destructure property 'body' of 'undefined' as it is undefined.",
"stack": [
"TypeError: Cannot destructure property 'body' of 'undefined' as it is undefined.",
" at /var/task/node_modules/#elastic/elasticsearch/lib/Helpers.js:679:81"
]
},
"promise": {},
"stack": [
"Runtime.UnhandledPromiseRejection: TypeError: Cannot destructure property 'body' of 'undefined' as it is undefined.",
" at process.<anonymous> (/var/runtime/index.js:35:15)",
" at process.emit (events.js:314:20)",
" at process.EventEmitter.emit (domain.js:483:12)",
" at processPromiseRejections (internal/process/promises.js:209:33)",
" at processTicksAndRejections (internal/process/task_queues.js:98:32)"
]
}
I was also getting this error when referencing #opensearch-project/opensearch (which is an elasticsearch-js client fork) and using the client.helpers.bulk helper. That was in conjunction with aws-elasticsearch-connector for implementing AWS SigV4 signed API requests.
The error message was as follows:
TypeError: Cannot destructure property 'body' of 'undefined' as it is undefined.
at node_modules/#opensearch-project/opensearch/lib/Helpers.js:704:93
It was quite annoying and I was not in the mood for implementing my own OpenSearch client and interacting with the APIs directly, so I dig deeper and found the issue.
How can one reproduce the bug?
I created an isolated test to illustrate the problem. Hopefully it's easily reproducible this way.
import { Client } from '#opensearch-project/opensearch';
import * as AWS from 'aws-sdk';
// My fork of https://www.npmjs.com/package/aws-elasticsearch-connector capable of signing requests to AWS OpenSearch
// #opensearch-project/opensearch is not yet capable of signing AWS requests
const createAwsElasticsearchConnector = require('../modules/aws-oss-connector');
const domain =
'PUT_YOUR_DOMAIN_URL_HERE.es.amazonaws.com';
const index = 'YOUR_TEST_INDEX_NAME';
const bootstrapOSSClient = (): Client => {
const ossConnectorConfig = createAwsElasticsearchConnector(AWS.config);
const client = new Client({
...ossConnectorConfig,
node: `https://${domain}`,
});
return client;
};
const main = async (): Promise<void> => {
try {
console.info('Starting processing');
// TEST DEFINITION
const input = [
{ id: '1', name: 'test' },
{ id: '2', name: 'test 2' },
];
const client = bootstrapOSSClient();
const response = await client.helpers.bulk({
datasource: input,
onDocument(doc: any) {
console.info(`Processing document #${doc.id}`);
return {
index: { _index: index, _id: doc.id },
};
},
});
console.info(`Indexed ${response.successful} documents`);
// END TEST DEFINITION
console.info('Finished processing');
} catch (error) {
console.warn(`Error in main(): ${error}`);
}
};
try {
main().then(() => {
console.info('Exited main()');
});
} catch (error) {
console.warn(`Top-level error: ${error}`);
}
and the result was
$ npx ts-node ./.vscode/test.ts
Starting processing
Processing document #1
Processing document #2
(node:39232) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'body' of 'undefined' as it is undefined.
at D:\Development\eSUB\Coronado\git\platform\node_modules\#opensearch-project\opensearch\lib\Helpers.js:704:93
(Use `node --trace-warnings ...` to show where the warning was created)
(node:39232) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:39232) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Indexed 2 documents
Finished processing
Exited main()
Stepping through the code I am able to intercept a single call to node_modules/#opensearch-project/opensearch/lib/Helpers.js:704:93 where
client.bulk() is called
which calls bulkApi() in \opensearch\api\api\bulk.js and returns successfully
await finish() is called in \opensearch\lib\Helpers.js:559
inside \opensearch\lib\Transport.js a call to prepareRequest() is made, ending with return transportReturn
this ends up in request():177 calling
return p.then(onFulfilled, onRejected)
with p being null at that time. That resulted my callback in the AWS Transport class responsible for signing requests to callback to the Helper.js tryBulk() with second parameter undefined, resulting in the Cannot destructure property 'body' error.
What is the expected behavior?
Transport.js implementation of the request should obviously not result in a null p promise issue when callback is passed in the request() call. I logged a bug in the opensearch-js repository.
Workaround
At least for me, this looks to be a problem only when using custom AWS signed requests connector implementation. If your case is similar, a quick workaround involves modifying that implementation Transport class. Here is a quick and dirty hotfix that's specific to aws-elasticsearch-connector.
You need to modify AmazonTransport.js from
class AmazonTransport extends Transport {
request (params, options = {}, callback = undefined) {
...
// Callback support
awaitAwsCredentials(awsConfig)
.then(() => super.request(params, options, callback))
.catch(callback)
}
to
// Callback support
// Removed .then() chain due to a bug https://github.com/opensearch-project/opensearch-js/issues/185
// .then() was calling then (onFulfilled, onRejected) on transportReturn, resulting in a null value exception
awaitAwsCredentials(awsConfig).then();
try {
super.request(params, options, callback);
} catch (err) {
callback(err, { body: null });
}

Express is res.sending both success and error - ERR_HTTP_HEADERS_SENT

I have strange problem with [ERR_HTTP_HEADERS_SENT]: : Cannot set headers after they are sent to the client. It's in try/catch, but still somehow after res.send({message: 'Company set up'}) it also sends res.send({error}) which shouldn't occur.
I also tried return res.send({message: 'Company set up'}), but I have still same error. Along with that error I also get:
(node:19234) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1).
What I found that there is no problem when await knex() calls are commented out.
try {
if (req.files) { //multer
let data = req.body
const locations = JSON.parse(data.locations)
const parsedLocations = locations.map((obj, index) => {
(...)
})
await asyncForEach(parsedLocations, async (element, index) => {
const country = await knex('countries').first().where({ country: element.country })
})
console.log('company set up')
res.send({ message: 'Company set up' })
}
} catch (error) {
console.log('error')
console.log(error)
res.send({ error})
}
Here's the asyncForEach function for reference:
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
Error log from Node.js:
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (/Users/xx/Developer/microservice/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/Users/xx/Developer/microservice/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/Users/xx/Developer/microservice/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/xx/Developer/microservice/node_modules/express/lib/response.js:158:21)
at router.post (/Users/xx/Developer/microservice/routes/admin.js:714:11)
(node:19337) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
This error is due to trying to send multiple response.
You can check if response has been already sent or not before returning response.
Like:
if (res.headerSent) {
res.send({ data/ error})
If already sent, then it will not send another.
Your code is not catching the error.
return res.status(405).send('User already exist');

How to test a function that throws an error asynchronously, using tape?

I am attempting to test this module (receiver.js) for an error thrown:
var request = require('request')
module.exports = function(url){
request({
url: url,
method: 'POST'
}, function(error) {
if(error){
throw error
}
})
}
using this test (test.js):
var test = require('tape')
test('Receiver test', function(t){
var receiver = require('./receiver')
t.throws(function(){
receiver('http://localhost:9999') // dummy url
}, Error, 'Should throw error with invalid URL')
t.end()
})
but tape runs the assertion before the error is thrown, resulting in the following error message:
TAP version 13
# Receiver test
not ok 1 Should throw error with invalid URL
---
operator: throws
expected: |-
[Function: Error]
actual: |-
undefined
at: Test.<anonymous> (/path/to/tape-async-error-test/test.js:5:4)
...
/path/to/receiver.js:9
throw error
^
Error: connect ECONNREFUSED 127.0.0.1:9999
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1062:14)
Is there a way around this?
Generally, using tape, you have to ensure you call assert.end() after the async call has completed. Using promises (would require request-promise and returning the promise):
test('Receiver test', function(t){
// Tells tape to expec a single assertion
t.plan(1);
receiver('http://localhost:9999')
.then(() => {
t.fail('request should not succeed')
})
.catch(err => {
t.ok(err, 'Got expected error');
})
.finally({
t.end();
});
});
Using async/await:
test('Receiver test', async function(t) {
try {
await receiver('http://localhost:9999');
assert.fail('Should not get here');
} catch (err) {
assert.ok(err, 'Got expected error');
}
t.end();
});
The above example is mostly correct but here's a complete working example that compares async to synchronous side by side and also shows how to check for the error message in a manner similar to the tape examples given on tape's README.md.
test('ensure async function can be tested to throw', async function(t) {
// t.throw works synchronously
function normalThrower() {
throw(new Error('an artificial synchronous error'));
};
t.throws(function () { normalThrower() }, /artificial/, 'should be able to test that a normal function throws an artificial error');
// you have to do this for async functions, you can't just insert async into t.throws
async function asyncThrower() {
throw(new Error('an artificial asynchronous error'));
};
try {
await asyncThrower();
t.fail('async thrower did not throw');
} catch (e) {
t.match(e.message,/asynchronous/, 'asynchronous error was thrown');
};
});

Resources