I need to send custom error message in JSON format, from my express app, served in a lambda function using serverless-http
Please correct me if i got it wrong, but as i understand it, we need to use LAMBA_PROXY APIG integration to be able to send custom error messages defined directly from a lambda function.
This is what i have tried so far:
res.status(400).json({ message: 'email already taken' });
serverless.yml
functions:
auth:
handler: src/express/auth/index.handler
name: ${self:service}-auth-${env:STAGE}
# warmup: true
integration: lambda-proxy
memorySize: 128
timeout: 15
events:
- http:
path: /auth/
method: ANY
cors: true
- http:
path: /auth/{any+}
method: ANY
cors: true
this is what the API is returning(with status code 400)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Bad Request</pre>
</body>
</html>
Any leads, on how I can send a custom response, in JSON format?
update:
After more tests, I found out that calling next(error) doesn't reach the last error handler
const register = async (req, res, next) {
try {
await verifyEmail(req.body.email);
const user = await Users.register(req.body);
const token = sign(user.attrs, {});
res.json({ token, user });
} catch (e) {
next(e);
}
};
const generalError = async (err, req, res, next) => {
// doesn't reach this part! :(
console.log('generalError handler', JSON.stringify(err));
res.status(errorOut.status).json(errorOut);
};
ApiRouter.post('/register', register);
app.use('/auth', ApiRouter);
app.use(generalError);
(I just answered a very similar question here)
Yes, this is explained in the Express docs under Error Handling.
Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
If you pass an error to next() and you do not handle it in a custom error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. The stack trace is not included in the production environment.
To override this handler, refer to the section in the Express docs titled Writing error handlers.
It explains:
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next). For example:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
You define error-handling middleware last, after other app.use() and routes calls
So in your case, if you wanted to respond with a 400 and some JSON, you might write something like this:
const serverless = require('serverless-http');
const express = require('express');
const app = express();
// Your middleware and other routes here
app.use(/* register your middleware as normal */);
// Finally, your custom error handler
app.use(function customErrorHandler(err, req, res, next) {
res.status(400).json({ message: 'email already taken' });
});
module.exports.handler = serverless(app);
Related
Question: Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Background: The IIFE is used to create an async container to wrap await statements. I can't see a way out of this and I wonder if I'm using the wrong basic, pattern altogether.
Simplified Example:
app.get('/', function(req, res, next) {
(async function() {
try {
let example = await someLogic(x);
} catch(err) {
return next(new Error('oops'));
}
})();
console.log('main endpoint');
});
app.use(function(err, req, res, next) {
console.log('my error', err.message);
});
Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Yes, that works fine. It will call next(err) just fine. But, your return will return only from the IIFE and the rest of your request handler after the try/catch will still execute (not sure if you want that or not).
FYI, it's probably simpler to declare the request handler as async and then you don't need the IIFE wrapper:
app.get('/', async function(req, res, next) {
try {
let example = await someLogic(x);
console.log('main endpoint');
// send some response here
} catch(err) {
return next(new Error('oops'));
}
});
As far as I know I need to place multer middleware before express-validator middleware in order to be able to access req.body from express-validator custom validators, like this:
app.post('/editPhoto',
upload.single('avatar'),
[express-validator middleware],
(req, res, next) => {
// req.file
//req.body
});
There are 2 options you can use to work with multer:
app.post('/editPhoto', upload.single('avatar'), [express-validator middleware],(req, res, next) => {
// req.file
//req.body
})
Or you can use:
app.post('/editPhoto', (req, res, next) => {
upload(req, res, function (err) {
if (err) {
// This is a good practice when you want to handle your errors differently
return
}
// Everything went fine
})
})
In the second option, you can handle Multer's errors, I would like to know if I could handle Multer's errors in the express-validator custom validators or in express route handling middleware if I use Multer as a middleware before the express-validator middleware, as in my first example
I finally solved it creating a function with the multer code where I check for errors (using the second option of the example in my question). I call that function in the middleware chain before all the express-validator middleware functions.
I'm in express 4.16.3.
At first, this code works fine:
const express = require('express')
const router = express.Router()
let app = express()
router.use((req, res, next) => {
console.log('hi, express')
next()
})
app.use(router)
app.all('*', (req, res) => {
res.send("hello, world")
})
app.listen(8075, function () {
console.log('listening localhost:8072')
})
But when I try to set a param in next():
const express = require('express')
const router = express.Router()
let app = express()
router.use((req, res, next) => {
next('hello, express') ----------------mark
})
app.use(router)
app.all('*', (msg, req, res) => {
console.log(msg)
res.send(msg)
})
app.listen(8075, function () {
console.log('listening localhost:8072')
})
the response is always an error page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>hello, express</pre>
</body>
</html>
I just add a param in next function, but it seems broken express router.
What is the right way to use next() with params?
Isn't it a better practice to attach your required data with req object before calling next() ?
router.use((req, res, next) => {
req.data = { title: 'your sample data' };
next();
});
app.all('*', (req, res) => {
console.log(req.data);
});
This will attach your required data with the request object and pass the control to the next processing pipeline(app.all() handler in your code).
Then you can use that object in all of the available routes.
I don't know where in the Express docs this is stated, but what you're calling when you invoke next isn't another middleware function directly, rather telling the Express framework to call the next middleware function. A middleware function has fixed arguments passed to it, so you will only have access to the request, the response, and a function that may call the next middleware function in the chain. The only parameters this function takes is optionally the string 'route'.
In order to pass data to the next middleware function down the line your best bet is to modify the request object or the response object.
See Using Express middleware on the Express website.
How does the the error handler get triggered?
In sample code, I find that it is placed at the bottom of all the middleware functions. Is the position important?
You can refer below example for some details.
Here, for the '/' GET endpoint a middleware explicitly throws an error 'problem error'.
At this point, express error handler mechanism is triggered and it looks for an error handler (with err as a param). As a result, subsequent 'Hello' is not sent back to the client as its handler is not an error one.
Subsequent error handlers logErrors, clientErrorHandler and errorHandler are invoked one by one to perform relevant tasks with final one writing back the response.
The reason they are placed at the end is to catch the errors thrown by declared middlewares and handle them elegantly i.e. printing it, logging it, sending mails etc. Think of it in terms of a try catch mechanism in other languages like Java. If declared above other middlewares, they would render useless as the errors won't be handled. You can see the difference in output by swapping the order of 'GET' request with the error handlers.
const express = require('express');
const app = express();
app.get('/', (req, res, next) => next(new Error('problem error')), (req, res) => {
res.status(200).send("Hello");
});
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}
app.listen(3000, () => console.log('Example app listening on port 3000!'))
I'm looking for a clean way to have my express app return 405 Method Not Allowed if a client sends a request that matches a mapped url route but does not match the mapped HTTP method.
My current implementation is to have a default "catch-all" handler that tries to match the url against the register routes, ignoring the HTTP method. If there is a match, then we know to return a 405, otherwise we let express do its default 404 behavior.
I'm hoping there is a better way that doesn't involve running all the route matching twice (once by express, once by my handler).
Here is an approach that I have used successfully with multiple Django applications and now with Node and Express. It is also follows RFC 2616 (HTTP/1.1) that says the following about HTTP 405:
The response MUST include an Allow header containing a list of valid
methods for the requested resource.
So, the key point is to route the requests to the same handler without regard to methods.
app.all('/page/:id', page.page);
app.all('/page/:id/comments', page.comments);
app.all('/page/:id/attachments', page.attachments);
...
The next point is to validate the method in the handler function 'comments'. Note that the handler is responsible for handling all the methods. In Django's world this is the only way to go because the framework forces you to separate the routing of the URLs from the actual action about to be performed against the resource the URL represents.
In the handler you could check the method like this...
exports.comments = function (req, res) {
if (req.route.method === 'get') {
res.send(200, 'Hello universe.');
} else {
res.set('Allow', 'GET');
res.send(405, 'Method Not Allowed');
}
}
...but as you can expect the code will quickly become repetitious and not nice to read especially when you have many handler functions and many different sets of allowed methods.
Therefore I prepared a shortcut function named restful for the job. Define the function wherever you want. I personally would place it in helpers.js under the same directory where the handler functions are implemented.
var restful = function (req, res, handlers) {
//
// This shortcut function responses with HTTP 405
// to the requests having a method that does not
// have corresponding request handler. For example
// if a resource allows only GET and POST requests
// then PUT, DELETE, etc requests will be responsed
// with the 405. HTTP 405 is required to have Allow
// header set to a list of allowed methods so in
// this case the response has "Allow: GET, POST" in
// its headers [1].
//
// Example usage
//
// A handler that allows only GET requests and returns
//
// exports.myrestfulhandler = function (req, res) {
// restful(req, res, {
// get: function (req, res) {
// res.send(200, 'Hello restful world.');
// }
// });
// }
//
// References
//
// [1] RFC-2616, 10.4.6 405 Method Not Allowed
// https://www.rfc-editor.org/rfc/rfc2616#page-66
//
// [2] Express.js request method
// http://expressjs.com/api.html#req.route
//
var method = req.route.method; // [2]
if (!(method in handlers)) {
res.set('Allow', Object.keys(handlers).join(', ').toUpperCase());
res.send(405);
} else {
handlers[method](req, res);
}
}
With restful it is now quite painless to handle 405 responses automatically and having proper Allow header being set. Just give a function for each method you allow and restful does the rest.
So lets modify the previous example:
exports.comments = function (req, res) {
restful(req, res, {
get: function (req, res) {
res.send(200, 'Hello restful universe.');
}
});
}
Why the name restful? In RESTful web it is quite essential for the API to obey the conventions like responsing with HTTP 405 to the request having non-supported method. Many of those conventions could be integrated to restful when needed. Therefore the name is restful and not something like auto405 or http405handler.
Hope this helps. Any thoughts?
Method 1: Use .route() and .all()
// Your route handlers
const handlers = require(`./handlers.js`);
// The 405 handler
const methodNotAllowed = (req, res, next) => res.status(405).send();
router
.route(`/products`)
.get(handlers.getProduct)
.put(handlers.addProduct)
.all(methodNotAllowed);
This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .get() and .put() handlers will catch GET and PUT requests, and the rest will fall through to the .all() handler.
Method 2: Middleware
Create middleware which checks for allowed methods, and returns a 405 error if the method is not whitelisted. This approach is nice because it allows you to see and set the allowed methods for each route along with the route itself.
Here's the methods.js middleware:
const methods = (methods = ['GET']) => (req, res, next) => {
if (methods.includes(req.method)) return next();
res.error(405, `The ${req.method} method for the "${req.originalUrl}" route is not supported.`);
};
module.exports = methods;
You would then use the methods middleware in your routes like this:
const handlers = require(`./handlers.js`); // route handlers
const methods = require(`./methods.js`); // methods middleware
// allows only GET or PUT requests
router.all(`/products`, methods([`GET`, `PUT`]), handlers.products);
// defaults to allowing GET requests only
router.all(`/products`, methods(), handlers.products);
Due to ambiguity, there really is no other way. Personally, I would do something like this:
var route = '/page/:id/comments'
app.get(route, getComments)
app.all(route, send405)
function send405(req, res, next) {
var err = new Error()
err.status = 405
next(err)
}
Either way, you have to check the routes twice.
Kinda old question but here is what i did. I just put this after all my routes but before my 400 handler
// Handle 405 errors
app.use(function(req, res, next) {
var flag = false;
for (var i = 0; i < req.route.stack.length; i++) {
if (req.method == req.route.stack[i].method) {
flag = true;
}
}
if (!flag) {
err = new Error('Method Not Allowed')
err.status = 405;
return next(err)
}
next();
});
I have been doing it this way:
Say if you have GET and POST method handlers for /. You can wrap the path with app.route or router.route and assign the handlers accordingly.
app.route("/").get((req, res) => {
/* DO SOMETHING*/
}).post((req, res) => {
/* DO SOMETHING*/
}).all((req, res) => {
res.status(405).send();
});
http://expressjs.com/en/4x/api.html#app.route
http://expressjs.com/en/4x/api.html#router.route
A request will get matched to the route and filtered through the handlers. If a handler is present, it will get handled as usual. Else, it will reach the all handler that will set the status code to 405 and ending the request.
I fixed it like this :
/*paths here*/
router.get('/blah/path1', blah.do_something );
router.post('/blah/path2', blah.do_something_else );
/* if we get here we haven't already gone off down another path */
router.all('/*', (req,res) => { res.status(405),
res.json({'status':405,
'message':req.method + ' not allowed on this route'})
});
/* simples */
I thought this was a pretty interesting problem and so I dove deeeeeep down into the depths of the express app function and found a way to dynamically build a 405 error that includes all of the possible routes (without having to manually update anything when you add a new route).
app.use("", (req, _, next) => {
const err = buildError(app, req);
if (!err) return next();
return next(err);
});
For those interested you can find the npm package here https://www.npmjs.com/package/express-ez-405, and below is a quick example of what it looks like to use it.
const express = require("express");
const { buildError } = require("express-ez-405");
const app = express();
app.use(express.json());
const userRouter = require("./routes/user");
const mainRouter = require("./routes/main");
const nestedRouter = require("./routes/nested");
// // Routes
app.use("/main", mainRouter);
app.use("/user", userRouter);
app.use("/nested/route", nestedRouter);
// Routes
// 405 & 404 error catcher
app.use("", (req, _, next) => {
const err = buildError(app, req);
if (!err) return next();
return next(err);
});
// 405 & 404 error catcher
// Error handling
app.use((err, _, res, __) =>
res.status(err.status).json({ message: err.message })
);
// Error handling
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}...`);
});
Once this in in there you never have to worry about updating it regardless of whether you add, remove, or change routing.