I'm developing a rest server on Node JS with Express.
I'm trying to wrap all my endpoints in try\catch block, so a central point of error will response back to the sender with details.
My problem that response (res instance) is alive for each of the endpoints methods, but I don't know how to make it global.
try {
app.get('/webhook', function (req, res) {
webhook.register(req, res);
});
app.get('/send', function (req, res) {
sendAutoMessage('1004426036330995');
});
app.post('/webhook/subscribe', function (req, res) {
webhook.subscribe("test");
});
app.post('/webhook/unsubscribe', function (req, res) {
webhook.unsubscribe("test");
});
} catch (error) {
//response to user with 403 error and details
}
There is a library (express-async-errors) which could suit your needs.
This enables you to write async route handlers without wrapping the statements in try/catch blocks and catch them with global error handler.
To make this work you must:
1. Install the express-async-errors package
2. Import package (before the routes)
3. Set up global express error handler
4. Write async route handlers (More info about this)
Example usage:
import express from 'express';
import 'express-async-errors';
const app = express();
// route handlers must be async
app.get('/webhook', async (req, res) => {
webhook.register(req, res);
});
app.get('/send', async (req, res) => {
sendAutoMessage('1004426036330995');
});
app.post('/webhook/subscribe', async (req, res) => {
webhook.subscribe("test");
});
app.post('/webhook/unsubscribe', async (req, res) => {
webhook.unsubscribe("test");
});
// Global error handler - route handlers/middlewares which throw end up here
app.use((err, req, res, next) => {
// response to user with 403 error and details
});
try catch can not catch error asynchronously.
This will work:
app.get('/webhook', function (req, res) {
try {
//enter code here
} catch (error) {
// something here
}
});
But it is local and not the best way.
Good way is make error-handling middleware function. It is global. You need to define it after all app.use() and routes calls.
app.use(function(err, req, res, next) {
// This is error handler
});
You can send the html page with details of error to client as usual.
Also, by default, Express have built-in error handler. The error will be written to the client with stack trace (It does not work in production mode).
this kind of try catch will not catch errors when some third function is called do the best solution will use global exception handler
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.end();
});
also you have to use promises in your end point handlers
that will catch errors in any scope
Related
I am writing a library that requires the user to handle errors generated by my library. The way I do it now is that I tell users to add an error handler after all their routes like so:
const app = express();
app.use(MyLibrary.init())
// <user's routes here>
app.use(MyLibrary.errorHandler())
app.use((err, req, res, next) => {
// user's generic error handler
})
The init function in my lib is as follows:
function init() {
return async (request, response, next) => {
try {
// some logic here that modifies the request object and can throw an error
return next(); // this can also throw an error, or generate an unhandled rejection / error
} catch (err) {
next(err);
}
};
}
The errorHandler function is as follows:
function errorHandler() {
return async (err, request, response, next) => {
try {
if (/*err from MyLibrary*/) {
// do something and send a response
return;
}
next(err);
} catch (err) {
next(err);
}
};
}
My aim is to make it so that the user doesn't need to add app.use(MyLibrary.errorHandler()).
One solution that comes to mind is that in the init function, instead of calling next(err), I can handle my library's error directly. However, errors from MyLibrary can also be generated in any of the API handlers that the user writes (since they can interact with my library via the modified request object). These API handlers may be async.
Is there any solution to this issue?
Thank you
There is a very basic middleware, which I would like to test.
First question is what kind of test I'm using. For my understanding, I cannot write a unit test for this code.
I would call that an integration test. Is that correct?
The second problem ist the test itself: I'm running into a timeout, althoug I've used done().
What am I doing wrong? And is that the correct way to test this middleware?
/middlewares/graphql.js
module.exports = (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With')
if (req.is('application/graphql')) {
req.body = { query: req.body }
}
if (req.method === 'OPTIONS') {
res.sendStatus(200)
} else {
next()
}
}
/tests/middlewares/graphql.js
import nodeMocks from 'node-mocks-http'
import middleware from '../middlewares/graphql'
describe('GraphQL middleware', () => {
it('Should return 200 for valid Content-Type header', (done) => {
const req = nodeMocks.createRequest({
headers: {
'Content-Type': 'application/graphql'
},
body: {
content: 'anything'
},
method: 'OPTIONS'
})
const res = nodeMocks.createResponse()
middleware(req, res, (err) => {
expect(res.statusCode).toEqual(200)
expect(res.body.content).toEqual('anything')
expect(err).toBeNull()
done()
})
})
})
middleware is a function with (req, res, next) as parameters. You send req, res and a callback where you handle the assertions. I.e. the assertion testing is passed in as next().
But there is no reason for next() to be called when you pass in OPTIONS as request method. The middleware will then do res.sendStatus(200) instead. So you have to call middleware as an ordinary function instead (for this particular test).
middleware(req, res);
expect(res.statusCode).to.equal(200);
expect(res.body.content).to.equal('anything');
done();
It will fail on the except for res.body, but that's because the middleware function is written that way.
As long as you only test the middleware function I would call it unit testing. If you care about what happens after next() gets called I would call it integration testing. But what it's called is not that important as long as it's tested.
You can see this answer for testing when the middleware has to call next().
I'm new to Express and trying to use middleware to handle a POST request. If I expose the endpoint, and make a request to the API, everything works fine.
Working Correctly
api/index.js
app.post('/api/endpoint', (req, res, next) => {
next();
});
server.js
app.use(function() {
console.log('hello'); // => hello
});
But when I try to replace the middleware function with a module that exports a function, the function never gets invoked.
Not Working
api/index.js
app.post('/api/endpoint', (req, res, next) => {
next();
});
server.js
const makeExternalRequest = require('./server/makeExternalRequest');
...
console.log(makeExternalRequest, typeof makeExternalRequest);
// => [Function] 'function'
app.use(makeExternalRequest);
server/makeExternalRequest.js
module.exports = function(err, req, res, next) {
console.log('hello', err);
}
The function in server/makeExternalRequest.js is never invoked, and nothing logs... Am I using app.use(...) incorrectly?
Express middleware requires three arguments, the third of which is a function you call when you're done to move the request along to the next handler:
module.exports = function (req, res, next) {
console.log('hello');
next();
};
Without calling the third parameter, your request will just remain pending and a response will never be sent. Also, be sure you call app.use before any handler that would return the response. If the response is sent first, then your middleware will never be reached.
I currently use 2 middlewares:
Express-jwt which extracts/validates a JsonWebToken from a request and my own middleware that checks that the JWT contains specific information (permissions).
I want to conditionally use those middlewares together (based on whether there's a specific swagger attribute on a route).
I want to do something like this:
let expressjwt = function(req, res, next) { ... };
let jwtValidator = function(req, res, next) { ... };
app.use((res, req, next) => {
if(req.swagger.someAttribute) {
expressjwt(req, res, jwtValidator(req, res, next));
// The issue here is that jwtValidator will get called even if
// expressjwt produces an error
} else {
next();
}
});
It sounds like the question is - "how do you conditionally call service B only if service A succeeds."
This is one of main goals of promises - it allows you to chain together async calls and have them conditionally "resolve." I can post a code sample if needed.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
I ended up wrapping my first middleware in a Promise using Promise.fromCallback, from memory, something like this:
if (req.swagger.someAttribute) {
Promise.fromCallback(cb => expressjwt(req, res, cb))
.then(() => {
return jwtValidator(req, res, next);
})
.catch(next); // Or deal with the rejection
} else {
next();
}
Promise.fromCallback is useful because next() is only called with arguments if the middleware failed and thus will become promise.reject
I'm working in a restful service using express.js and i want to enhance the req and res variables so for example you could write something like
app.use(function (req, res, next) {
res.Ok = function (data) {
res.status(200).send(data);
};
res.InternalError = function (err) {
res.status(500).send(err);
};
});
And later
router.get('/foo', function (req, res) {
res.Ok('foo');
})
This will send 'foo' in the body of the response and set the status code to 200 and is working perfectly.
My first question is if it is possible to add such functionality without a middleware function, lets say in a property or the prototype of the app variable?
The second question is if there are performance issues if you add many functionality with middleware functions at the app level. Are this functions attached to the request and response object per request or once on the application startup?
I know the Sails framework already do this but I'm wondering if they use middleware functions as well.
I keep digging and turns out that the request and response object are exposed in express using the __proto__ property.
var express = require('express'),
app = express();
app.response.__proto__.foo = function (data) {
this.status(200).send(data);
};
And later in the router
router.get('/foo', function (req, res, next) {
res.foo('test');
});
This will print test in your browser so it is possible to add functionality without using any middleware.
Note: I'm sure there are some drawbacks to this approach (overwriting express predefined properties, for example) but for testing purposes and adding very simple functionality I think is slightly better in terms of performance.
I'm not aware of any other way than using middleware. But in my opinion you could do the following to achieve nearly the same thing.
// Some Route
router.get('/foo', function(req, res, next) {
// ...
if(err) {
res.status(500);
return next(err);
}
return res.send('ok');
});
// Another route
router.get('/bar', function(req, res, next) {
// ...
if(badUserId) {
res.status(400);
return next('Invalid userId.');
}
req.result = 'hello';
return next();
});
router.use(function(req, res) {
// I prefer to send the result in the route but an
// approach like this could work
return res.send(req.result);
});
// Error Middleware
router.use(function(err, req, res, next) {
if(res.statusCode === 500) {
// Log the error here
return res.send('Internal server error');
} else {
return res.send(err);
}
});