How to implement Joi validation in hapi.js? - node.js

I just want to implement Joi in Hapi API.
server.route([
{
method: 'POST',
path: '/login',
config: {
tags: ['login', 'auth'],
auth: false,
validate: {
payload: payloadValidator,
failAction: (req, h, source, error) => {
console.log("Error ::: ", source.details[0].message);
return h.response({ code: 0, message: source.details[0].message });
}
}
},
handler: async (request, h) => {
console.log(request.payload.email);
console.log(request.payload.password);
...
}
}
]);
Hear I call payloadValidator.
const payloadValidator = Joi.object({
email: Joi.string().required(),
password: Joi.string().required()
}).options({ allowUnknown: true });
Actually I'm new with hapi and I'm missing something in my code. Can anyone help me to fix this issue?
Required output
If I do not pass email then the app must throw an error of Email is required and it should be the same with the password field also.
Error:
Error ::: "email" is required
Debug: internal, implementation, error
Error: Lifecycle methods called before the handler can only return an error, a takeover response, or a continue signal
at Request._lifecycle (/var/www/html/hapi/node_modules/#hapi/hapi/lib/request.js:326:33)
at process._tickCallback (internal/process/next_tick.js:68:7)

As an error suggests Lifecycle methods called before the handler can only return an error, a takeover response, or a continue signal you have to return takeover response.
return h.response({ code: 0, message: source.details[0].message }).takeover();
For more information you can visit this link : reference link

Related

Mongoose CastError

I have the following code, which does absolutely nothing, and for some reasons, I have an error with mongoose which is not even called, here's the code:
.post('/testRequest', express.json(), upload.none(), async (req, res) => {
try {
res.status(200);
res.send('test');
} catch (err) {
console.error(err.stack);
res.status(err.code || 400);
res.send(err.message || err);
}
})
And here's the error:
message: 'Cast to ObjectId failed for value "test" at path "_id" for model "Requests"',
name: 'CastError',
stringValue: '"test"',
kind: 'ObjectId',
value: 'test',
path: '_id',
reason: undefined,
I've tried many things but didn't seem to fix it
You probably have a .post(':id', ...) on top of your code. So a post request to /testRequest matches the '/:id' and '/testRequest' routes, but only the top one executes.
The ':id' route reads testRequest as an ID and throws the CastError.
You can swap the order of the methods, which was already discussed here.

GraphQL Error Handling with ApolloClient and Apollo-Server-Express

The onError function from apollo-link-error has a populated graphQLErrors Object, but when ApolloClient throws an error object, the graphQLErrors property contains an empty array. Further inspection reveals the graphql error message in error.networkError.result.errors.
How can one properly configure Apollo Client to return a populated graphQLErrors object?
Apollo Client Setup:
const {ApolloClient} = require('apollo-client')
const { ApolloLink } = require('apollo-link')
const {HttpLink} = require('apollo-link-http')
const {onError} = require('apollo-link-error')
const {InMemoryCache} = require('apollo-cache-inmemory')
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
})
const middleware = (req, ignored_res, next) => {
const client = new ApolloClient({
link: ApolloLink.from([errorLink, new HttpLink({ uri:'http://somegraphqlserver.com', fetch: require('node-fetch') })]),
cache: new InMemoryCache(),
})
req.GQLClient = client
return next()
}
module.exports = middleware
Calling apollo-server-express:
req.GQLClient
.query({
query: SOME_MALFORMED_QUERY,
})
.then((data) => {...})
.catch((error) => {
console.log('rawError', error)
console.log('error.networkError.result.errors', error.networkError.result.errors)
return next(error)
})
Console Results:
[GraphQL error]: Message: Cannot query field "blah" on type "CustomerList"., Location: [object Object], Path: undefined
[Network error]: Error: Response not successful: Received status code 400
rawError { Error: Network error: Response not successful: Received status code 400
at new ApolloError (/.../node_modules/apollo-client/bundle.umd.js:121:28)
at /.../node_modules/apollo-client/bundle.umd.js:1187:41
at /.../node_modules/apollo-client/bundle.umd.js:1620:17
at Array.forEach (<anonymous>)
at /.../node_modules/apollo-client/bundle.umd.js:1619:18
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (/.../node_modules/apollo-client/bundle.umd.js:1614:22)
at /.../node_modules/apollo-client/bundle.umd.js:1114:31
at process._tickCallback (internal/process/next_tick.js:178:7)
graphQLErrors: [],
networkError:
{ Error: Response not successful: Received status code 400
at throwServerError (/.../node_modules/apollo-link-http-common/lib/bundle.umd.js:33:21)
at /.../node_modules/apollo-link-http-common/lib/bundle.umd.js:58:17
at process._tickCallback (internal/process/next_tick.js:178:7)
response:
Response {
size: 0,
timeout: 0,
[Symbol(Body internals)]: [Object],
[Symbol(Response internals)]: [Object] },
statusCode: 400,
result: { errors: [Array] } },
message: 'Network error: Response not successful: Received status code 400',
extraInfo: undefined }
error.networkError.result.errors [ { message: 'Cannot query field "blah" on type "CustomerList".',
locations: [ [Object] ] } ]
library versions:
Server:
"apollo-server-express": "^1.3.2"
Client:
"apollo-cache-inmemory": "^1.1.12"
"apollo-client": "^2.2.8"
"apollo-link-error": "^1.0.9"
"apollo-link-http": "^1.5.4"
This is by design. From the docs:
graphQLErrors: An array of errors from the GraphQL endpoint
networkError: Any error during the link execution or server response,
that wasn't delivered as part of the errors field in the GraphQL result
In other words, if your query is malformed, you request a field that isn't valid (like in your example), or hit any other issue that results in status other than 200, the error will appear as part of the networkError property. On the other hand, if the request returns a 200, but the errors array inside the response is populated, those same errors will be returned as part of graphQLErrors.
If you want to see an example of graphQLErrors being populated, format your query correctly but have one of your resolvers throw an error as soon as it's called. As long as the query doesn't hit any other issues, you should see the same error pop up inside graphQLErrors.

mailgun not sending email returning undefined data

I am trying to implement a simple email confirmation on signup flow using Angular 4, nodejs, mailgun-js and mailgun. The problem is the mailgun send:
mailgun.messages().send(data, function(error, body) {
console.log(body);
});
is timing out with the following error on the front-end:
POST http://localhost:3000/users/sendMail net::ERR_EMPTY_RESPONSE
core.js:1448 ERROR
HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false, …}
error
:
ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: "error", …}
headers
:
HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, headers: Map(0)}
message
:
"Http failure response for (unknown url): 0 Unknown Error"
name
:
"HttpErrorResponse"
ok
:
false
status
:
0
statusText
:
"Unknown Error"
url
:
null
__proto__
:
HttpResponseBase
The back-end show now errors but the return value (body) is undefined and the email is not sent. This is my complete back-end sendMail module:
const api_key = '<key-f40360259dea7c667ca06d4aee956421>';
const DOMAIN = '<sandbox836cdf3cfe9c467b916fe5bb0d73e7e7.mailgun.org>';
const mailgun = require('mailgun-js')({ apiKey: api_key, domain: DOMAIN });
// ---------------- sendMail variables -----
router.post('/sendMail', (req, res, next) => {
console.log("Email message data: " + JSON.stringify(req.body));
const data = {
from: 'xxxxdev#gmail.com',
to: [req.body.email, 'xxxxxxx#gmail.com'],
name: req.body.firstName,
code: req.body.workshopCode,
subject: req.body.workshopTitle,
text: req.body.messageBody
};
mailgun.messages().send(data, function(error, body) {
console.log(body);
});
});
Both From and To email are valid email address and the req.body displayed in the console.log is the correct data passed from the Front-End.
This the Front-end sendmail module:
sendEmailConfirmation(message) {
const headers = new HttpHeaders();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post('http://localhost:3000/users/sendMail', message).subscribe((data) => {
console.log('Mail return data: ' + data); >>>THIS LINE IS NEVER EXECUTED<<<<<<
},
);
}
What am I missing here??.....
I noticed the absence of a res.send so I added the following:
if (error) throw error;
else {
res.send('Email sent');
}
Now I get a forbidden error on the back-end:
(node:10780) UnhandledPromiseRejectionWarning: Error: Forbidden
at IncomingMessage.res.on (C:\connect2Server\node_modules\mailgun-js\lib\request.js:301:17)
at IncomingMessage.emit (events.js:165:20)
at endReadableNT (_stream_readable.js:1101:12)
at process._tickCallback (internal/process/next_tick.js:152:19)
(node:10780) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a ca
tch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Please make sure that if you are sending from Europe to also include the host like this:
const mg = mailgun({apiKey: MAILGUN_PRIVATE_API_KEY, domain: MAILGUN_DOMAIN_NAME, host:MAILGUN_HOST});
For Europe, host is api.eu.mailgun.net and for America it's api.mailgun.net from what I know.
Problem solved.....caused by typo in mailgun credentials. The '<' '>' brackets where copied over .....the clue was the Forbidden error.

adCreative create Error: 'Invalid parameter' code: 1487390

Cannot create adCreative. Get an error:
Adcreative Create Failed: The Adcreative Create Failed for the following reason: Oops, something went wrong. Please try again later
and in the same error stack:
message: 'Invalid parameter'
code: 1487390
My code:
exports.create = (data, campaign) => {
return new adsSdk.AdCreative({
body: data.appearance.vacancyDescription,
image_url: data.appearance.backgroundImage,
name: `Creative for campaign ${campaign.id}`,
link_url: data.appearance.link,
title: data.appearance.linkDescription,
object_story_spec: {
instagram_actor_id: 'xxxxxx',
page_id: 'xxxxx',
link_data: {
link: data.appearance.link
}
}
}, `act_xxxxx`)
.create()
I'm using facebook-js-ads-sdk.
The problem was because of wrong page_id

mongodb update is throwing a critical error

When I try to update a user using a web form, it runs a app.post on express. The object user is correct, but sometimes it throws a error in node console
app.post('/register/update', jsonParser, (request, response) => {
let user = request.body.user;
let users = mongoUtil.users();
console.log(user);
users.update({email: user.email}, user, (err, res) => {
if(err) {
response.sendStatus(400);
}
response.sendStatus(201);
});
});
In node console:
{ _id: '578246ec9eb0587a5d67b8c9',
email: 'test#test.com',
zipcode: '1231-123',
companyName: 'test',
tradeName: 'test',
contactName: 'Test',
tel: '(14) 1232-1231',
password: 'test',
passwordConfirm: 'test',
adress: 'test',
adressComplement: 'test',
adressNumber: '123' }
/home/ec2-user/ ... /mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
at ServerResponse.header (/home/ec2-user/ ... /node_modules/express/lib/response.js:719:10)
at ServerResponse.contentType (/home/ec2-user/ ... /node_modules/express/lib/response.js:552:15)
at ServerResponse.sendStatus (/home/ec2-user/ .. /node_modules/express/lib/response.js:340:8)
at users.update (/home/ec2-user/menuWebApp/server/app.js:94:14)
at handleCallback (/home/ec2-user/ ... /node_modules/mongodb/lib/utils.js:96:12)
at /home/ec2-user/ ... /node_modules/mongodb/lib/collection.js:1008:42
at commandCallback (/home/ec2-user/ ... /node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:1194:9)
at Callbacks.emit (/home/ec2-user/ ... /node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:119:3)
at Connection.messageHandler (/home/ec2-user/ ... /node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:358:23)
I'm probably dealing with a callback problem, but I don't have idea how to solve it D:
The error is not coming from the update statement itself its coming from your callback function.
Specifically these lines of code.
if(err) {
response.sendStatus(400);
}
response.sendStatus(201);
What you are doing is if there is an error send the status 400, then send the status 201. The problem is once you send a response with your headers to the requestor you can't try and set the headers again.
So your code should change to:
if(err) {
response.sendStatus(400);
}else{
response.sendStatus(201);
}
So if an error is generated you will send a 400 response otherwise you will send a 201 response instead of trying to send a 400 response and then a 201 response immediately after.

Resources