Custom error handler in express.js is not called - javascript

I am setting up a simple express.js node server, and I can't seem to get my custom error handler to work. It seems like it isn't being called at all and instead some default express.js error handler is being called.
Note that this is in typescript, and uses mongodb, if that matters (don't think it should). Here is the relevant code:
index.ts:
import routes from './routes/index.ts';
import express, { Request, Response, NextFunction } from 'express';
// other imports...
type ServerErrror = {
status: number;
message: string;
}
const app = express();
// set up logging...
app.use(bodyParser.json());
app.use('/', routes);
app.use((err: ServerError, req: Request, res: Response, next: NextFunction) => { // eslint-disable-line no-unused-vars
console.error('in error handler');
res.status(err.status).send(`got error: ${err.message}`);
});
mongoose.connect(MONGODB_URI)
.then(() => {
console.log('Successfully connected to database.');
app.listen(SERVER_PORT, () => {
console.log(`Server is up and running on port ${SERVER_PORT}`);
}).on('error', error => {
console.log('Error starting server:', error);
});
}, error => {
console.log('Error connecting to database:', error);
});
routes/index.ts:
import { Router } from 'express';
import dataRoutes from './data.ts';
const router = Router();
router.use('/data', dataRoutes);
export default router;
routes/data.ts:
import { Router } from 'express';
import Data from './models/Data';
const router = Router();
router.get('/', (_, res, next) => {
Data.find((error, data) => {
if (error) {
console.log('found an error')
next({ status: 500, message: 'got an error' });
return;
}
res.send(`Got data: ${JSON.stringify(data.map(datum => datum.toJSON()))}`);
});
});
export default router;
When I start the server and then send a GET request to the /data endpoint using postman, this is the output on the server:
Successfully connected to database.
Server is up and running on port 1234
found an error
GET /data 500 35.289 ms - 142 // this is from morgan logging
[object Object] // no idea where this is coming from, i assume express default error handler
And this is the return value i see in postman:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>[object Object]</pre>
</body>
</html>
Again, not sure where this is coming from. I assume it must be the express default error handler.

There are some problems with your code:
import routes from './routes/index.ts' should not have the *.ts extension.
import dataRoutes from './data.ts'; should not have the *.ts extension.
ServerErrror has one too many letters.
Here is short example that works on my machine. I removed the Mongoose-related code and changed the sample so that it is easy to reproduce in a single file. A call to http://localhost:5000/data returns customErrorHandler > dataRoutes.get > Error, which is I think what you require.
import { Request, Response, NextFunction } from 'express';
import bodyParser = require('body-parser');
import express = require('express');
type ServerError = {
status: number;
message: string;
}
const dataRoutes = express.Router().get('/', (_, res, next) => {
setTimeout(() => { // mimic an asynchronous operation
const error = true;
if (error) {
next({ status: 500, message: 'dataRoutes.get > Error' });
return;
}
res.send('dataRoutes.get > Success');
}, 1000);
});
const routes = express.Router().use('/data', dataRoutes);
const app = express();
app.use(bodyParser.json());
app.use('/', routes);
app.use((err: ServerError, req: Request, res: Response, next: NextFunction) => {
res
.status(err.status)
.send(`customErrorHandler > ${err.message}`);
});
app.listen(5000);
console.log('Now listening on port 5000');
This is my tsconfig.json file:
{
"compilerOptions": {
"moduleResolution": "node",
"module": "commonjs",
"allowSyntheticDefaultImports": true
}
}
These are my package.json dependencies:
"devDependencies": {
"#types/express": "^4.16.1",
"typescript": "^3.4.1"
},
"dependencies": {
"express": "^4.16.4"
}

It turns out the problem was my use of babel-preset-minify which apparently did not transform the code correctly. Getting rid of this preset made the code work with no other modifications.

In my case, I disable babel-preset-minify option deadcode and work well.
{
"presets": [
[
"minify",
{
"deadcode": false
}
]
]
}

Related

Accessing the TypeORM Entity (MYSQL Table) in TypeScript

index.ts
import express, { Express } from 'express';
import dotenv from 'dotenv';
import { DataSource } from 'typeorm';
import cors from 'cors';
import bodyParser from 'body-parser';
import { Task } from './src/tasks/tasks.entity';
import { tasksRouter } from './src/tasks/tasks.router';
// Instantiate express app
const app: Express = express();
dotenv.config();
//Parsing incoming request
app.use(bodyParser.json());
//Use CORS install types as well
app.use(cors());
// Create Databse Connection
export const AppDataSource = new DataSource({
type: 'mysql',
host: 'localhost',
port: 3306,
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DB,
entities: [Task],
synchronize: true,
logging: true,
});
//Define Server Port
const port = process.env.PORT;
AppDataSource.initialize()
.then(() => {
// Start listening Request on defined port
app.listen(port);
console.log('Data Source has been initialized');
})
.catch((err) => console.log(err));
//create a Route
app.use('/', tasksRouter);
tasks.router.ts
import { Request, Response, Router } from 'express';
import { validationResult } from 'express-validator';
import { tasksController } from './tasks.controller';
import { createValidator } from './tasks.validator';
export const tasksRouter: Router = Router();
tasksRouter.get('/tasks', tasksController.getAll);
tasksRouter.post(
'/tasks',
createValidator,
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
//#ts-ignore
async (req: Request, res: Response) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res
.json({
errors: errors.array(),
})
.status(400);
}
},
);
tasks.controller.ts
import { AppDataSource } from '../../index';
import { Task } from './tasks.entity';
import { instanceToPlain } from 'class-transformer';
import { Request, Response } from 'express';
class TasksController {
constructor(private taskRepository = AppDataSource.getRepository(Task)) {}
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
//#ts-ignore
public async getAll(req: Request, res: Response): Promise<Response> {
let allTasks: Task[];
try {
allTasks = await this.taskRepository.find({
order: {
date: 'ASC',
},
});
allTasks = instanceToPlain(allTasks) as Task[];
res.json(allTasks).status(200);
} catch (_errors) {
return res.json({ error: 'Internal Server Error' }).status(500);
}
}
}
export const tasksController = new TasksController();
Hi everyone, I wish you all a very Happy New Year.
I have a question, While accessing the Task Entity, I have been getting error in the tasks.controller.ts file and
TypeError: Cannot read properties of undefined (reading
'getRepository')
What can be the actual Issue? What difference soes it make if I Instantiate the class in tasks.controller.ts file instead of the tasks.router.ts file? coz it is working if I Instantiate the TasksController class inside the route

Struggling to find memory leak. NodeJS, Express, MongoDB

so I have a NodeJS with Express configured for API calls to get data from my backend Database.
Everything works great except there is a memory leak that I can't seem to solve no matter what I do. I have narrowed it down to a MongoDB fetch call, that grabs data from MongoDB.
Since the Data being sent is the same, and to avoid multiple requests to MongoDB, I created a top level variable that fetches that data so on a request it sends that, vs doing a fetch constantly as it would be 1000's of requests every minute.
I have also set the --max_old_space_size variable to 4096 and higher at 8192 and will eventually crash also.
Below is the fetch code.
//router get apis.js
import { Model } from '../../dbmodels/models.js';
let data = null;
// Bot interval
setInterval(async () => {
try {
data = await tokenModel.find({
$or: [
{ "currentRanks.minuteTokenRank": {$lt: 51} },
{ "currentRanks.fiveMinuteTokenRank": {$lt: 51} },
{ "currentRanks.fifteenMinuteTokenRank": {$lt: 51} },
{ "currentRanks.thirtyMinuteTokenRank": {$lt: 51} },
{ "currentRanks.hourlyTokenRank": {$lt: 51} },
{ "currentRanks.dailyTokenRank": {$lt: 51} },
{ "currentRanks.weeklyTokenRank": {$lt: 51} }
]
}).lean();
} catch (error) {
console.error(error);
return;
}
}, 45000);
export async function main(req, res) {
let dataRes = data;
try {
res.status(200).json(dataRes);
dataRes = null;
} catch {(err) => {
res.status(500).json({ message: err.message })
console.log('err', err.message)
}
}
//console.log('Get Top Data')
}
//main server.js file
import dotenv from "dotenv"
dotenv.config()
import express from 'express';
const app = express();
import { createServer } from 'https';
import { createServer as _createServer } from 'http';
import { readFileSync } from 'fs';
import compression from "compression";
import pkg from 'mongoose';
const { connect, connection } = pkg;
import cors from 'cors';
import auth from './utils/auth.js'
connect(process.env.DB_URL);
let mongoClient = connection;
mongoClient.on('error', (error) => console.error(error));
mongoClient.once('open', () => console.log(`Cncted to DB ${mongoClient.name}`));
app.use(compression());
app.use(cors({ origin: ['http://localhost:3000']}));
// Apis route
import apis from './apis/apis.js';
app.use('/api', auth, apis);
//listen both http & https
const httpServer = _createServer(app);
const httpsServer = createServer({
key: readFileSync('redacted.pem'),
cert: readFileSync('redacted.pem'),
}, app);
httpServer.listen(3000, () => console.log('Server Started port 3000'));
httpsServer.listen(3001, () => {
console.log('HTTPS Server running port 3001')
})
So looks like I was able to find the leak. It wasn't with any of the API's I posted. But a hidden one that I use or Web3. There is a known bug on the web3 package leaving connections open.
This is tied to the data I am retrieving above which is why it seemed to me like it was this API, but further troubleshooting found the real issue.
Here is the issue in case anyone uses web3 packages and runs into similar.
https://github.com/web3/web3.js/issues/3042#issuecomment-663622882

i try to upload file in my mocha nodejs test but i got [Object null prototype] { file: { ... }}

i find evrywhere solution white :
app.use(bodyParser.urlencoded({extended: true}));
i can use
JSON.stringify(req.files)
but im sur having a way to fix my problem
my mocha test :
it('a file', async function () {
const body = { pseudo: 'user', password: 'test#123', mail: 'supermail' };
const response = await fetch(hostName + '/authentication/register', {
method: 'post',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' }
})
const usr = await response.json();
request.post('/usrAction1/doc')
.field('token', usr.token)
.attach('file', 'test/test.pdf')
.end(function (err, res) {
if (err) {
console.log(err)
}
console.log(res.status) // 'success' status
});
});
and my rout handler :
router.post('/doc', async (req, res) => {
console.log('req.files');
console.log(req.files)
})
also my server.js:
import express from 'express'
import authentication from './src/login.js'
import './global/host.js'
import bodyParser from 'body-parser'
import cors from "cors"
import verifyToken from './middleware/auth.js'
import { userAction1, userAction2 } from './src/userAction.js'
import verifyLevel from './middleware/level.js'
import fileUpload from 'express-fileupload';
export default function myApp() {
const whitelist = [/http:\/\/localhost:*/, /http:\/\/127.0.0.1:*/]
const corsConfig = { origin: whitelist }
const app = express();
const port = hostPort;
//json encoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())
app.use(cors(corsConfig))
// enable files upload
app.use(fileUpload({
createParentPath: true
}));
app.use('/usrAction1', userAction1())
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
return app;
}
myApp();
but don't to work for me .
i also test white external client server who juste runing a form and send it to my tested adress and
do the same [Object null prototype]
thank u for evry litel help
i waiting of some help i using this magique code i found somwhere on stackoverflow:
req.files && Object.keys(req.files)?.map((obj, idx) => { console.log(req.files['file'].data) })
if somone have a better idea i waiting .
thank to all

Nextjs when using promises inside app.prepare().then() Error: getaddrinfo ENOTFOUND undefined

I am setting up a new server using nextJS and I would like to have a graphql endpoint within my app. This setup works perfectly when using 2 servers, however we only have access to 1 server so now I am moving the graphql server into my express server.
Server.js
import express from 'express'
import next from 'next'
import pg from 'pg'
import expressGraphiqlMiddleware from 'express-graphiql-middleware'
import requestProxy from 'express-request-proxy'
import ConnectionFilterPlugin from 'postgraphile-plugin-connection-filter'
const { ApolloServer } = require('apollo-server-express')
const { makeSchemaAndPlugin } = require('postgraphile-apollo-server')
....
app
.prepare()
.then(async () => {
const { schema, plugin } = await makeSchemaAndPlugin(
pgPool,
'public', // PostgreSQL schema to use
{
appendPlugins: [ConnectionFilterPlugin],
graphiql: true,
// PostGraphile options, see:
// https://www.graphile.org/postgraphile/usage-library/
}
)
...
const expressServer = express()
...
expressServer.get('/p/:id', (req, res) => {
const actualPage = '/post'
const queryParams = { title: req.params.id }
app.render(req, res, actualPage, queryParams)
})
...
//Scraping Tools
scrape(expressServer)
expressServer.get('*', (req, res) => {
return handle(req, res)
})
...
expressServer.listen(3001, err => {
if (err) throw err
console.log('> Ready on http://localhost:3001')
})
})
.catch(ex => {
console.info('error')
console.error(ex.stack)
process.exit(1)
})
I have simplified my example, but the key point here is: if I remove the async/await/promise from the current then() I will get the following error:
Error: Apollo Server requires either an existing schema, modules or typeDefs
As for course, schema and plugin will not be defined, however if I enable these promises I get: as the error
ror.js
generate SourceMap
error
Error: getaddrinfo ENOTFOUND undefined
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)
Waiting for the debugger to disconnect...

res.jwt is not a function - NodeJS Express

I kept getting
res.jwt is not a function
I have installed jwt-express and import it like so
import jwt from 'jwt-express'
This is my auth.js
import Account from '../services/account.js'
import env from 'dotenv'
import _ from 'lodash'
const dotenv = env.config();
module.exports = {
/**
* Process the user login, generating and returning a token if successful.
*
* #return {res}
*/
async login(req, res, next) {
try {
let origin = req.headers.origin;
let accounts = await Account.getAccounts();
let account = _.find(accounts, {
'email_address' : req.body.username,
'password' : req.body.password
});
if (!account) {
res.send('Username/Password Wrong');
}
// res.send(account);
let authentication = res.jwt({
'email': account.email_address,
'id': account.account_id
});
res.send(authentication);
} catch (error) {
next(error)
}
}
};
index.js
import express from 'express'
import favicon from 'serve-favicon'
import path from 'path'
import bodyParser from 'body-parser'
import bluebird from 'bluebird'
import jwt from 'jwt-express'
import env from 'dotenv'
//Controllers
import fortinetController from './controllers/fortinet'
import authController from './controllers/auth.js'
//Logger
import logger from './config/logger.js'
//Constant
const router = express.Router();
const app = express();
const PORT = 3000;
const dotenv = env.config();
Promise = bluebird;
app.use(bodyParser.urlencoded({extended: true }));
app.use(bodyParser.json());
app.use(router)
app.use(express.static('public'))
app.use(favicon(path.join(__dirname,'public','favicon.ico')))
app.use(jwt.init('CARWASH', {cookies: false }));
router.get('/', (req,res) => {
res.send('Welcome to the backend provisioning daemon to program FortiManager')
});
router.post('/login', authController.login);
//Fortinet
router.post('/fortinet/login', fortinetController.login);
router.post('/fortinet/getSessionTimeOut', fortinetController.getSessionTimeOut);
router.post('/fortinet/logout', fortinetController.logout);
//Error handling function
app.use((err,req,res,next) => {
console.error(err.stack)
res.status(500).send(`Red alert! Red alert!: ${err.stack}`)
logger.error(`${req.method} ${req.url} - ${err.log || err.message}`);
});
app.listen(PORT, () => {
console.log(`Your server is running on ${PORT}`)
}
);
How can I debug this?
Update
I've tried to add this
console.log(jwt);
I got
[nodemon] 1.17.3
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `babel-node ./index.js`
{ active: [Function: active],
clear: [Function: clear],
create: [Function: create],
init: [Function: init],
options:
{ cookie: 'jwt-express',
cookieOptions: { httpOnly: true },
cookies: false,
refresh: true,
reqProperty: 'jwt',
revoke: [Function: revoke],
signOptions: {},
stales: 900000,
verify: [Function: verify],
verifyOptions: {} },
require: [Function: require],
valid: [Function: valid] }
Your server is running on 3000
You are not configuring express-jwt correctly.
You are using express-jwt completely wrong.
Let's walk through each point.
I'm not sure why you think you need to call jwt.init(...) when the documentation here states to simply do: jwt(...). So you'll need to make the following changes:
Change
app.use(jwt.init('CARWASH', {cookies: false }));
To
app.use(jwt({secret: 'CARWASH'}));
There does not exist a cookies options, not sure where you got that from.
Now express-jwt will only handle verification of the JWT. It does not generate JWT for as you are trying to do in your auth.js.
In order to generate JWT, you will need another module: jsonwebtoken. You will then use the module in your auth.js like so:
import jwt from "jsonwebtoken";
// ...
module.export = {
async login(req, res, next) {
try {
// ... auth logic omitted
// Here we generate the JWT
// Make sure the JWT secret is the SAME secret you used for express-jwt
let authentication = jwt.sign({
'email': account.email_address,
'id': account.account_id
}, 'CARWASH');
res.send(authentication);
}
catch (error) {
next(error);
}
}
}

Categories