I'm trying to validate a PATCH request payload for a Todo App that should have at least a text or a value property. I'm doing so in updateTodoValidators. Both properties are optional but at least one must exist (hence the oneOf) and if any exists it must be valid as the validators outside the oneOf indicate.
const validate = (validations) => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req)
if (errors.isEmpty()) {
return next();
}
res
.status(422)
.json({
payload: {
errors: errors.array()
},
message: "Validation error/s",
error: true
})
}
}
const updateTodoValidators = [
oneOf([
body('text').exists().withMessage('not specified'),
body('value').exists().withMessage('not specified')
]),
body('text').optional().trim().notEmpty().withMessage('cannot be empty'),
body('value').optional().isInt({ min: 1 }).withMessage('must be a valid positive number')
]
app.patch('/todos/:id', validate(updateTodoValidators), async (req, res, next) => { /* Route handler implementation */ })
I was looking into running the validations imperatively as indicated in Running validations imperatively [docs] for code readability purposes. And I discovered that if I have some validations that involve a oneOf my validate() throws with a TypeError saying validation.run is not a function. Bellow is a stack trace detailing the error:
(node:88997) UnhandledPromiseRejectionWarning: TypeError: validation.run is not a function
at Promise.all.validations.map.validation (/Users/alejandro/code/Production/server/validators/validate.js:6:64)
at Array.map (<anonymous>)
at /Users/alejandro/code/Production/server/validators/validate.js:6:35
at Layer.handle [as handle_request] (/Users/alejandro/code/Production/server/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/alejandro/code/Production/server/node_modules/express/lib/router/route.js:137:13)
at loginRequired (/Users/alejandro/code/Production/server/auth/helpers.js:18:24)
at Layer.handle [as handle_request] (/Users/alejandro/code/Production/server/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/alejandro/code/Production/server/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/alejandro/code/Production/server/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/alejandro/code/Production/server/node_modules/express/lib/router/layer.js:95:5)
(node:88997) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:88997) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
So, my question is: Is there any way to run validations imperatively when validations might involve one or more oneOfs?
I ended up doing it manually.
Do a .run() on both param checks. Then validate the _errors property is empty on both.
export const validateTokens = async (req, res, next) => {
// Check to make sure one of the header tokens is set and a UUID
const token1 = await header('token1')
.isUUID()
.run(req);
const token2 = await header('token2')
.isUUID()
.run(req);
// Manual oneOf
if (isEmpty(token1._errors) || isEmpty(token2._errors)) {
return next();
}
// Return error results
res.status(400).send('Must include either token1 or token2');
};
oneOf returns a middleware instance, not a validation chain. It doesn't have a .run() function which your customer middleware validator uses to validate hence it is erroring out.
There is an open issue for this here
For the moment I think you would need to implement you own custom function to validate these conditional parameters.
Related
I am currently working on an IoT project which requires service discovery (I am in the Android side). I decided to use react-native-zeroconf and I encountered a problem.
There is a warning Error: Request failed with status code 500 once I called .scan() method.
I have already added permission into the AndroidManifest file. Thank you in advance.
Edit: remove async from function
export function scanmDNS() {
const zeroconf = new Zeroconf();
zeroconf.scan();
const res = zeroconf.getServices();
console.log({ res });
zeroconf.stop();
}
Object {
"res": Object {},
}
Possible Unhandled Promise Rejection (id: 0):
Error: Request failed with status code 500
createError#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:224752:26
settle#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:224742:25
onloadend#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:224619:15
dispatchEvent#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:33843:31
setReadyState#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:32985:29
__didCompleteResponse#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:32783:29
emit#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:4940:42
__callFunction#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:5979:36
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:5707:31
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:5933:15
callFunctionReturnFlushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.doji.dojimobileapplication&modulesOnly=false&runModule=true:5706:21
callFunctionReturnFlushedQueue#[native code]
Possible Unhandled Promise Rejection (id: 1):
There are two ways to handle Promises, since you're using async/await syntax your response should use the await keyword also you are wrapping your response in an object which you shouldn't (without assigning it to a property).
const res = await zeroconf.getServices();
console.log(res)
The other way would be
zeroconf.getServices()
.then((res) => {
console.log(res) //do whatever you want to do with your response here
})
.catch((err) => {
console.log(err) //handle errors here.
}
Not sure if this will solve your problem but hope it leads you to your solution :)
Basically, the question is do I need to use next(err) when encountering any errors? The nodejs error documentation says it is fine to use a standard sort of if(err) else... for asynchronous callbacks and EventEmitters, as long as the error isnt handled with a try-catch block for non async-await functions, as it will cause crashing. If I do need to use them, what is to prevent the next() function being called multiple times in the same handler for different asynchronous operations? Wouldnt using the default error handler cause headers to be sent multiple times and cause an error of its own when using event emitters ?
Apologies if the question has been asked, its just I cannot find a specific answer to why usage of express.js error handling is preferred.
If you are asking if you need to use an explicit next(err) in a handler,
e.g.
app.get('/someurl', (req, res, next) => {
//do something - whoops had an error
next(err);
})
No, the above is not required in a handler.
The only time you would need to explicitly wrap or pass on the error is if you have, for example, used a try/catch and are not handling the error condition itself in the handler, i.e. not returning a response in the handler (Not sure why you would want to do that).
What will happen above when an error occurs in the handler, express will continue on through the middlewares until it finds a handler that will deal with your error.
If there are none, it will exit.
So to use a global error handler, you could write your app like the following and not worry about next(err) in each handler function.
app.get('/route/one', async (req, res) => {
// do something that could throw an error
const result = await aFunctionThatCouldThrowAnError();
// No error handling in this function
res.json({ result });
});
app.get('/route/two', (req, res) => {
res.json({ hello: 'world-two' });
});
// A Global Error handler
app.use((err, req, res, next) => {
//handle all errors here
if(err) {
res.status(500).send('some error message')
}
res.status(404).send('not found');
});
Note that the order of middlewares is important, so the global error handler should be applied last.
My backend in ExpressJS and NodeJS used to work well. I just realized that logs exploded the disk (the backend is still functional), because when the backend is on, it keeps trying:
kpi.js GET /socket.io/?EIO=3&transport=polling&t=NBiaEK6
index.js GET /socket.io/?EIO=3&transport=polling&t=NBiaEK6
index.js router.get *
kpi.js POST /socket.io/?EIO=3&transport=polling&t=NBiaER6
index.js POST /socket.io/?EIO=3&transport=polling&t=NBiaER6
Error: Not Found
at /opt/funfun/app.js:99:13
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at /opt/funfun/node_modules/express/lib/router/index.js:635:15
at next (/opt/funfun/node_modules/express/lib/router/index.js:260:14)
at /opt/funfun/routes/index.js:18:2
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/opt/funfun/node_modules/express/lib/router/index.js:174:3)
at router (/opt/funfun/node_modules/express/lib/router/index.js:47:12)
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at /opt/funfun/node_modules/express/lib/router/index.js:635:15
kpi.js GET /socket.io/?EIO=3&transport=polling&t=NBiaF8A
index.js GET /socket.io/?EIO=3&transport=polling&t=NBiaF8A
index.js router.get *
kpi.js POST /socket.io/?EIO=3&transport=polling&t=NBiaFFz
index.js POST /socket.io/?EIO=3&transport=polling&t=NBiaFFz
Error: Not Found
at /opt/funfun/app.js:99:13
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at /opt/funfun/node_modules/express/lib/router/index.js:635:15
at next (/opt/funfun/node_modules/express/lib/router/index.js:260:14)
at /opt/funfun/routes/index.js:18:2
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/opt/funfun/node_modules/express/lib/router/index.js:174:3)
at router (/opt/funfun/node_modules/express/lib/router/index.js:47:12)
at Layer.handle [as handle_request] (/opt/funfun/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/opt/funfun/node_modules/express/lib/router/index.js:317:13)
at /opt/funfun/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/opt/funfun/node_modules/express/lib/router/index.js:335:12)
at next (/opt/funfun/node_modules/express/lib/router/index.js:275:10)
at /opt/funfun/node_modules/express/lib/router/index.js:635:15
kpi.js GET ... and index.js GET ... are what I print. Here is the code of opt/funfun/app.js:
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('Not Found'); // line 99
err.status = 404;
next(err);
});
Does anyone know what may be the reason?
This may happen if you have a socket.io client trying to connect to
your backend, but your backend does not have a socket.io server configured or
is not properly configured to accept connection and you are logging any errors in
the error handler.
Make sure the socket.io client and server are properly configured.
Please see the socket.io docs on how to setup the client and the server.
By default the client keeps trying to reconnect infinitely when the connection fails,
you can prevent it from polling the server infinitely by setting the
reconnectionAttempts options.
For example, this will prevent the client from polling the server after 10 failed attempts
const socket = io(serverURL, { reconnectionAttempts: 10 });
Also to minimize the size of your logs, don't log the entire error object when you are in production,
you could update the error handler to log only relevant details
// catch errors and forward to error handler
app.use(function (req, res, next) {
...
next(err);
});
//In the error handler
app.use(function (err, req, res, next) {
//log only relevant details
logger.info(err.message)
})
Since you are passing an Error to the next parameter, it will detects as an error and Express will use its default handler if you don't have any Error handler.
If you pass anything to the next() function (except the string 'route'), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions.
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.
From: https://expressjs.com/en/guide/error-handling.html
So it might be because:
You don't have error handler middleware, and Express default error handler is logging all of the error stack traces
You have error handler, but it's logging all the error stack trace.
First, make sure you are running in production environment so the error stack trace doesn't get sent to the client
Set the environment variable NODE_ENV to production, to run the app in production mode.
If you want to pass data (error) to next middleware without it getting detected as an error, store it in res.locals instead, read more here.
app.use(function (req, res, next) {
const err = new Error("Not Found");
err.status = 404;
res.locals.exception = err;
next();
});
// Custom Error handler
app.use(function (req, res) {
let err = res.locals.exception;
// Add Minimum logging here if you want
if(err.message === "Not Found") {
return res.status(404).send();
} else {
return res.status(500).send();
}
// or
return res.status(err.status).send(err.message);
});
or write your own actual custom error handler middleware (make sure to put it after the 404 error handler)
app.use(function (req, res, next) {
const err = new Error("Not Found");
err.status = 404;
next(err);
});
// Actual Error handler Middleware, notice the first parameter is the error
app.use(function (err, req, res) {
// Add Minimum logging here if you want
if(err.message === "Not Found") {
return res.status(404).send();
} else {
return res.status(500).send();
}
// or
return res.status(err.status).send(err.message);
});
// -- or if you are using typescript --
// declare the error handler first
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
// Add Minimum logging here if you want
if(err.message === "Not Found") {
return res.status(404).send();
} else {
return res.status(500).send();
}
// or
return res.status(err.status).send(err.message);
};
// then use it
app.use(errorHandler);
I've been using the first option (res.locals) because on Typescript it doesn't support error handler middleware somehow. I just figured it out, adding it to example, found the solution from this github issue
I am trying to connect to MongoDB atlas using mongoose but always get an error,
I have tried to change my ip address and also try to change the link
also change all the settings in mongoDB altas but always get the same error
const express = require('express')
const graphqlHTTP = require('express-graphql')
const app = express()
const catSchema = require('./Schema/categorySchema')
const mongoose = require('mongoose')
// SetUp MongoDB
mongoose.connect('mongodb+srv://khawar111:khawar111#e-selling-bh1wv.mongodb.net/test?retryWrites=true&w=majority', { useNewUrlParser: true })
mongoose.connection.once('open', () => {
console.log('Database Connected')
})
mongoose.connection.on('error', (e) => {
console.log(e)
})
// Set Route for GraphQL
app.use('/graphQL', graphqlHTTP({
schema: catSchema,
graphiql: true
})
)
app.listen(3000, () => {
console.log('Server Started')
})
Error:
Error: queryTxt ETIMEOUT e-selling-bh1wv.mongodb.net
at QueryReqWrap.onresolve [as oncomplete] (dns.js:203:19) {
errno: 'ETIMEOUT',
code: 'ETIMEOUT',
syscall: 'queryTxt',
hostname: 'e-selling-bh1wv.mongodb.net'
}
(node:25313) UnhandledPromiseRejectionWarning: Error: queryTxt ETIMEOUT e-selling-bh1wv.mongodb.net
at QueryReqWrap.onresolve [as oncomplete] (dns.js:203:19)
(node:25313) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25313) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Actually,you need to remove " test?retryWrites=true&w=majority " from your connection string and replace it with the name of your database.Like this
mongoose.connect('mongodb+srv://khawar111:khawar111#e-selling-bh1wv.mongodb.net/DatabaseName', { useNewUrlParser: true })
this is because "test?retryWrites=true&w=majority" is used for testing purposes
Make sure to change the node version to 2.2.12
And add IP address like below:
I faced the issue and I try to find the answer but nothing works then after some time it works normally again . I think it is just a connection limit issue.
Here is the background of the question : I'm following the kick-off-koa using Koa 2. But the exercises in the kick-off are designed for Koa 1. I've created an issue for this problem of Koa 2 : Task of error handler with Koa 2 cannot pass.
For short, my problem is how to display a custom error page when a 500 error happens.
Here are the codes :
// error handler middleware
function errorHandler(ctx, next) {
try {
return next();
}
catch(err) {
ctx.status = err.status || 500;
// I would like to display the custom message as follows
ctx.body = 'Oops! internal server error';
// with emitting the error event, don't work
// ctx.app.emit('error', err, ctx);
}
}
// to generate error
app.use(router.get('/error', ctx => {
ctx.throw('oops', 500);
}));
But my page of error is always displaying as "Internal Server Error", which is the default message. It seems that ctx.body = 'Oops! internal server error'; couldn't modify the page.
Thanks for the helps!
If you are using Koa2, you don't have to return inside middleware, instead, use await. And by the way, your middleware function MUST be an async function.
Here is an example of a combined 404 and 500 middleware:
app.use(async (ctx, next) => {
try {
await next()
if (ctx.status === 404) ctx.throw(404)
} catch (err) {
console.error(err)
ctx.status = err.status || 500
ctx.body = errorPage.render({ // Use your render method
error: err,
})
}
})
// Your normal routes here
First, Koa awaits for the next middleware in the chain (which is your normal routes). If nothing is found or an error occurred, the middleware chain goes backwards and the next line is executed, which throws a 404 and its captured inside the catch.
Now in the catch statement, you can get either 404, 500 (by default) or 5xx if other error occurred.
The body of the page is also set with a render of your template and passing the error to the template so you can make use of it.
You don't have to emit the error as this is the last catch in the chain.