I am currently trying to create an API handler that will ensure that all my requests follow the same template using express in Node JS. So I have defined the structure of the response and registered it as middleware in my express application. I am however unable to get the responses to work.
I have tried importing express into the external class for it to use the res parameter for the middleware. I have also tried it without. I have tried expressing it with middleware parameters such as (req, res, next) which didn't work either. So I am unsure what to try next.
The external handler is as follows:
exports.success = (message, results, statusCode) => {
return {
message,
error: false,
code: statusCode,
results
};
};
I however tried the following as well which didn't work:
exports.success = (message, results, statusCode) => {
return res.json({
message,
error: false,
code: statusCode,
results
});
};
I tried this as well which didn't work:
exports.success = (message, results, statusCode) => {
return (req, res, next) => {
res.json({
message,
error: false,
code: statusCode,
results
});
next();
}
};
I have implemented it in the middleware as follows:
this.app.use((req, res, next) => {
res.success = responseHandler.success;
next();
});
Is it not possible to implement what I am trying (which I doubt)? I have a feeling I am just returning the wrong thing but I am not sure what it is.
I think you should be able to get this working, I've tested a simple version of what you're trying to achieve.
I've used a bind() call to ensure the context is correct for the success() call, also we'll use the function declaration rather than an arrow function in the response-handler module for the same reason.
index.js
const express = require("express");
const port = 3000;
const app = express();
const bodyParser = require('body-parser')
const responseHandler = require('./response-handler.js')
app.use(bodyParser.json());
app.use((req, res, next) => {
res.success = responseHandler.success.bind(res);
next();
});
app.get('/success', function(req,res,error) {
res.success("Success - yay!!", { foo: 'bar' }, 418);
})
app.listen(port);
console.log(`Serving at http://localhost:${port}`);
response-handler.js
exports.success = function(message, results, statusCode) {
this.json({
message,
error: false,
code: statusCode,
results
});
};
Related
Issue
I am trying to pass my variable, req.app.locals.userId down to the /getuser route. Now I have tried with and without app in req.app.locals.userId. The issue is the variable keeps returning undefined in my /getuser route. I have tried to run the middleware directly in the router and the variable returns correctly, but it seems like req.app.locals does not work when I am using it as a middleware. req.app.locals returns an Object which means that req.app.locals works.
const verifyJWT = (req, res, next) => {
const token = req.headers["x-access-token"]
if (!token) {
res.send("We need a token")
} else {
jwt.verify(JSON.parse(token), "jwtSecret", (err, decoded) => {
if (err) {
res.json({ auth: false, message: "You failed to authenticate" })
} else {
req.app.locals.userId = decoded.id; //This is the variable I am trying to get
next();
}
})
}
}
router.route("/getuser", verifyJWT).get((req, res) => {
console.log(req.app.locals.userId) // <--- Returns undefined
});
I just can't see what I am doing wrong.
My index file
const express = require('express');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const bp = require('body-parser');
const auth = require("./routes/auth");
const PORT = process.env.PORT || 3003;
const app = express();
app.use(cors());
app.use(express.json());
app.use("/auth", auth);
app.get("/", (req, res) => {
console.log("/");
});
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Index file and your controller appear fine once working on your route file.
router.route("/getuser", verifyJWT).get((req, res) => {
console.log(req.app.locals.userId) // <--- Returns undefined
});
Instead of this try to get req...
router.get("/getuser", verifyJWT, (req, res, next) => {
console.log(req.app.locals.userId);
});
router.route() only takes one argument (the path to match).
Instead, use something like this:
router.route('/getuser').use(verifyJWT);
Or the mostly equivalent:
router.use('/getuser', verifyJWT);
Environment: node.js, Express
I'm attempting to use an error handling pattern based on work done by Valeri Karpov (creator of Mongoose). He explains the pattern that he uses in this article, The 80/20 Guide to Express Error Handling.
In the simplified server below I can successfully feed errors through to my error handling middleware.
const express = require('express');
const app = express();
app.set('view engine', 'ejs');
app.get('/', async function(req, res, next) {
let catStatus = false;
function answer(X) {
return new Promise( function(resolve, reject) {
if(X) {
resolve('cat does exist');
} else {
reject('whoops, cat does not exist');
}
});
}
answer(catStatus)
.then( function(data) {
res.render('index', { output: data });
})
.catch( function(error) {
next(new Error(error));
});
});
app.use( function(error, req, res, next) {
res.render('error', { error });
});
app.listen(8080, function(){
console.log('listening on port 8080');
});
However I'm stuck on how to implement his wrapper pattern with my basic setup. Does the code inside my '/' endpoint go inside of his '*' endpoint?
If so does anything go inside of his function wrapAsync(fn)? Should I just delete his 2 comment lines and leave it as is?
app.get('*', wrapAsync(async function(req, res) {
await new Promise(resolve => setTimeout(() => resolve(), 50));
// Async error!
throw new Error('woops');
}));
app.use(function(error, req, res, next) {
// Gets called because of `wrapAsync()`
res.json({ message: error.message });
});
app.listen(3000);
function wrapAsync(fn) {
return function(req, res, next) {
// Make sure to `.catch()` any errors and pass them along to the `next()`
// middleware in the chain, in this case the error handler.
fn(req, res, next).catch(next);
};
}
I think the * endpoint in app.use() is just a wildcard saying to route all incoming requests to that code, similar to the way your / is working.
But yes, you are understanding correctly. Basically, he is saying that any middleware which makes asynchronous requests should use this wrapAsync function. You can remove the comment lines in the wrapAsync implementation if you want.
With the wrapAsync function, you can pass your own async middleware functionality into this wrapper, and it will make sure to call your async middleware and .catch() the promise to call next, so that you don't have to worry about that detail when writing code. You can simply throw errors, and then async wrapper will handle the Express requirement of calling next() when async code fails.
app.use("/", (req, res, next) => {
verifyRequestorPermissionsAsync(req.params)
.catch(err => {
return next(err);
});
});
With the wrapAsync middleware, you will automatically call next with the rejected error (if there is one), so you can clean up the code quite a bit (as well as avoid accidentally forgetting to call next on a rejection).
app.use("/", wrapAsync(async(req, res, next) => {
await verifyRequestorPermissionsAsync(req.params);
}));
These two docs / articles will help clear things up a lot, if I am guessing correctly where the confusion is coming from:
Why do we even care about next and passing a value into that function: https://expressjs.com/en/guide/error-handling.html
What is this wrap function doing, which returns a function that calls your function? https://eloquentjavascript.net/03_functions.html#h_hOd+yVxaku
There's single api application like this:
const express = require('express')
const app = express()
const router = require('express').Router()
...
route.post('/dogs', (req, res, next) => {
const dog = new Dog() // it defined in real app
dog.validate() // error happens here
.then(() => {
return res.status(201)
})
// [1]
})
...
app.use('/api/v1', router)
app.use(notFoundErrorHandler)
app.use(globalErrorHandler)
function notFoundErrorHandler (req, res, next) {
res.status(404)
res.send({error: 'Not found'})
}
function globalErrorHandler (err, req, res, next) {
if (err) {
res.status(err.status || 500)
res.json({error: err.message || err})
}
}
If there's validation error it won't pass to the globalErrorHandler, but catching and rethrowing error solves the problem [1]:
.catch(err => { return next(err) })
Does this behaviour is normal for mongoose with not complete Promise implimentation, or it could be implimentated in another way?
It doesn't raise error when using other methods like save, find, etc.
Thats normal, yes and has nothing to do with mongoose but with Express.
Express doesn't handle unhanded exceptions implicit, you will have to handle them explicit. So in each route you will have to catch any errors and pass them to next.
I know that this may be frustrating sometimes so I would suggest that you create a route manager of sorts that will register each route with an action handler and a try/catch pattern.
There are many examples out there, here is one simple I like.
var dogHandler = require('dogHandler')
var serverRoutes = [{
path: "dogs",
method: "post",
action: dogHandler
}]
serverRoutes.forEach(route => {
app[route.method](route.path, (request, response, next) => {
route.action(request, response)
.then(() => next)
.catch(err => next(err));
});
});
I tried to use middleware errorhandler, but doesn't work, even i set process.env.NODE_ENV ='development'
below is the server code:
var express = require('express');
var app = express();
var errorhandler = require('errorhandler');
var notifier = require('node-notifier');
process.env.NODE_ENV = 'development'; //just purposely do this, see if it can work
if (process.env.NODE_ENV == 'developmet') {
app.use(errorhandler({ log: errorNotification }));
}
function errorNotification(err, str, req) {
var title = 'Error in' + req.method + '' + req.url;
notifier.notify({
title: title,
message: str
});
}
app.get('/', function (req, res, next) {
nonexist(); //the error is still captured Native node.js not errorhandler
res.send('this is home page!');
next();
});
app.listen(1338);
no matter what kind of options i tried in errorhandler, it still doesn't work.
can anyone help me to check any setting is wrong?
Error handling is declared after all your other routes. Express moves through routes from top to bottom or left to right (if you imagine all your code on one line).
You can capitalize on this by putting a splat route after all your other routes, and it will be activated if no other exact route matches. This is how you can do an error 404 page.
It's why you build your routes like this (which will prepare you for React Router 'switch component', if you move into React coding):
GET /test/:slug
GET /test
GET /
Here is an example of a splat route and following that, your error handler middleware:
// Try switching the order of these first two
app.get('/', async (req, res, next) => {
return res.status(200).send('test')
})
app.get('*', async (req, res, next) => {
return res.status(404).send('error 404') // res.render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if next() is called with a parameter, which can be anything,
// this middleware will fire
res.status(500).send('error 500') // res.render('error/500')
throw err
})
// Try replacing your default route with this now
app.get('/', async (req, res, next) => {
return next('Extreme detonations')
})
You do not need the async functions as I have shown here, but I do it as a matter of convention so I can always slap await in there. I use explicit returns to prevent any issues with sending headers after they are already sent, and because async functions return promises, so explicit return will resolve them.
All my routes generally look like this:
app.get('/admin', async (req, res, next) => {
try {
if (!req.user) throw 'garbageUser'
const poop = await something()
return res.render('template', {
data: obj,
bonusData
})
} catch (e) {
if (e === 'garbageUser') {
log.add(`illegal: ${req.originalUrl} from ${sniffer.getClientIp(req)}`)
return res.render('403')
}
return next(e)
}
})
This should hopefully be informative for you and offer some code to forensically analyze. The Express error handler middleware takes a 4th parameter in the first position called err which contains the value passed into next().
Check out the Express documentation again after studying this, and it will make much more sense :)
To answer your question:
var express = require('express');
var app = express();
// You can add these back now that you understand
// var errorhandler = require('errorhandler');
// var notifier = require('node-notifier');
function handleErrors(error) {
console.log('I'm telling your mom about this: ' + error);
}
app.get('/', function(req, res, next) {
return next('REALLY BAD');
return res.send('this is home page!');
});
// Remember, this must be after all your other routes
app.use(function(err, req, res, next) {
console.log('Problem occurred, we could put logic here...');
console.log('Error was: ' + err);
if (err === 'REALLY BAD') {
handleErrors(err);
}
next();
});
app.listen(1338);
Try commenting this out now return next('REALLY BAD'); and run it again. You should see "this is home page!".
When you call next() with no parameter in it, Express treats it as no error. If you pass any value in, like next(err) or next('Chicken tastes good'), you will see err defined with that value in your error handling middleware.
I'm trying to unit test this piece of code in Mocha:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
I don't know how to get my request inside a Mocha unit test to trigger it.
First I would break out the middleware into its own file/function. As it sits, it's "integrated" with the Express app. So you're not testings only the error middleware, but also the Express app instance to an extent.
With that said, decouple the error middleware from the Express app:
src/middleware/error-handler.js
module.exports = (err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
}
You will still .use() it in the main app.js or wherever you setup Express:
const express = require('express')
const errorHandler = require('./src/middleware/error-handler')
const app = express()
app.use(errorHandler)
But now we're free of the Express dependency and we have a simple function we can isolate and test. Below is a simple test with Jest which you can easily adjust to work with Mocha.
__tests__/middleware/error-handler.test.js
const errorHandler = require('../../src/middleware')
describe('middleware.ErrorHandler', () => {
/**
* Mocked Express Request object.
*/
let req
/**
* Mocked Express Response object.
*/
let res
/**
* Mocked Express Next function.
*/
const next = jest.fn()
/**
* Reset the `req` and `res` object before each test is ran.
*/
beforeEach(() => {
req = {
params: {},
body: {}
}
res = {
data: null,
code: null,
status (status) {
this.code = status
return this
},
send (payload) {
this.data = payload
}
}
next.mockClear()
})
test('should handle error', () => {
errorHandler(new Error(), req, res, next)
expect(res.code).toBeDefined()
expect(res.code).toBe(500)
expect(res.data).toBeDefined()
expect(res.data).toBe('Something broke!')
})
})
In your route you can pass an error object in the next parameter. For example:
app.get('/some-route', (req, res, next) => {
const error = {....};
next(error);
});
or just throw an error:
app.get('/some-route', (req, res, next) => {
const error = {....};
throw new Error(error);
});