I'm building a simple API with Express and Sequelize JS. I tried to update an record with req.School but nothing happend. I shared the code below. Also I got no error for output. Can you help me ?
Controller js
module.exports.params = function(req, res, next, id) {
School.find({
where: {
id: id
}
})
.then(function(school) {
if (!school) {
next(new Error("School not found by given ID"));
}else {
req.School = school;
next();
}
}, function(err) {
next(err);
});
};
module.exports.delete = function (req,res,next) {
req.School.Status = SysEnums.Deleted;
School.update(req.School,{
where:{
id:req.School.id
}
}).then(function (deleted) {
res.json(deleted);
});
};
Route JS
var router = require("express").Router();
var controller = require("../Controllers/SchoolController");
router.param("id",controller.params);
router.route("/").get(controller.get);
router.route("/:id").get(controller.getOne).delete(controller.delete);
module.exports = router;
Since the params method, req.School is a sequelize object. Here is the params method.
module.exports.params = function(req, res, next, id) {
School.find({
where: {
id: id
}
})
.then(function(school) {
if (!school) {
next(new Error("School not found by given ID"));
}else {
req.School = school;
next();
}
}, function(err) {
next(err);
});
};
As #Shivam said it' s a sequelize object, I could use provided methods. I want to soft delete my record, so here is my delete method with Sequelize JS.
module.exports.delete = function(req, res, next) {
try {
req.School.Status = SysEnums.Deleted;
req.School.save().then(function(entity) {
res.json(entity);
});
} catch (e) {
console.log(e);
}
};
#PandhiBhaumik
Thank you for your help. I' ll try to use build method as a second option.
Related
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.
The app is using basic Node.js, Express, and MongoDB to return some data at different routes.
Route:
router.get('/api/v1/:collection', (req, res, next) => {
try {
const data = ApiController.getAllRecipes(req, res, next);
console.log(data);
res.send(data);
} catch(error) {
res.send(error);
}
});
The methods that query the database are contained within a simple controller:
module.exports = {
getAllRecipes: async (req, res, next) => {
try {
req.app.db.collection(req.params.collection).find({}).toArray((error, result) => {
if (result.length > 0) {
console.log(result);
return result;
} else {
let error = new Error();
error.statusCode = 404;
error.message = 'No API results found.';
next(error);
}
});
} catch (error) {
next(error);
}
}
};
Inside the controller, the console.log(result); returns the correct data that I would like to be handled within the route file. However, the console.log(data); in the Route file returns undefined, even within the try/catch block.
Am I missing something in passing the data from the controller back to the route? Thank you :)
Found a solution! Since the controller is asynchronous, it doesn't need to return data, but rather save it to res.locals and execute Express' next() method to continue with the route.
Updated Route:
router.get('/api/v:versionNumber/:collection', ApiController.getAllRecipes, (req, res, next) => {
res.send(res.locals.results);
});
Updated controller:
getAllRecipes: async (req, res, next) => {
try {
req.app.db.collection(req.params.collection).find({}).toArray((error, result) => {
if (result.length > 0) {
res.locals.results = result;
next();
} else {
let error = new Error();
error.statusCode = 404;
error.message = 'No API results found.';
next(error);
}
});
} catch (error) {
next(error);
}
}
I have this snippet of code in my controller but I'd like to filter the incoming records by a field archived == true in the MongoDB document.
Here is the filterless code that works right now. I'm not sure where to add the filter. I tried adding a simple filter() using prototype but it broke. What am I missing?
var Candidate = require('../models/candidate');
var async = require('async');
// Display list of all Candidate
exports.candidate_list = function(req, res, next) {
Candidate.find({}) //should something go in between the {}?
.sort([['name', 'ascending']])
.exec(function (err, list_candidates) {
if (err) { return next(err); }
//Successful, so render
res.render('candidate_list', { title: 'Candidates', list_candidates: list_candidates});
});
};
You can try this
var Candidate = require('../models/candidate');
var async = require('async');
// Display list of all Candidate
exports.candidate_list = function(req, res, next) {
Candidate.find({achived:true})
.sort([['name', 'ascending']])
.exec(function (err, list_candidates) {
if (err) { return next(err); }
//Successful, so render
res.render('candidate_list', { title: 'Candidates', list_candidates: list_candidates});
});
};
I want to create a custom middleware for passport-jwt to handle authentication.
here is what I have done to create my own middleware :
var models = require('../models');
var passport = require("passport");
var passportJWT = require("passport-jwt");
var config = require("../config/config.json");
var ExtractJwt = passportJWT.ExtractJwt;
var Strategy = passportJWT.Strategy;
var params = {
secretOrKey: config.jwtSecret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
/**
* jwt authentication strategy
*/
var strategy = new Strategy(params, function(payload, done) {
models.User.findById(payload.id)
.then((user)=>{
if (user) {
return done(null, {
id: user.id,
username : user.username
});
} else {
return done(new Error("User not found"), false);
}
}).catch((err)=>{
return done(err, false);
});
});
passport.use(strategy);
module.exports = {
initialize: function() {
return passport.initialize();
},
authenticate: (req, res, next)=>{
passport.authenticate('jwt', { session: false }, (err, user, info)=>{
if (err) { return next(err); }
if (!user) { return res.send("Custom Unauthorised").end(); }
// edit as per comment
//return res.send("Test Route Accessed").end();
req.user = user; // Forward user information to the next middleware
next();
})(req, res, next);
}
};
but everytime I type 'npm start' to run the app I face this error :
if (request.headers[AUTH_HEADER]) {
^
TypeError: Cannot read property 'headers' of undefined.
the authorization header is set in the request.
yes I did Find the answer here it is :
first define the strategy logic:
var strategy = new Strategy(params, function (payload, done) {
//finding the user in the database
console.log(payload);
models.users.findById(parseInt(payload.userId))
.then((user) => {
//if the user is found
if (user) {
return done(null, {
id: user.id,
username: user.username
});
} else {
return done(new Error("User not found"), null);
}
}).catch((err) => {
console.log(err);
return done(new Error("uncaught error! try again later"), null);
})
});
then make passport use that strategy"
passport.use(strategy);
and finally export the initialization function and the middleware function
module.exports = {
initialize: function () {
return passport.initialize();
},
authenticate: function (req, res, next) {
return passport.authenticate("jwt", {
session: false
}, (err, user, info) => {
if (err) {
console.log(err);
return next(err);
}
if (!user) {
return res.json({
status: 'error',
error: 'ANOTHORIZED_USER'
});
}
// Forward user information to the next middleware
req.user = user;
next();
})(req, res, next);
}
};
and then you can call the function authenticate defined above as a middleware in your routes.
here is an example :
//import the express router
var express = require('express');
var router = express.Router();
//here I am importing the functions defined above, I put them in the config folder
var jwt_login_strategy = require('../config/jwt-login-strategy');
//and finally use the jwt_login_strategy as a middleware
router.post('something', jwt_login_strategy.authenticate, your_other_middleware(req, res, next)=>{...});
you have to call the authenticate function without adding parentheses, just like this jwt_login_strategy.authenticate.
hope it will solve your problem as it did for mine.
Suppose each users has huge amount of data that no need to store those in single table. I want to store each user's data in separate database for faster query.
I want when any user logging in loopback, change datasets based on users connection strings that stored in User model.
I read all of loopback docs and try so many practice to do this but i can't implement this.
I try this in server/server.js:
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
app.datasources.db= ds;// <----- IT DOES'NT WORKING
next();
});
});
but IT DOES'NT WORKING (marked in code).
Any idea to solve this issue?
You can use attachTo() function to attach a datasource based on conditions.
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
//console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
// This way you can attach memory datasource to model.
// This only covers models defined using app.model();
var models = app.models();
models.foreach(function(model) {
model.attachTo(app.dataSources.memory);
});
next();
});
});
Also use defined datasource, memory in datasource.json.
{
"memory": {
"name": "memory",
"connector": "memory"
}
}
Please refer this too: app.models()