req.body undefined in custom middleware express - javascript

So I'm doing a simple NodeJS app with MongoDB/Express/Mongoose. In my mongoose schema I have a field (pictureURL) with a default value, the problem is that if pictureURL is an empty string the default value does not get applied. To solve this I though about using a custom middleware when doing the POST request either when creating or updating the model.
But the issue I'm having is that from within the middleware req.body is undefined. It is fine when in the router.post method but not in the middleware. Here is the code I have.
middleware (pictureU.js)
const app = require('../app');
const bookPictureUrl = (res, req, next) => {
console.log({ body: req.body });
if (!req.body.pictureUrl)
req.body.pictureUrl = 'images/default';
next();
};
module.exports = { bookPictureUrl };
book.routes.js
const app = require('../app');
const router = require('express').Router();
const Book = require('../models/Book.model');
const { bookPictureUrl } = require('../middleware/pictureUrl');
router.post('/update/:id', bookPictureUrl, async (req, res, next) => {
try {
req.body.authors = req.body.authors.split(',');
const data = await Book.findByIdAndUpdate(req.params.id, req.body);
res.redirect('/books');
} catch (err) {
next(err);
}
});
Any help trying to fix this so that I can use req.body within the middleware would be greatly appreciate.
Thanks

You mixed up your argument order. req should come before res.
const bookPictureUrl = (req, res, next) => {
console.log({ body: req.body });
if (!req.body.pictureUrl)
req.body.pictureUrl = 'images/default';
next();
};

Related

Handling Express Validator in a middleware

The classic way shown in tutorials would be:
Router.post('/add-post', addPostValidation(), addPost)
But what if I want to do the validation in a middleware like this:
The router:
Router.post('/add-post', addPost)
The middleware:
module.exports = (req, res, next) => {
if(req.method == 'POST') {
console.log('hello')
body('name').notEmpty()
let result = validationResult(req)
console.log(result)
}
next()
}
The "hello" shows up but the result won't show me any error if I do this way
You can access a validation chain's run function to do what you want to do.
const addPost = (req, res, next) => {
const validations = [
body('name').notEmpty(),
body('content').notEmpty(),
];
await Promise.all(validations.map(chain => chain.run(req)));
const result = validationResult(req);
// ...
};
Docs

How can I store Code Snippet in mongo db?

I want to create A post route So I Can store the User Typed code snippets into the collection in MongoDB.
the schema would look like this:-
const newSnippetSchema= new mongoose.Schema({
title:String,
snippet:String
})
to be brief I am working on creating a web app like codeSandbox or code-pen where I can save the code user has saved or typed....
I want to send data in Json format when Post Route is triggered
Create a http post web api.
const express = require('express')
const mongoose = require('mongoose')
const NewSnippetSchema = require('./models/newSnippetSchema')
const app = express()
mongoose.connect('mongodb://localhost/mydb', {
useNewUrlParser: true, useUnifiedTopology: true
})
app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended:false }))
app.post('/newSnippet', async (req, res) => {
await NewSnippetSchema.create({ title: req.body.title, snippet: req.body.snippet })
res.redirect('/')
})
app.listen(process.env.PORT || 5000);
catchAsync Code:
const catchAsync = (fn) =>
function asyncUtilWrap(...args) {
const fnReturn = fn(...args);
const next = args[args.length - 1];
return Promise.resolve(fnReturn).catch(next);
};
export default catchAsync;
AppError Code:
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
export default AppError;
Controller Code:
const snippetController = catchAsync(async (req, res, next) => {
const { title, snippet } = req.body;
if (!title || !snippt) {
return next(new AppError('All fields are required', 400));
}
const snippetDoc = await Snippet.create(req.body);
return res.status(201).json({
status: 'success',
snippetDoc
});
});
export default snippetController;
Router Code:
router.route('/snippet').post(snippetController);
The database has nothing to do with this. It doesn't matter how you store it, what matters is how render it in your HTML representation on the UI.
Just use the built-in HTML encoding function to encode the code snippet before storing it in the database.(eg - encode all & as & something like this).
After fetching the data, decode it back before rendering it on the UI.

Async Express Router Function to MongoClient

I'm using Nodejs Express, the router middleware, in this case, MongoDB and using the POST method via JQuery ajax. Does anyone know why the
router.post doesn't call, but a .get works.
Does anyone know why the router.post
doesn't get triggered when called as a Promise/async.
Async route here doesn't get called on ajax post.
async function asyncMain() {
const uri = "mongodb endpoint";
const client = new MongoClient(uri)
try {
await client.connect().then(console.log('Connected to MongoDB'));
//keeps awaiting cause it never executes.
await pushToDB(client)
//all mongoClient functions work with await here
//Express http Post doesn't work with await here
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
asyncMain().catch(console.error);
async function pushToDB(client) {
router.post('/', async(req, res, next) => {
const result = await client.db('user').collection('notes').insertOne(req.body);
console.log(`Results ${ result.insertedId }`);
})
}
I tried using this async Middleware implementation with no results.
Such as wrapping the post function's callback in asyncMiddleware to no avail.
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
My working sync function for reference.
function main() {
const uri = "mongodb endpoint";
const client = new MongoClient(uri)
client.connect().then(console.log('connected to DB'));
router.post('/', (req, res) => {
const result = client.db('user').collection('notes').insertOne(req.body);
console.log(`Results ${ result.insertedId }`);
})
}

CastError: Cast to ObjectId failed for value "customers" at path "_id" for model "customer"

I was hoping someone can help me figure out what I'm doing wrong here. I've been searching and I can find a lot of similar issues, but nothing that I'm smart enough to solve my issue from. I'm getting the following error:
CastError: Cast to ObjectId failed for value "customers" at path "_id" for model "customer"
It was working before and I managed to break it, and I undid everything I thought I changed and I'm still getting the error.
Here is my Schema:
const Schema = mongoose.Schema;
const CustomerSchema = new Schema({
custName: {
type: String,
required: true
},
custStreet: {
type: String
},
custCity: {
type: String
},
custState: {
type: String
},
custZip: {
type: String
}
});
module.exports = Customer = mongoose.model('customer', CustomerSchema);
My Routes:
const router = express.Router();
const customerController = require('../../controllers/customer')
const Customer = require('../../models/Customer');
router.route('/')
.get(customerController.index)
.post(customerController.newCustomer);
router.route('/:customerID')
.get(customerController.getCustomer)
module.exports = router;
And my controller:
module.exports = {
index: async (req, res, next) => {
try {
const customers = await Customer.find({})
res.status(200).json(customers);
} catch(err) {
next(err);
}
},
newCustomer: async (req, res, next) => {
try {
const newCustomer = await Customer(req.body);
const customer = await newCustomer.save();
res.status(201).json(customer);
} catch(err) {
next(err);
}
},
getCustomer: async(req, res, next) => {
try {
const { customerID } = req.params;
const customer= await Customer.findById(customerID);
res.status(200).json(customer);
} catch(err) {
next(err);
}
}
};
Also, here is the whole message I get:
CastError: Cast to ObjectId failed for value "customers" at path "_id" for model "customer"
at model.Query.exec (C:\Users\natha\Desktop\Coding\HAMMER\node_modules\mongoose\lib\query.js:4371:21)
at model.Query.Query.then (C:\Users\natha\Desktop\Coding\HAMMER\node_modules\mongoose\lib\query.js:4463:15)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
Something else that is confusing me, I have another Route file and Controller for another collection in my database. It's basically the same code, except instead of routing to '/:Customers' it routes to '/:Tickets' and it works just fine. I don't see how this code is working and the Customers code is not.
Routes:
const router = express.Router();
const ticketController = require('../../controllers/ticket');
router.route('/')
.get(ticketController.index)
.post(ticketController.newTicket);
router.route('/:ticketID')
.get(ticketController.getTicket)
.put(ticketController.replaceTicket)
.patch(ticketController.updateTicket);
module.exports = router;
Controller:
module.exports = {
index: async(req, res, next) => {
try {
const tickets = await Ticket.find({})
res.status(200).json(tickets);
} catch(err) {
next(err);
}
},
newTicket: async (req, res, next) => {
try {
const newTicket = await Ticket(req.body);
const ticket = await newTicket.save();
res.status(201).json(ticket);
} catch(err) {
next(err);
}
},
getTicket: async(req, res, next) => {
try {
const { ticketID } = req.params;
const ticket = await Ticket.findById(ticketID);
res.status(200).json(ticket);
} catch(err) {
next(err);
}
},
replaceTicket: async(req, res, next) =>{
try {
const { ticketID } = req.params;
const newTicket = req.body;
const result = await Ticket.findByIdAndUpdate(ticketID, newTicket);
res.status(200).json({ Success: true });
} catch(err) {
next(err);
}
},
updateTicket: async(req, res, next) =>{
try {
const { ticketID } = req.params;
const newTicket = req.body;
const result = await Ticket.findByIdAndUpdate(ticketID, newTicket);
res.status(200).json({ Success: true });
} catch(err) {
next(err);
}
}
};
I'd appreciate any help!
Thanks
-N8
I found the problem. I had this in my server.js file:
app.use('/', customers);
app.use('/customers', customers);
app.use('/materials', materials)
app.use('/tickets', tickets)
Once I got rid of the app.use('/', customers); all is good.
You have an error in getCustomer handler.
You shouldn't use the string id to find the customer, instead, you must first use mongoose.Types.ObjectId(STRING_ID).
getCustomer: async(req, res, next) => {
try {
const { customerID } = req.params;
const customer= await Customer.findById(mongoose.Types.ObjectId(customerID));
res.status(200).json(customer);
} catch(err) {
next(err);
}
}
I upvoted the solution of the OP. To save a lot of time, I just want to explain why that is an issue and what someone else could look out for as I. So in OP server.js, '/' comes before other routes which defy the rule to list specific routes first. Meaning, if '/' was moved after other routes problem is solved.
More importantly, where does this error come from? when you call any of the other routes, your app calls '/' first and uses the customers' routes while passing the route paths (i.e. '/customers', '/materials', '/tickets') as id params and then mongo is trying to cast customer, materials, and tickets as an id inside the customer collection.
app.use('/customers', customers);
app.use('/materials', materials)
app.use('/tickets', tickets)
app.use('/', customers);
This will have solved the problem for OP.
In my case this was the problem:
router.get('/', getAllUser);
router.get('/:id', getUserById)
router.get('/facebook', facebookPassport)
router.get('/google', customersPassport);
So here I get this error Cast error for value "facebook"/"google" at path "_id" for model User because the strings, "facebook" and "google" in the last two routes are specific but will never be reached because the '/:id' route will accept any value that comes after the '/' as an id. When I rearrange like this:
router.get('/', getAllUser);
router.get('/facebook', facebookPassport)
router.get('/google', customersPassport);
router.get('/:id', getUserById)
Take away: move specific routes to the top of dynamic routes (routes with params) both at the app level and in your route files.

node JS expreess UNDEFIED CALL BACK

I am working on a personal project and I do not have much experience with nodeJS, the idea is to bring a JSON that has remotely taken some data and generate some statistics, I am doing some tests before starting fully in the project and I am having problems with the callback.
the server.js works correctly,
my module is the following:
const extjson = require('remote-json');
//---------------------API CONFIG--------------------------
//apikey
const apikey ="xxxxxxxxxxxxxxxxxxxxx";
function get_sum_id(sumname){
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
var id;
extjson(urlsumbySumName).get((err, res, body)=> {
id = body.id;
});
return id;
}
module.exports = {get_sum_id
};
and the routes.js is the following:
const riot = require('./rapi.js');
const express = require('express');
//---------------------------------------------------------
const router = express.Router();
//Jtask -- task remote json
//const Task = require('../models/Task'); // taskdb
router.get('/',async (req, res) => {
res.render('index');
});
router.post('/profile', (req,res)=>{
const sum = req.body.summoners;
console.log(riot.get_sum_id(sum));
res.render('profile',{sum});
});
module.exports = router;
I want to show that id by console and it returns undefined, the idea is to pass that value to the render below to have it available in an EJS document.
Your module make an asynchronous call to another server with remote-json. It means that the callback will be called only after the request to this other server. So, this line return id; is read before this line id = body.id;.
One way to fix that is to provide the callback from the place where you call your module function.
Based on your code you could do something like that :
// module.js
const extjson = require('remote-json');
const apikey ="xxxxxxxxxxxxxxxxxxxxx";
function get_sum_id (sumname, callback) {
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
extjson(urlsumbySumName).get(callback);
}
module.exports = { get_sum_id };
// app.js
const riot = require('./rapi.js');
const router = express.Router();
router.post('/profile', function(req, res, next) {
riot.get_sum_id(req.body.summoners, function (err, resp, body) {
console.log(body);
res.json(body); // Response here
});
});
module.exports = router;
Now, requests to your server will be in pending until your callback close it with res.json(body);.
Thank you very much I am working, now I understand much better how the asynchronous functions work. I leave here the complete solution to my problem in case someone needs it in the future:
//rapi.js
const extjson = require ('remote-json');
//---------------------API CONFIG--------------------------
//apikey
const apikey ="RGAPI-77f658f1-ff2b-40e7-a74c-47f7510c8dac";
//trayendo los datos desde riot
function get_sum_id(sumname, callback){
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
extjson(urlsumbySumName).get(callback)
}
module.exports = { get_sum_id };
//routesapp.js
const riot = require('./rapi.js');
const express = require('express');
//---------------------------------------------------------
const router = express.Router();
router.get('/',async (req, res) => {
res.render('index');
});
router.post('/profile', (req, res, next)=>{
const sum = req.body.summoners;
riot.get_sum_id(sum,function (err, resp, body){
console.log(body.id);
//responces....
res.render('profile',{sum, id: body.id})
});
});
module.exports = router;
TNX very much!

Categories