Can't set headers after they are sent when using EventEmitter - node.js

Here is my code:
const cluster = require('cluster');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const queueMgr = require('./lib/queueManager');
const eventEmitter = new (require('events')).EventEmitter();
const app = express();
app.use(cors());
app.use((req, res, next) => {
if (channel) {
next();
}
queueMgr
.connect()
.then(ch => {
channel = ch;
next();
})
.catch(err => {
console.log(err);
res
.status(500)
.json({ message: 'Something Went Wrong' })
.end();
});
});
app.get('/', (req, res) => {
const taskId = Date.now() + '' + process.hrtime()[1];
const worker = queueMgr.launchWorker();
eventEmitter.once(taskId, msg => {
console.log(taskId);
res
.status(200)
.send({ message: msg, status: true })
.end();
});
queueMgr.addTaskToQueue(channel, config.taskQueueName, taskId, 'testmsg', 1000);
});
app.listen(PORT, () => {
console.log(`Process ${process.pid} listening on port ${PORT}`);
});
Here, for each GET / request, I create a unique taskId, attach it to the `eventEmitter.once. Then I attack it to a task queue.
Internally, I launch a worker to do the task then emit the taskId event, and on reciever, I send the response for that taskId.
Now, my first request works fine. However, my second and subsequest requests fail with the error
Error: Can't set headers after they are sent.

Be aware that next() function can be called two times.
Here
if (channel) {
next();
}
And then here
.then(ch => {
channel = ch;
next();
})
Maybe the second call of next() should not be done when the first one was called, if so, try to use return statement.
if (channel) {
return next();
}

Related

How to use express middleware on router level

I'm trying to add a simple middleware function for every request on the router level.
The docs are stating:
a middleware function with no mount path will be executed for every
request to the router
In my application I have only one router with one endpoint that is listening for every request and I'm placing my middleware function above this endpoint, but the middleware never gets launched.
Express setup:
const initializeExpress = (): void => {
const port = process.env.PORT;
const app = express();
app.use(helmet());
app.use(express.json());
app.use(cors());
app.use('/api', Router);
app.listen(port, () => {
console.log(`Listening for requests at http://localhost:${port}`);
});
};
My router code:
const Router = express.Router();
Router.use((req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
});
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?',
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);
What am I doing wrong here?
On which route the middleware will be active, you need to define it.
There's two way to make this
First, call middleware before the your route:
const Router = express.Router();
const myMiddleware = (req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
}
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?',
myMiddleware(), //Call middleware here
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);
Second, calling middleware for all routes that you define:
const Router = express.Router();
const myMiddleware = (req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
}
Router.use('/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?', myMiddleware());
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?'
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);

Node.js: Is there a way to mock the prometheus pushgateway server?

I am using node.js package prom-client to try to push different custom metrics to the Prometheus Pushgateway server.
The code is working and now I am writing the test code for the functionality. I wonder whether there is a way to mock the Prometheus Pushgateway server?
I had tried to use a normal express API server (like the following)
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
let data = null;
// ROUTES FOR OUR API
// =============================================================================
const router = express.Router();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
router.get('/', (req, res) => {
console.log("pushgateway server: get to '/' received");
res.json(data);
});
router.post('/', (req, res) => {
console.log("pushgateway server: post to '/' received");
console.log(req.body);
data = req.body;
res.send('POST request to the homepage');
})
app.use(router);
// API JSON BASED ERRORS
// =============================================================================
app.use((err, req, res, next) => {
if (req.xhr) {
console.error(err);
res.status(500).send({ err: 'Something failed!' });
} else {
next(err);
}
});
app.use((err, req, res, next) => { // eslint-disable-line
console.error(err);
res.status(500).send({ err: err.message });
});
// START THE SERVER
// =============================================================================
const port = 9876;
const server = app.listen(port, '0.0.0.0');
console.log(`Prometheus Pushgateway Mock Server is listening on port ${port}`);
// SHUTDOWN HANDLING
// =============================================================================
// Nodemon Signal Handling
process.once('SIGUSR2', () => {
console.log('Received kill signal, attempting gracefully close');
server.close(() => {
console.log('Closed out remaining connections');
process.kill(process.pid, 'SIGUSR2');
});
setTimeout(() => {
console.error('Timeout, forcefully shutting down');
process.kill(process.pid, 'SIGUSR2');
}, 3000);
});
module.exports = server;
but it did not work -- when I call gateway.pushAdd() on my test code, the server did not receive any post message.
Can anyone give me some hints on how to do this (mock the prometheus pushgateway server)?
There are different options out there to mock a third party http server. Here are 3:
Nock
Dyson
Stubborn
Disclaimer: I am the maintainer behind stubborn.
Nock and Dyson are the most popular ones, they have pro and cons. Stubborn is more recent and tries to cover some limitations of Nock and Dyson.
You can checkout this reddit post first comment I wrote for a comparison between the 3 solutions.
So I have solved the problem by creating a simple http server.
The following is the code of the server:
const http = require('http');
const body = [];
let text = null;
let path = null;
function createServer(port) {
return http.createServer()
.on('request', (req, res) => {
if (req.method === 'POST' || req.method === 'PUT') {
path = req.url;
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
text = Buffer.concat(body).toString();
res.end(`${req.method} success`);
}).on('error', (err) => {
console.error(err);
});
} else if (req.method === 'GET') {
res.end(JSON.stringify({ path, text }));
} else if (req.method === 'DELETE') {
path = null;
text = null;
res.end('DELETE success');
}
}).on('error', (err) => {
console.log(`Server error: ${err}`);
})
.listen(port, '0.0.0.0');
}
module.exports = (createServer);
The server accepts POST/PUT/DELETE/GET requests, to handle the pushAdd()/push()/delete() functions of the gateway to handle the metric data, as well as checking the push data on the mocked pushgateway.
Also, the request.url and the text data pushed to the server are saved and passed to the test program for verification.
The following is one of the test cases (using Mocha+Chai):
describe('Check adding custom push count metric', () => {
it('Check connection: should return - network status: 200, result contains custom count metric string', async () => {
metricInstance = promMetric.createCustomPushMetric({
name: 'test_counter',
help: 'Used to test the pushgateway for custom counter metrics',
type: 'counter',
jobName: 'custom-metric-pushgateway-counter',
groupings: { key: 'test', type: 'customMetric' },
});
await promMetric.addPushMetric(metricInstance, 0.879);
await sleep(500);
const result = await chai.request(mockPgwServer).get('/');
expect(result).to.have.status(200);
const json = JSON.parse(result.text);
expect(json.path).to.equal('/metrics/job/custom-metric-pushgateway-counter/key/test/type/customMetric');
expect(json.text).to.match(/# HELP Push_gateway_test_test_counter Used to test the pushgateway for custom counter metrics/);
expect(json.text).to.match(/# TYPE Push_gateway_test_test_counter counter/);
expect(json.text).to.match(/Push_gateway_test_test_counter 0.879/);
});
});
The "path" and "text" stored in the server when the metric is posted from the client are sent back to the client when queried and verified.

Node.js DELETE Request return wrong response

I'm making a DELETE request on my nodejs server, but the response is wrong.
When i try to send a DELETE request at my localhost server, it returns success: 0, message: "Record Not Found"
but when i go to check in the database, the parameters are gone
index.js
require("dotenv").config();
const express = require("express");
const app = express();
const userRouter = require("./api/users/user.router");
var cors = require('cors');
var corsOptions = {
origin: "http://localhost:3000"
};
app.use(cors(corsOptions));
app.use(express.json());
app.use("/api/", userRouter);
const port = process.env.PORT || 4000;
app.listen(port, () => {
console.log("server up and running on PORT :", port);
});
user.router.js
const router = require("express").Router();
const {
deleteUtenti
} = require("./user.controller");
router.delete("/", deleteUtenti);
module.exports = router;
user.controller.js
const {
deleteUtenti
} = require("./user.service");
module.exports = {
deleteUtenti: (req, res) => {
const data = req.body;
deleteUtenti(data, (err, results) => {
if (err) {
console.log(err);
return;
}
if (!results) {
return res.json({
success: 0,
message: "Record Not Found"
});
}
return res.json({
success: 1,
message: "user deleted successfully"
});
});
}
};
user.service.js
const pool = require("../../config/database");
module.exports = {
deleteUtenti: (data, callBack) => {
pool.query(
`delete from utenti where email = ?`,
[data.email],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
}
};
is it a problem of the code or of the server?
A few days ago the same thing happened to me with another function but to solve it was enough to recreate the table in the database, I tried to do the same thing but it didn't work
I Just found the problem, results[0] on user.service.js did not read the response from the database, so to do some tests I changed it to results where all the response from the database came out and I saw that I just had to take the affectedRows response from the database, so I changed it to results.affectedRows and now everything works as it should.

POST request not coming through (MERN)

I'm using the MERN stack to build an application for the first time.
In order to log HTTP requests I use "morgan".
I managed to send data to mongodb which seems to be working fine. The problem is that my post request is not coming through. It says "pending" for 4 minutes, then fails.
Here's what I think is the relevant part of my code:
"server.js":
const express = require("express");
const mongoose = require("mongoose");
const morgan = require("morgan");
const path = require("path");
const cors = require("cors");
const app = express();
const PORT = process.env.PORT || 8080;
const routes = require("./routes/api");
const MONGODB_URI =
"...";
mongoose.connect(MONGODB_URI || "mongodb://localhost/app", {
useNewUrlParser: true,
useUnifiedTopology: true
});
mongoose.connection.on("connected", () => {
console.log("Mongoose is connected.");
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cors());
app.use(morgan("tiny"));
app.use("/api", routes);
app.listen(PORT, console.log(`Server is starting at ${PORT}`));
Then I've put my routes into another file "api.js":
const express = require("express");
const router = express.Router();
const Lane = require("../models/lanes");
router.get("/", (req, res) => {
Lane.find({})
.then(data => {
res.json(data);
console.log("Get request successful!");
})
.catch(error => {
console.log("Error: ", error);
});
});
router.post("/save", (req, res) => {
const data = req.body;
const newLane = new Lane();
newLane.collection.insertMany(data, err => {
if (err) {
console.log(err);
} else {
console.log("Multiple docs inserted");
}
});
});
module.exports = router;
I'm using axios to send the request. This happens after submitting a form within my application.
reducer function:
const reducer = (state, action) => {
switch (action.type) {
case "add":
axios({
url: "http://localhost:8080/api/save",
method: "POST",
data: [...state, { id: uuid(), title: action.title, tasks: [] }]
})
.then(() => {
console.log("Data has been sent to the server");
})
.catch(() => {
console.log("Internal server error");
});
return [...state, { id: uuid(), title: action.title, tasks: [] }];
The reducer is being used by my context provider component, which looks like this:
export function LanesProvider(props) {
const [lanes, dispatch] = useReducer(reducer, defaultLanes);
return (
<LanesContext.Provider value={lanes}>
<DispatchContext.Provider value={dispatch}>
{props.children}
</DispatchContext.Provider>
</LanesContext.Provider>
);
}
The "add" method inside my reducer is being called when submitting a form inside another component.
Please let me know if I can add anything to my question that would help.
Thank you in advance!
you are not sending any response back to client. Try to modify post method like
router.post("/save", (req, res) => {
const data = req.body;
const newLane = new Lane();
newLane.collection.insertMany(data, err => {
if (err) {
console.log(err);
res.send(err)
} else {
console.log("Multiple docs inserted");
res.send("Multiple docs inserted")
}
});
});

Why am I getting a 401 Unauthorized Error when hitting my protected endpoint at /api/protected?

I am using JWT to generate a token for access control. I can hit /api/auth/login and get back the token, however, when attempting to hit /api/protected with a GET request, I get 401 Unauthorized.
I've looked through SO and haven't found anything specific although it seems like a routine issue, maybe. I have tried moving the route around in the server.js file to see if that is the issue . I have removed the preceeding slash from the route (from /api/protected to api/protected) and using the latter I get back a bunch of html due to, I think, the app.use(express.static....
I am using Postman to test it but i'm not sure what I'm missing here. I have also made sure to set the authorization to Bearer Token in Postman.
'use strict';
const { Strategy: LocalStrategy } = require('passport-local');
// Assigns the Strategy export to the name JwtStrategy using object destructuring
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
const { User } = require('../users/models');
const { JWT_SECRET } = require('../config');
const localStrategy = new LocalStrategy((username, password, callback) => {
let user;
User.findOne({ username })
.then(_user => {
user = _user;
if (!user) {
// Return a rejected promise so we break out of the chain of .thens.
// Any errors like this will be handled in the catch block.
return Promise.reject({
reason: 'LoginError',
message: 'Incorrect username or password'
});
}
return user.validatePassword(password);
})
.then(isValid => {
if (!isValid) {
return Promise.reject({
reason: 'LoginError',
message: 'Incorrect username or password'
});
}
return callback(null, user);
})
.catch(err => {
if (err.reason === 'LoginError') {
return callback(null, false, err);
}
return callback(err, false);
});
});
const jwtStrategy = new JwtStrategy(
{
secretOrKey: JWT_SECRET,
// Look for the JWT as a Bearer auth header
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
// Only allow HS256 tokens - the same as the ones we issue
algorithms: ['HS256']
},
(payload, done) => {
done(null, payload.user);
}
);
module.exports = { localStrategy, jwtStrategy };
'use strict';
//How does order of code affect how it works?
// YES
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const morgan = require('morgan');
const passport = require('passport');
const path = require('path');
const { router: usersRouter } = require('./users');
const { router: authRouter, localStrategy, jwtStrategy } = require('./auth');
mongoose.Promise = global.Promise;
// Is this needed if dotenv is in this file also?
const { PORT, DATABASE_URL } = require('./config');
const app = express();
// Logging
app.use(morgan("common"));
// const logRequest = (req, res, next) => {
// const now = new Date();
// console.log(
// `local log - ${now.toLocaleDateString()} ${now.toLocaleTimeString()} ${req.method} ${req.url}`
// );
// next();
// }
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE');
if (req.method === 'OPTIONS') {
return res.send(204);
}
next();
});
passport.use(localStrategy);
passport.use(jwtStrategy);
//app.use(logRequest);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/api/users/', usersRouter);
app.use('/api/auth/', authRouter);
app.use("/api/items", require('./routes/api/items'));
// protected route that needs a valid JWT for access
const jwtAuth = passport.authenticate("jwt", { session: false });
// route to handle static content ie.e *.jpg
app.use(express.static(path.join(__dirname, "client", "build")));
app.get('/api/protected', jwtAuth, (req, res) => {
return res.json({
data: 'Hello World'
});
});
// have react client handle all additional routes
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
let server;
function runServer(DATABASE_URL, port = PORT) {
return new Promise((resolve, reject) => {
// How is DATABASE_URL used? What is the value? Is it referencing
// DATABASE_URL?
mongoose.connect(DATABASE_URL, { useNewUrlParser: true, useFindAndModify: false }, (err) => {
console.log("Success");
if (err) {
return reject(err);
}
server = app.listen(port, () => {
console.log(`Your app is listening on port ${PORT}`);
resolve();
})
.on('error', (err) => {
mongoose.disconnect();
reject(err);
});
});
});
}
function closeServer() {
return mongoose.disconnect()
.then(() => new Promise((resolve, reject) => {
console.log("Closing server");
server.close((err) => {
if (err) {
return reject(err);
}
resolve();
});
}));
}
if (require.main === module) {
runServer(DATABASE_URL)
.catch(err => console.error(err));
}
module.exports = { app, runServer, closeServer };
enter code hereI am expecting to get back a string that says "Hello World" just to make sure i'm hitting the endpoint correctly. Instead I get the 401 error, GET /api/protected HTTP/1.1" 401enter code here

Resources