How to use Error.captureStackTrace in node.js - node.js

Lately I'm going through the implementation of Global Error Handling Middleware in node.js.
Then, I came across this Error.captureStackTrace(this,this.constructor).
I have checked the Node documentation & found that - Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.
MDN Docs - Maintains proper stack trace for where our error was thrown
appError.js File
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
// Error.captureStackTrace(this, this.constructor);
}}
app.js File
const AppError = require('./appError');
const express = require('express');
const app = express();
app.all('*', (req,res,next) => {
const custErr = new AppError('Mentioned Route is not available on server','404');
next();
})
My Observations when I tried to debug the code:
I found that .stack property is available on the custErr object even though I have commented the
Error.captureStackTrace(this, this.constructor) in appError.js file.
I'm still confused how to leverage the Error.captureStackTrace()
Can someone explain me on this?

One thing you need to understand is that apart from instances of the Error-classs the throw-statement can also throw other types. Consider this for example:
function throwSomeObj() {
throw {statusCode: 500};
}
try {
throwSomeObj();
} catch(err) {
console.log(err);
console.log(err.stack);
}
The exception that is thrown yields the object you passed to it, i.e. {statusCode: 500}. Now, as you can see this object does not have any stack-trace, since undefined is logged.
However, you can use Error.captureStackTrace to capture the stack-trace where you throw the error. Consider this:
function throwObjWithStacktrace() {
const someError = {statusCode: 500}
Error.captureStackTrace(someError)
throw someError;
}
try {
throwObjWithStacktrace();
} catch (err) {
console.log(err);
console.log(err.stack);
}
As you can see, now err contains the stack property and contains the stack to the function where the error was thrown.
Note that when instantiating a new Error-object the stack will automatically be set on that object.

So Today I got this error so what you have to do is follow these steps.
first of all, kill your ports by using npx kill-port 8000
see this
check you have installed all the dependencies.(reinstall them)
In my case I have reinstalled all the dependencies.
and if the error still persists, reinstall the node.
see this now after following all the steps

You can add this to your package.json file.
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon --experimental-modules --es-module-specifier-resolution=node index.js"
},

In my case, I had set the wrong path, so the node did not find the right path and what I did was:
Moved my src folder into my server folder.
Executed node again: node src/server.mjs
Done, server up and running.

Related

Cannot catch BadRequestException thrown by FilesInterceptor

I am trying to set up a logic to save uploaded files using Multer.
To do this, I follow the Nestjs tutorial and use the "FilesInterceptor" interceptor.
controller file :
import {
Controller,
FileValidator,
ParseFilePipe,
Post,
UploadedFiles,
UseInterceptors
} from '#nestjs/common';
import { FilesInterceptor } from '#nestjs/platform-express';
import { Public } from 'src/auth/decorators/public.decorator';
import { MimeTypeValidationPipe } from './pipes/mimetype.validation.pipe';
const ACCEPTED_FILE_MIMETYPES = ["image/jpeg", "image/jpg", "image/png"]
const validators: FileValidator[] = [
new MimeTypeValidationPipe({ accepted: ACCEPTED_FILE_MIMETYPES })
];
#Controller('uploads')
export class UploadsController {
#Public()
#Post("/")
#UseInterceptors(FilesInterceptor("files"))
public async create(
#UploadedFiles(new ParseFilePipe({ validators }))
files: Express.Multer.File[]
){
files[0].originalname
const filenames = files.map(({ originalname }) => originalname)
return { filenames };
}
}
However, when I test the behavior of the server when the number of uploaded files exceeds the limit, the server returns me an error 500 (As if the error was not handled).
I then try to catch it by using an ExcepetionFilter like this one:
#Catch()
class TestFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
console.debug(exception)
if(exception instanceof HttpException) console.debug("This is an HTTP Exception !");
else console.debug("This is NOT an HTTP Exception");
const response = host.switchToHttp().getResponse<Response>();
return response.status(500).json({statusCode: 500 , message: "ERROR" });
}
}
And i get the following output :
BadRequestException: Too many files
at transformException (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/multer/multer/multer.utils.js:19:20)
at ~/development/Nest/nestapi/node_modules/#nestjs/platform-express/multer/interceptors/files.interceptor.js:18:73
at ~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/multer/lib/make-middleware.js:53:37
at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
at listener (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/on-finished/index.js:170:15)
at onFinish (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/on-finished/index.js:101:5)
at callback (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/ee-first/index.js:55:10)
at IncomingMessage.onevent (~/development/Nest/nestapi/node_modules/#nestjs/platform-express/node_modules/ee-first/index.js:93:5)
at IncomingMessage.emit (node:events:539:35)
at endReadableNT (node:internal/streams/readable:1345:12) {
response: { statusCode: 400, message: 'Too many files', error: 'Bad Request' },
status: 400
}
This is NOT an HTTP Exception
The filter indicates that it is NOT an HTTPException.
However, while digging in the FilesInterceptor.ts code I notice that the caught errors are handled by a small utility function "transformException" which is supposed to transform the Multer error into an HttpException (depending on the error code returned by Multer)
multer.utils.ts file (from nest repo)
import {
BadRequestException,
HttpException,
PayloadTooLargeException,
} from '#nestjs/common';
import { multerExceptions } from './multer.constants';
export function transformException(error: Error | undefined) {
if (!error || error instanceof HttpException) {
return error;
}
switch (error.message) {
case multerExceptions.LIMIT_FILE_SIZE:
return new PayloadTooLargeException(error.message);
case multerExceptions.LIMIT_FILE_COUNT:
case multerExceptions.LIMIT_FIELD_KEY:
case multerExceptions.LIMIT_FIELD_VALUE:
case multerExceptions.LIMIT_FIELD_COUNT:
case multerExceptions.LIMIT_UNEXPECTED_FILE:
case multerExceptions.LIMIT_PART_COUNT:
return new BadRequestException(error.message);
}
return error;
}
I don't understand why my filter (and NestJS' unhandledExceptionFilter) can't detect this exception, since for me it is supposed to be an instance of HttpException.
Can you help me?
Best regards
You probably have 2 copies of #nestjs/common being included in your project. The code that creates the error is using one copy, and your exception filter is using the other copy. When your code is checking instanceof, it's checking to see if the exception is an instance of HttpException from it's copy of #nestjs/common, but it's not, it's an instance of HttpException from the other copy of #nestjs/common. This is known as "multiple realms" (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms).
The way to fix this is to make sure you only have 1 copy of #nestjs/common in your project. Often, the reason you have 2 is because you have 2 package.json files with different version specs that they call for (e.g. "#nestjs/common": "^8.0.0" in one package.json, and "#nestjs/common": "^9.0.0" in another). You may need to use e.g. the overrides key to force a dependency to use the same version that you use elsewhere.
Hope that helps!
Sorry!
I think the problem is with me.
The LTS version (1.4.4-lts.1) of multer is buggy. So I decided to downgrade to 1.4.4 (version in which the bug in question does not occur). But to do so, I had to downgrade the nested dependency manually by doing npm install multer#1.4.4 in the node_modules/#nest/platform-express directory.
But that's when nestjs starts to format my errors badly.
The funny thing is that going back (npm install multer#1.4.4-lts.1 to the node_modules/#nest/platform-express directory), it doesn't solve the problem (Errors are still badly handled) and I have to delete the node_modules/#nest/platform-express folder and reinstall the package from the root of the project to get everything back in order (But with the LTS version bug, of course).
It's weird.

Jest test passed but get Error: connect ECONNREFUSED 127.0.0.1:80 at the end

I'm using node with TypeScript on my back end and Jest and Supertest as my test framework on my back end.
When I'm trying to test I have the result pass but I get an error at the end. Here's the result:
PASS test/controllers/user.controller.test.ts
Get all users
✓ should return status code 200 (25ms)
console.log node_modules/#overnightjs/logger/lib/Logger.js:173
[2019-12-05T04:54:26.811Z]: Setting up database ...
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.284s
Ran all test suites.
server/test/controllers/user.controller.test.ts:32
throw err;
^
Error: connect ECONNREFUSED 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1104:14)
npm ERR! Test failed. See above for more details.
Here's my test code:
import request from "supertest";
import { AppServer } from '../../config/server';
const server = new AppServer();
describe('Get all users', () => {
it('should return status code 200', async () => {
server.startDB();
const appInstance = server.appInstance;
const req = request(appInstance);
req.get('api/v1/users/')
.expect(200)
.end((err, res) => {
if (err) throw err;
})
})
})
Here's my server setup. I'm using overnightjs on my back end.
I created a getter to get the Express instance. This is coming from overnight.js.
// this should be the very top, should be called before the controllers
require('dotenv').config();
import 'reflect-metadata';
import { Server } from '#overnightjs/core';
import { Logger } from '#overnightjs/logger';
import { createConnection } from 'typeorm';
import helmet from 'helmet';
import * as bodyParser from 'body-parser';
import * as controllers from '../src/controllers/controller_imports';
export class AppServer extends Server {
constructor() {
super(process.env.NODE_ENV === 'development');
this.app.use(helmet());
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: true }));
this.setupControllers();
}
get appInstance(): any {
return this.app;
}
private setupControllers(): void {
const controllerInstances = [];
// eslint-disable-next-line
for (const name of Object.keys(controllers)) {
const Controller = (controllers as any)[name];
if (typeof Controller === 'function') {
controllerInstances.push(new Controller());
}
}
/* You can add option router as second argument */
super.addControllers(controllerInstances);
}
private startServer(portNum?: number): void {
const port = portNum || 8000;
this.app.listen(port, () => {
Logger.Info(`Server Running on port: ${port}`);
});
}
/**
* start Database first then the server
*/
public async startDB(): Promise<any> {
Logger.Info('Setting up database ...');
try {
await createConnection();
this.startServer();
Logger.Info('Database connected');
} catch (error) {
Logger.Warn(error);
return Promise.reject('Server Failed, Restart again...');
}
}
}
I read this question - that's why I called the method startDB.
So I figured out and the solution is quite easy. I can't explain why though.
This req.get('api/v1/users/') should be /api/v1/users - you need a leading /.
For Frontend...
If you are making use of axios and come across this error, go to the testSetup.js file and add this line
axios.defaults.baseURL = "https://yourbaseurl.com/"
This worked for me. So, typically, this is a baseURL issue.
I had this error in my React frontend app tests.
I was using React testing library's findBy* function in my assert:
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
After I changed it to:
await waitFor(async () => {
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
});
the error is gone.
I don't know exactly why, but maybe it will help someone
UPDATE: I was mocking fetch incorrectly, so my test called real API and caused that error
I put this line in my setupTests file:
global.fetch = jest.fn()
It mocks fetch for all tests globally. Then, you can mock specific responses right in your tests:
jest.mocked(global.fetch).mockResolvedValue(...)
// OR
jest.spyOn(global, 'fetch').mockResolvedValue(...)
Slightly different issue, but same error message...
I was having this error when using node-fetch when trying to connect to my own localhost (http://localhost:4000/graphql), and after trying what felt like everything under the sun, my most reliable solution was:
using this script in package.json: "test": "NODE_ENV=test jest --watch"
If the terminal shows connection error I just go to the terminal with Jest watching and press a to rerun all tests and they pass without any issue.
¯\_(ツ)_/¯
Success rate continued to improve by renaming the testing folder to __tests__ and moving my index.js to src/index.js.
Very strange, but I am too exhausted to look at the Jest internals to figure out why.
The rules for supertest are the same as the rules for express. OvernightJS does not require any leading or ending "/" though.
For anyone landing on this, but not having issues with trailing slashes:
jest can also return a ECONNREFUSED when your express app takes some time (even just a second) to restart/init. If you are using nodemon like me, you can disable restarts for test files like --ignore *.test.ts.
This error also occurs if you have not set up a server to catch the request at all (depending on your implementation code and your test, the test may still pass).
I didn't get to the bottom of this error - it wasn't related to the (accepted) leading slash answer.
However, my "fix" was to move the mocks up into the suite definition - into beforeAll and afterAll for cleanup between tests).
Before, I was mocking (global.fetch) in each test, and it was the last test in the suite to use the mock that would cause the error.
In my case, the issue was related to package react-inlinesvg. Package makes a fetch request to get the svg file and since server is not running, it gets redirected to default 127.0.0.1:80.
I mocked react-inlinesvg globally to output props including svg filename to assert in testing.
jest.mock('react-inlinesvg', () => (props) => (
<svg data-testid="mocked-svg">{JSON.stringify(props)}</svg>
));

Undefined property when unit testing my discord.js bot (the test itself is passed, but it is followed by an error)

I am trying to set up unit testing for my discord.js bot, but when running npm test in the terminal, while the test is being passed, still gives an error.
This is an image of the test being passed followed by the error:
https://i.imgur.com/m2EOuxc.png
I need to fix this error in testing, while still having the bot being able to function.
I have tried to completely remove the line referenced in the error (and the lines that had something to do with that specific line)
jsfiles.forEach((f, i) => {
let props = require(`./cmds/${f}`)
bot.commands.set(props.help.name, props)
})
Removing this resolved the testing issue, but resulted in the bot not functioning correctly (it did not load the commands; meaning, the bot couldn't be interacted with), which is not the goal here.
I've also checked, that each of the files in the folder cmds ends with
module.exports.help = {
name: '<name of the command I use for each command>'
}
This is the part of my bot.js file that contains the problem.
// Loads the commands for the bot:
fs.readdir('./cmds/', (err, files) => {
if (err) console.error(err)
let jsfiles = files.filter(f => f.split('.').pop() === 'js')
if (jsfiles.length <= 0) {
console.log('No commands to load!')
return
}
if (testingSettings) {
console.log(`Loading ${jsfiles.length} commands!`)
}
// This is the problem referenced above:
// ----------------------------------------------------------------------
jsfiles.forEach((f, i) => {
let props = require(`./cmds/${f}`)
bot.commands.set(props.help.name, props)
})
// ----------------------------------------------------------------------
})
This is all of my code in the bot.test.js file
const {
// Functions
checkingTesting,
// Variables
testingSettings,
} = require('./bot')
test('checking to see if testing-mode is on', () => {
expect(checkingTesting(testingSettings, 'token')).toBe(process.env['token']);
});
If it is needed. This is the function, variable and exporting method that is used to connect bot.js to bot.test.js:
Variable (in bot.js file)
const testingSettings = false
Function (in bot.js file)
function checkingTesting (testingSettings, name) {
if (testingSettings) {
return testSettings[name]
} else if (!testingSettings) {
return process.env[name]
}
}
Exporting (in bot.js file)
module.exports = {
// Exporting functions
checkingTesting: checkingTesting,
// Exporting variables
testingSettings: testingSettings,
}
props.help is undefined. The required file's exported obj is either empty, doesn't have help, or some other unforeseen event.
A good practice is to always check whether an object key exist prior using it.
if (props && props.help) {
bot.commands.set(props.help.name, props)
} else {
//throw or handle error here
}
In your command file, it seems like there is no help property of module.exports. When you try to read help.name, it throws your error because help is undefined.
Check to make sure that you're declaring module.exports.help in every command file.

express body-parser utf-8 error in test

Super stumped by this. I have some server code that for some reason throws a UTF-8 error in my tests but works fine when running the server normally:
code:
export default ({ projectId = PROJECT_ID, esHost = ES_HOST } = {}) => {
let app = express();
app.use(cors());
app.use(bodyParser.json({ limit: '50mb' }));
let http = Server(app);
let io = socketIO(http);
let server = {
app,
io,
http,
status: 'off',
listen(
port = PORT,
cb = () => {
rainbow(`⚡️ Listening on port ${port} ⚡️`);
},
) {
this.http.listen(port, () => {
main({ io, app, projectId, esHost, port });
this.status = 'on';
cb();
});
},
close(cb = () => {}) {
if (this.http) {
this.http.close(() => {
this.status = 'off';
cb();
});
} else {
throw '❗️ cannot close server that has not been started ❗️';
}
},
};
return server;
};
usage (exactly the same, but in jest test body-parser isn't working properly):
import createServer from '../server'
let server = createServer()
server.listen(5050);
I'm using postman, post response outside of test:
{
"projects": [
{
"id": "test",
"active": true,
"timestamp": "2018-02-25T21:33:08.006Z"
},
{
"id": "TEST-PROJECT",
"active": true,
"timestamp": "2018-03-05T21:34:34.604Z"
},
{
"id": "asd",
"active": true,
"timestamp": "2018-03-06T23:29:55.348Z"
}
],
"total": 3
}
unexpected post response inside jest test server:
Error
UnsupportedMediaTypeError: unsupported charset "UTF-8" at /Users/awilmer/Projects/arranger/node_modules/body-parser/lib/read.js:83:18 at invokeCallback (/Users/awilmer/Projects/arranger/node_modules/raw-body/index.js:224:16) at _combinedTickCallback (internal/process/next_tick.js:131:7) at process._tickCallback (internal/process/next_tick.js:180:9)
So I was able to reproduce the issue and find the source of the issue and the workaround to make it work. The issue is caused by jest framework.
Before you jump on reading the rest of the thread, I would suggest you read another Jest thread I answer long back. This would help get some context internals about the require method in jest
Specify code to run before any Jest setup happens
Cause
The issue happens only in test and not in production. This is because of jest require method.
When you run your tests, it starts a express server, which calls the node_modules/raw-body/index.js as shown in below image
As you can see the encodings is null. This is because the iconv-lite module does a lazy loading of encodings. The encodings are only loaded when getCodec method gets executed.
Now when your test has fired the API, the server needs to read the body so the getCodec gets called
This then goes through the jest-runtime/build/index.js custom require method (which is overloaded if you read the previous link).
The execModule has a check for this._environment.global, which is blank in this case and hence a null value is returned and the module never gets executed
Now when you look at the exports of the encodings module, it just is a blank object
So the issue is purely a jest. A feature jest lacks or a bug mostly?
Related Issues
Related issues have already been discussed on below threads
https://github.com/facebook/jest/issues/2605
https://github.com/RubenVerborgh/N3.js/issues/120
https://github.com/sidorares/node-mysql2/issues/489#issuecomment-313374683
https://github.com/ashtuchkin/iconv-lite/issues/118
https://github.com/Jason-Rev/vscode-spell-checker/issues/159
Fix
The fix to the problem is that we load the module during our test itself and force a early loading instead of lazy loading. This can be done by adding a line to your index.test.js at the top
import encodings from '../../node_modules/iconv-lite/encodings';
import createServer from '#arranger/server';
After the change all the test pass, though you have a error in the url of the test so you get Cannot POST /
I'm adding a slightly different solution inspired from #Tarun Lalwani
Add the following lines at the top of your test file.
const encodings = require('./node_modules/iconv-lite/encodings');
const iconvLite = require('./node_modules/iconv-lite/lib');
iconvLite.getCodec('UTF-8');
I spent many hours trying to figure out why Jest would report a 415 error code when testing the Node.js server. Node.js is configured to use app.use(bodyParser.json(...)); on our system, too. That didn't solve the issue.
Solution
When using res.status(...), you MUST either chain on .json() or use res.json(), too. That means if you respond with a 500 error or otherwise and you don't return any JSON data, you still need to use res.json(). No idea why, as that defeats the whole purpose of app.use(bodyParser.json(...)); in the first place.
Example
const express = require('express');
const router = express.Router();
router.post("/register", (req, res) => {
// ...
res.status(500).json();
// ...
});

Mocha test express startup

My express server throws an Error, if there are parameters missing (e.g. the DB URI). I want to test in Mocha that it actually threw the errors, but I don't know how I can make Mocha do that.
if(!parameters.db) {
throw new Error('Please provide a db URI');
}
I have a test setup like this:
it('Throws Error when no db URI provided in production mode', function () {
expect(require('../server')).to.throw();
done();
});
When my Express app throws the error, the error is thrown into the console and the test fails (in fact, it doesn't end).
The other problem I have is that Express only checks for the parameters if the environment is in production mode. I have tried to set the process environment to production inside the test suite, but when I run it, the NODE_ENV is still set to 'test'.
before(function() {
env = process.env;
// This doesn't really set the environment when I run the tests.
process.env.NODE_ENV = 'production';
});
If you are trying to test for an exception that is not handled within your application try wrapping your invocation in a try/catch and returning an error if the error isn't thrown.
it('Throws Error when no db URI provided in production mode', function () {
let threwError = false;
try {
// create the server that throws the Error
require('../server');
} catch (err) {
threwError = true;
}
expect(threwError, 'Didn\'t throw an error, when DB URI is empty').to.be.true;
});

Resources