Is there a way to use callback functions in joi validation? - javascript

Their Guru's
I'm doin a research on a code I saw on the internet, but if I see the syntax, they have used Hapi/Joi which is depricated.
My question is how can I use this syntax in Joi?
app.post('/test', (req, res, next) => {
const id = Math.ceil(Math.random() * 9999999);
Joi.validate(data, schema, (err, value) => {
if (err) {
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data
});
} else {
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({id}, value)
});
}
});
});

By inspecting the types (index.ds.ts) file in Github indeed there is no callback style validate function, only those two methods:
/**
* Validates a value using the schema and options.
*/
validate(value: any, options?: ValidationOptions): ValidationResult;
/**
* Validates a value using the schema and options.
*/
validateAsync(value: any, options?: AsyncValidationOptions): Promise<any>;
However you can use util.callbackify - callbackify examples to transform validateAsync into a callback node style if you really want to:
const Joi = require('joi');
const { callbackify } = require('util');
// A very simple data object
const obj = { a: 23 };
// A very simple joi schema which checks if schema is an object with the `a` property being a string
const schema = Joi.object({
a: Joi.string(),
});
// Callback style (which i don't recommend since its not available in `joi` package)
callbackify(() => schema.validateAsync(obj))((err, ret) => {
if (err) {
return res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
});
}
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
});
});
I would use what Joi provide, as in the following 2 examples:
// using async/await - you need to mark you controller as async, like:
// app.post('/test', async (req, res, next) => ...
try {
await schema.validateAsync(obj);
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
});
} catch (err) {
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
});
}
Or even with just promises:
schema
.validateAsync(obj)
.then(() =>
res.status(200).json({
status: 'success',
message: 'User created successfully',
data: Object.assign({ id }, value),
})
)
.catch(() =>
res.status(400).json({
status: 'error',
message: 'Invalid request data',
data: data,
})
);

Related

Form Data doesn't save the image correctly

I'm trying to save Form Data with its information but when I do so I get a weird looking object that I think the server is refusing to accept and returns a 500 error.
Here is how I'm handling it:
const formData = new FormData();
let currentlyUploaded = e.target.files[0];
formData.append(currentlyUploaded.name, currentlyUploaded);
// passing the data in function then ...
And this is how it looks when I console it later before the call, and it doesn't look correct.
here is the route:
router.post(
`${prefix}/create-quiz`,
userAuth,
upload.single("file"),
async (req, res) => {
try {
const user = await User.findOne({ _id: req.params.userId });
const newQuiz = new Quiz({
purchaseCoins: req.body.purchaseCoins,
privacy: req.body.privacy,
created_by: req.params.userId,
category: req.body.category,
title: req.body.title,
description: req.body.description,
difficulty: req.body.difficulty,
questions: req.body.questions,
thumbnail: req.file.filename,
});
await newQuiz.save();
user.quizzes.push(newQuiz._id);
await user.save();
return res.status(201).json({
message: "Finally , quiz created properly!",
success: true,
});
} catch (error) {
// return res.status(500).json({
// message: "Can't save this quiz try again, check if it already exists",
// success: false,
// });
console.log("errorium: ", error)
}
}
);

How do I add an object to a database in Knex where the object is the child of two parents?

I have the code below. Its a standard blog type of setup with users which have posts and comments. Comments are the child of both users and post . Posts belong just to users. Im having a problem posting to comments table. IM not getting any errors when using the insert function , however, when I post a comment to the database nothing gets saved to the comments table . If i do a request to retrieve the comments table , the table still shows empty. What am i doing wrong here .
server.post("/users/:id/posts/:id2/comments", async (req, res) => {
const userID = req.params.id;
const postID = req.params.id2;
db("users")
.where({ id: Number(userID)})
.then((user) => {
db('posts') .where({ id: Number(postID)})
.then((post) => {
//verify if post and user exists
if (post && user) {
req.body.content ? insertComment({
content: req.body.content,
user: userID,
post: postID
})
.then(
res.status(201).json(req.body)
)
.catch((err) => {
console.log(err);
})
: res.status(400).json({
errorMessage: "Please insert text .",
});
} else {
res.status(404).json({
message: "user not found",
});
}
})
})
.catch((err) => {
res.status(500).json({
err,
message: "Error processing request",
});
});
});
function insertComment(comment) {
return db("comments").insert(comment).where({
user: comment.user,
post: comment.post
});
}
since you're already using async function i'd first recommend to use async/await, second notice is that knex returns an array and not an object for example
db("users")
.where({ id: Number(userID)})
.then((user) => {
// user is an array
});
you can chain a query with .first() to retrieve the first object and not an array
Reference from knex documentation
using async/await could save you from callback hell
server.post("/users/:id/posts/:id2/comments", async (req, res) => {
const userID = req.params.id;
const postID = req.params.id2;
try {
const user = await db("users").where("id", Number(userID)).first();
const post = await db("posts").where("id", Number(postID)).first();
if (post && user) {
if (req.body.content) {
await insertComment({
content: req.body.content,
user: userID,
post: postID,
});
return res.status(201).json(req.body);
} else {
return res.status(400).json({
errorMessage: "Please insert text .",
});
}
} else {
return res.status(404).json({
message: "user or post not found",
});
}
} catch (err) {
return res.status(500).json({
err,
message: "Error processing request",
});
}
});
async function insertComment(comment) {
return db("comments").insert(comment).where({
user: comment.user,
post: comment.post,
});
}
and if you have lots of relationships in your application you might find it useful if you want to use an ORM like Objection as it is built on knex.

How to check if there are no more documents to update using findOneAndUpdate

So I am learning CRUD for a school project and I followed a tutorial that was really useful. However, when I completed it I noticed that when there are no more quotes to update, it still updates quotes. How can I change this so that it will stop updating quotes that arent even there?
app.put('/quotes', (req, res) => {
quoteCollection.findOneAndUpdate(
{ name: 'Yoda' },
{
$set: {
name: req.body.name,
quote: req.body.quote
}
},
{upsert: true}
)
.then(result => {
//The if block that i am trying
if (result.deletedCount === 0) {
return res.json('No quote to delete')
}
})
.catch(error => console.error(error))
})
Why are you passing {name: "Yoda}? This route is supposed to only update the quote with "Yoda" as its name? If not, then you need to grab from the request object the quote that should be updated.
I tried to create a different version, based on the assumption that the quote that should be updated will come from the req.body:
app.put("/quotes", async (req, res) => {
//Grab the name/id/identifier for the quote you want to update from the body
const query = req.body.name;
// Try to update the document on the database
try {
const result = await quoteCollection.findOneAndUpdate(
query,
{
name: req.body.name,
quote: req.body.quote,
},
{
upsert: true,
new: true,
}
);
// If it worked, it will return the updated quote
res.status(200).json({
status: 200,
data: {
result,
},
});
} catch (err) {
res.status(400).json({
status: 400,
message: "Something went wrong",
});
}
});

Node.js RESTful API validation error on array schema

I am creating a RESTful API using Node.js and mongoose by following the tutorial by Acedemind. I have got it working just fine and am now expanding it to allow the client to post several products in the same order. Basically I am modifying a simple "POST" request to be an array instead of some variables. My problem is that I run into a long validation error that hinders the array from being created. Here is the code for the post request:
router.post("/", async (req, res, next) => {
const productsMaybeFalses = await Promise.all(req.body.products.map(async ({ productId })=> {
const product = await Product.findById(productId);
if (!product) {
return false;
}
return {
...product,
productId,
}
}));
const errors = productsMaybeFalses
.map((productMaybeFalse, index) => {
return {
productMaybeFalse, index
}
})
.filter(({ productMaybeFalse }) => !productMaybeFalse)
if (errors.length) {
console.log(error);
return;
}
console.log(productsMaybeFalses);
const products = productsMaybeFalses
.filter((productMaybeFalse) => productMaybeFalse);
const order = new Order({
_id: mongoose.Types.ObjectId(),
products: products
});
return order.save().then(results => {
console.log(results);
res.status(201).json(results.map((result) => ({
message: "order stored",
createdOrder: {
_id: result._id
},
request: {
type: "GET",
url: "http://localhost:3000/orders/" + result._id
}
})));
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
})
})
})
And here is the Schema for the Order:
const mongoose = require("mongoose");
const pSchema = mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true},
quantity: { type: Number, default: 1}
});
const orderSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
products: [pSchema]
});
module.exports = mongoose.model("Order", orderSchema)
To keep this question from being too long I will only post the end part of the error. The part that I feel tells the most information abut the problem. If anyone wants the whole error message to better understand the problem and maybe come up with a solution for me I will be very willing to post it as well. Here it is:
kind: 'Array',
value: [Array],
path: 'products',
reason: TypeError: value[i].toObject is not a function
at DocumentArray.cast (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schema/documentarray.js:309:27)
at DocumentArray.SchemaType.applySetters (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schematype.js:755:12)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:922:18)
at model._handleIndex (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:740:14)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:697:22)
at model.Document (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:114:12)
at model.Model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:73:12)
at new model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:4324:13)
at router.post (/Users/axelhagman/Documents/Jacobs/api/routes/orders.js:70:17)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7) } },
_message: 'Order validation failed',
name: 'ValidationError' }
POST /orders/ 500 440.085 ms - 7622
I am very new to using node.js and creating API overall so any help would be very much appreciated. Thanks!

Not sending push notifications

I have a problem regarding push notifications. I have this error in firebase functions logs:
Error: To send a message with a payload, the subscription must have
'auth' and 'p256dh' keys.
exports.storePostData = functions.https.onRequest(
(request, response) => {
cors(request, response, () => {
admin.database().ref('posts').push({
id: request.body.id,
title: request.body.title,
location: request.body.location,
image: request.body.image
}).then(() => {
webpush.setVapidDetails('mailto: xxxx#gmail.com', 'BLl7xIPAyJNzsMi5vo_aG-4RdXdyZ4Q4ZFpTgnm902qN79MIiSORBk9N-rfFEGiKNPuJu5SJmUX35Wwce9nuH94', 'M8E6hw7jCmu7qNQJ88FV5o02OAiLefEFJK8jyJimk7g')
return admin.database().ref('subscriptions').once('value');
}).then(subscriptions => {
subscriptions.forEach(sub => {
var pushConfig = {
endpoint: sub.val().endpoint,
keys: {
auth: sub.val().keys,
p256dh: sub.val().p256dh
}
}
webpush.sendNotification(pushConfig, JSON.stringify({
title: 'New Post',
content: 'New post added',
openUrl: '/help'
})).catch(err => {
console.log(err)
})
})
response.status(201).json({
message: 'Data stored',
id: request.body.id
})
}).catch(err => {
response.status(500).json({
error: err
})
})
})
});
This is my function for storing post data, and i think the problem is here because it can't even get to push event in serviceWorker (i don't get any logs there).
On the client side, you should be able to retrieve subscription
On Angular it would be like
const sw = await navigator.serviceWorker.register('/assets/js/service-worker.js', { scope: '/assets/js/' });
let subscription = await sw.pushManager.getSubscription();
Through this way, you should be able to get subscription and subscription object contains p256dh and auth keys.
You should be able to see the values by
console.log("subscription: ", subscription?.toJSON());
Ye i figured it need to go as:
keys: {
auth: sub.val().keys.auth,
p256dh: sub.val().keys.p256dh
}

Categories