Related
I have been struggling with this issue for more than a day now. I am building a backend API for a food delivery web and mobile app.
I have setup passport with the local strategy to authenticate my users.
I have two different types of users currently 1. Subscribers 2. Restaurants each using their own mongoose model (shown in my code below)
I initially set everything up for subscriber registration login, authentication sessions and cookies, all was working fine.
I then moved on to setting up passport for my Restaurant users so that a a restaurant would be able to login to their profile and make various changes to their restaurant and its listings etc.
What I found was that I was able to create an account for the Restaurant user but when trying to log the user in passport would consistently return "Unauthorized", when trying to us isAutheticated() I got the same response back. Passport would however happily keep creating restaurant users every time I hit my register endpoint. A Restaurant user would appear in my database but when trying to log them in and store a cookie, I would just get returned "unauthorized".
With Subscribers all works fine, I have check authentication on my restricted endpoints, register new Subscribers and login subscribers with 0 issues.
Through my testing I have realized a couple things
Passport doesn't make use of the local strategy at all, I can literally black the whole thing out and it will still register subscribers log them in and check authentication via cookies with 0 issues. When placing a console.log into the strategy I can see the strategy is literally not being called at all. How could this be happening, surely passport needs the strategy in order to do its job? In this case its seems not, WHY?
Initially I tried to create a separate strategy for each of my Models, one strategy for Subscribers and one strategy for Restaurants using .createStrategy() this returned the same issue, all functionality for Subscriber model users worked fine but as soon as I tried to login or store a cookie for a user from my Restaurant model passport would just return "Unauthorized. Then you will see in the below code under server.js that I tried to create one local strategy and then build an if else in so passport checks both Subscriber and Restaurant models, through doing this I realized passport wasn't using the strategy at all.
I am ready to tear my hair out at this point what is the point of creating a strategy is passport is somehow just bypassing it completely. If I try to log a Subscriber in with the wrong credentials passport does its job and doesn't allow access so how is it actually managing authentication?
Below is my code, anyone who could help with this would more than make my day!
Server.js:
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const app = express();
const cors = require("cors")
const bodyParser = require("body-parser");
const passport = require("passport");
const session = require("express-session");
const LocalStrategy = require('passport-local').Strategy;
mongoose.connect(process.env.DATABASE_URL)
const db = mongoose.connection
db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))
// app.use(express.json())
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
// console.log(req.originalUrl)
if (req.originalUrl === "/subscribers/webhook") {
next();
} else {
bodyParser.json()(req, res, next);
}
});
app.use(cors())
//INITIAL STRATEGY i TRIED
// passport.use(new LocalStrategy(
// function (username, password, done) {
// Subscriber.findOne({
// username: username
// }, function (err, user) {
// if (err) {
// return done(err);
// }
// if (!user) {
// return done(null, false);
// }
// if (!user.verifyPassword(password)) {
// return done(null, false);
// }
// return done(null, user);
// });
// }
// ));
app.use(session({
secret: "foodsecrets",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
// sTRATEGY i WROTE TO TRY SEPERATE THE MODELS BEFORE I REALIZED THE STRATEGY WAS NOT BEING CALLED AT ALL
passport.use(new LocalStrategy(function (username, password, done) {
Subscriber.findOne({
username: username,
password: password
}, function (err, user) {
console.log("called")
// first method succeeded?
if (!err && user && passwordMatches(password)) {
return done(null, user);
}
// no, try second method:
Restaurant.findOne({
name: username,
password: password
}, function (err, user) {
// second method succeeded?
if (!err && user && passwordMatches(password)) {
return done(null, user);
}
// fail!
done(new Error('invalid user or password'));
});
});
}));
const subscribersRouter = require("./routes/subscribers")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")
const seederRouter = require("./routes/seeder");
app.use("/subscribers", subscribersRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)
app.use("/seeder", seederRouter)
app.listen(3000, () => {
console.log("Server has started on port 3000")
});
Subscriber Model:
const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");
const cartSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
categories: {
type: Array,
required: true
},
rating: {
type: String
},
restaurantname: {
type: String
}
});
const favouritesSchema = new mongoose.Schema({
favouritemeals: {
type: Array
},
favouriteresturants: {
type: Array
}
});
const pendingItemsSchema = new mongoose.Schema({
name: String,
price: Number,
description: String
});
const pendingOrderSchema = new mongoose.Schema({
userID: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
items: [pendingItemsSchema],
removeItem: {
type: String
},
orderData: {
type: Date,
required: true,
default: Date.now
},
status: {
type: String
}
});
const subscriberSchema = new mongoose.Schema({
googleID: {
type: String
},
facebookID: {
type: String
},
username: {
type: String,
required: true
},
email: {
type: String,
},
subscribeData: {
type: Date,
required: true,
default: Date.now
},
orderHistory: {
type: Array,
},
favourites: {
favouritesSchema
},
cart: [cartSchema],
login: {
type: String,
},
pendingOrder: [pendingOrderSchema],
stripeCustId: {
type: String,
required: true
},
role:{
type: String,
// required: true
}
});
subscriberSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("subscriber", subscriberSchema);
Subscribers.js all required
const express = require("express");
const router = express.Router();
const Subscriber = require("../models/subscriber");
const Restaurant = require("../models/restaurant");
const passport = require("passport");
const Order = require("../models/order");
const bodyParser = require("body-parser");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const facebookStrategy = require('passport-facebook').Strategy;
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET
// passport.use(Subscriber.createStrategy());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
Subscriber.findById(id, function (err, user) {
done(err, user);
});
});
Subscribers.js Login endpoint:
// LOGIN USING PASSPORT JS
router.post("/login", (req, res) => {
const subscriber = new Subscriber({
username: req.body.username,
password: req.body.password,
email: req.body.email
});
req.login(subscriber, async function (err) {
if (err) {
console.log(err)
} else {
try {
passport.authenticate("LocalStrategy")(req, res, function () {
console.log("Authenticated")
res.status(201).json("authenticated")
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
})
Subscribers.js Register Endpoint:
// REGISTER USING PASSPORT JS
router.post("/register", async (req, res) => {
const customer = await stripe.customers.create({
name: req.body.username,
email: req.body.email
});
console.log("customer", customer)
Subscriber.register({
username: req.body.username,
email: req.body.email,
stripeCustId: customer.id
}, req.body.password, async (err, subscriber) => {
if (err) {
console.log(err)
} else {
try {
await passport.authenticate("local")(req, res, function () {
console.log("is authenticated")
res.status(201).json(newSubscriber)
})
const newSubscriber = await subscriber.save()
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
});
})
Restaurant Model:
const mongoose = require("mongoose")
const passportLocalMongoose = require("passport-local-mongoose");
const menueItemSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
categories: {
type: Array,
required: true
},
rating: {
type: Number
},
restaurantname: {
type: String
}
})
const activeOrderSchema = new mongoose.Schema({
userID: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
items: [menueItemSchema
],
orderData: {
type: Date,
required: true,
default: Date.now
},
status: {
type: String
}
})
const restaurantSchema = new mongoose.Schema({
username: {
type: String,
required: true
},
email: {
type: String,
},
src: {
type: String,
required: true
},
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
menue: [menueItemSchema],
rating: {
type: String
},
categories: {
type: String
},
subscribeData: {
type: Date,
required: true,
default: Date.now
},
activeOrders: [activeOrderSchema]
})
restaurantSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("restaurant", restaurantSchema)
Restaurants.js all required:
const express = require("express")
const router = express.Router()
const Restaurant = require("../models/restaurant")
const passport = require("passport");
const randomRest = require("randomrestgenerator")
// passport.use(Restaurant.createStrategy());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
Restaurant.findById(id, function (err, user) {
done(err, user);
});
});
Restaurants.js Login endpoint:
router.post("/login", (req, res) => {
const restaurant = new Restaurant({
username: req.body.username,
password: req.body.password,
email: req.body.email
});
req.login(restaurant, async function (err) {
if (err) {
console.log(err)
} else {
try {
passport.authenticate("local")(req, res, function () {
console.log("Authenticated")
res.status(201).json("authenticated")
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
})
Restaurants.js Register endpoint
// PASSPORT JS RESTAURANT REGISTRATION
router.post("/register", async (req, res) => {
const randomRestaurant = randomRest()
Restaurant.register({
username: req.body.username,
email: req.body.email,
src: randomRestaurant.img,
title: randomRestaurant.title,
description: randomRestaurant.description,
menue: randomRestaurant.menue,
rating: randomRestaurant.rating,
categories: randomRestaurant.categories
}, req.body.password, async (err, restaurant) => {
if (err) {
console.log(err)
} else {
try {
console.log("try called")
const newRestaurant = await restaurant.save()
await passport.authenticate("rest")(req, res, function () {
console.log("is authenticated")
res.status(201).json(newRestaurant)
})
} catch (err) {
console.log("here!")
res.status(400).json({
message: err.message
})
}
}
});
I managed to fix this a couple days ago, turns out I had left my JSON response outside of the passport.authenticate callback function in the restaurants login endpoint.
I moved serialize and deserialize into server.js as well as my session and passport initialization and passport session .use
Also had to setup my serialize and desearialize user functions with if elses' so that they serviced both models.
Then lastly I added an array of secrets to my session instead of just one string.
All is working fine now.
passport.serializeUser(function (user, done) {
if (user instanceof Subscriber) {
done(null, {
id: user.id,
type: "Subscriber"
});
console.log("sub user")
} else {
console.log("rest user")
done(null, {
id: user.id,
type: "Restaurant"
})
}
});
passport.deserializeUser(function (id, done) {
console.log("de-serialize called")
console.log("id type", id.type)
console.log("ID", id)
if (id.type === "Subscriber") {
Subscriber.findById(id.id, function (err, user) {
done(err, user);
})
} else {
Restaurant.findById(id.id, function (err, user) {
done(err, user);
})
}
});
// Creating one
router.post('/', async (req, res) => {
const post = new Post({
title: req.body.title,
category: req.body.category,
content: req.body.content,
author: req.body.author,
postDate: req.body.postdate,
postTags: req.body.posttags
})
try {
const newPost = await post.save()
res.status(201).json(newPost)
} catch (err) {
res.status(400).json({ message: err.message })
}
})
I am working with API server in node but after asking for the POS call from client end the response is
post validation failed: title: Path title is required.
the model for mondoDB is
const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
category:{
type: String,
required : true
},
content: {
type: String,
required: true
},
author :{
type : String,
required : true
},
postDate: {
type: Date,
required: true,
default: Date.now
},
postTags : {
type : Array,
default : ["Coding"]
}
})
module.exports = mongoose.model('post', postSchema)
Given you are using express. This goes in the main file after app initialization. So that your application is able to parse data from the json it is receiving from front end.
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
In case you don't use body parser you won't be able to access those data. So please check that out.
For those who not installed body parser follow this steps.
Run this common on terminal/cmd on project root path (installing body parser)
npm i body-parser
Import bodyParser on main file
var bodyParser = require('body-parser');
Use middle wire ( Choose only one that's correct for you based on the type of data you pass to POST request)
a. router.use(bodyParser.json({ type: 'application/*+json' })); // parse various different custom JSON types as JSON
b. router.use(bodyParser.raw({ type: 'application/vnd.custom-type' })); // parse some custom thing into a Buffer
c. router.use(bodyParser.text({ type: 'text/html' })); // parse an HTML body into a string
Also do your imports above
// Creating one
router.post('/', async (req, res) => {
const post = new Post({
title: req.body.title,
category: req.body.category,
content: req.body.content,
author: req.body.author,
postDate: req.body.postdate,
postTags: req.body.posttags
})
try {
const newPost = await post.save()
res.status(201).json(newPost)
} catch (err) {
res.status(400).json({ message: err.message })
}
})
More on body parser : https://www.npmjs.com/package/body-parser
I'm building a mock facebook app using MERN Stack. When I try to save posts to my database, it keeps throwing two errors. One is in the back end and says TypeError: Cannot read property 'create' of undefined and the other is in the front end and says Unhandled Rejection (Error): Request failed with status code 500
This is my front end API.js page
import axios from "axios";
export default {
// Gets all posts
getPosts: function() {
return axios.get("/api/users/posts");
},
// Gets the post with the given id
getPost: function(id) {
return axios.get("/api/users/posts/" + id);
},
// Deletes the post with the given id
deletePost: function(id) {
return axios.delete("/api/users/posts/" + id);
},
// Saves a post to the database
savePost: function(postData) {
return axios.post("/api/users/posts", postData);
}
};
This is my handleSubmit function
handleFormSubmit = (event) => {
// Preventing the default behavior of the form submit (which is to refresh the page)
event.preventDefault();
// Saving post to database
API.savePost(this.state)
.then(data => {
console.log("data: ", data);
this.setState({
title: data.data.title,
body: data.data.body,
});
});
};
This is my back end postController.js, where TypeError: Cannot read property 'create' of undefined is being thrown.
const db = require("../models");
console.log("--------------------------------------");
console.log("Controller Reached");
console.log("--------------------------------------");
// Defining methods for the postController
module.exports = {
findAll: function(req, res) {
console.log("----------------------findAll------------------------ ");
console.log("req.query: ", req.query);
db.User.posts
.find(req.query)
.sort({ date: -1 })
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
findById: function(req, res) {
db.User.posts
.findById(req.params.id)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
create: function(req, res) {
console.log("create func");
console.log("req.body: ", req.body);
db.User.posts
.create(req.body) //error here
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
update: function(req, res) {
db.User.posts
.findOneAndUpdate({ _id: req.params.id }, req.body)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
remove: function(req, res) {
db.User.posts
.findById({ _id: req.params.id })
.then(dbModel => dbModel.remove())
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
}
};
And these are my backend API routes for the posts (I removed other routes that weren't related to the issue)
const router = require("express").Router();
const db = require("../../models");
const passport = require("passport");
const postController = require("../../controllers/postController");
router
.route("/posts")
.post(postController.create)
.get(postController.findAll)
.put(postController.update)
.delete(postController.remove);
console.log("/posts reached");
// Matches with "/api/books/:id"
router
.route("/posts/:id")
.get(postController.findById)
.put(postController.update)
.delete(postController.remove);
//router get for login >>> router.get("/")
//router post for logout
//router.get for profile page
module.exports = router;
edit: This is my user model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const passport = require("passport");
const Schema = mongoose.Schema;
//const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new Schema({
email: {type: String, required: true},
password: {type: String, required: true},
firstname: String,
lastname: String,
following: [{
User: String,
id: {type: mongoose.Schema.Types.ObjectId }
}],
followers: [{
User: String,
id: {type: mongoose.Schema.Types.ObjectId }
}],
posts: [{
title: String,
body: String,
postedBy: {type: mongoose.Schema.Types.ObjectId},
dateCreated: Date,
comments: [{body:"string", by: mongoose.Schema.Types.ObjectId}],
}],
dateCreated: Date,
savedFiles:[{}],
favoritePosts: [],
avatarImage: [{Image: String}],
jumboImg: [{Image: String}],
profile: {
job: String,
location: String,
school: String,
bio: String,
interests: []
}
});
var User = (module.exports = mongoose.model("User", userSchema));
module.exports.createUser = function (newUser, callback) {
console.log("createUser - newUser", newUser)
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(newUser.password, salt, function (err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
};
module.exports.getUserByEmail = function (email, callback) {
console.log("getUserByEmail", email)
var query = { email: email };
console.log(query);
User.findOne(query, callback);
};
module.exports.getUserById = function (id, callback) {
console.log("getUserById", id);
User.findById(id, callback);
};
module.exports.comparePassword = function (candidatePassword, hash, callback) {
console.log("comparePassword")
bcrypt.compare(candidatePassword, hash, function (err, isMatch) {
if (err) throw err;
callback(null, isMatch);
});
};
var LocalStrategy = require("passport-local").Strategy;
passport.use(
new LocalStrategy({ usernameField: "email" }, function (
email,
password,
done
) {
console.log("LocalStrategy");
User.getUserByEmail(email, function (err, user) {
if (err) throw err;
if (!user) {
return done(null, false, { message: "Unknown User" });
}
User.comparePassword(password, user.password, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Invalid password" });
}
});
});
})
);
passport.serializeUser(function (user, done) {
console.log("serializeUser", user.id)
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
console.log("deserializeUser", id);
User.getUserById(id, function (err, user) {
console.log("deserializeUser - user", `name="${user.name}" \nemail="${user.email}"\npassword=${user.password} `);
done(err, user);
});
});
Any help would be greatly appreciated.
User.posts is undefined because .posts is a property of instance of User. Thus you need to instantiate the user first. In this case, by finding an existing object from User collection.
Since you defined User.posts as primitive arrays, not reference to another collection, the code will be something like below.
create: function (req, res) {
// 1. find the existing user (I guess passport does the job)
db.User.findById(req.body.userid).then((user) => {
// 2. add an post
user.posts.push({
title: req.body.title,
body: req.body.body,
postedBy: req.body.userid,
dateCreated: Date.now(),
comments: [],
});
// 3. persist the changes
user.save();
});
}
If you want to separate the collection, which I believe better, you need to create a new object on the separated collection with the reference to the user posted.
// Post schema
var postSchema = new Schema({
title: String,
body: String,
postedBy: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
dateCreated: Date,
comments: [{ body: "string", by: mongoose.Schema.Types.ObjectId }],
});
// The controller
create: function(req, res) {
// 1. find the existing user (or you get id from Passport session)
db.User.findById(req.body.userid).then((user) => {
// 2. add an post set "postedBy" as the user
return Post.create({
postedBy: user._id,
title: req.body.title,
body: req.body.body,
dateCreated: Date.now(),
});
});
}
Here is the official documentation about referencing: https://mongoosejs.com/docs/populate.html
Hope this helps.
I m trying to use populate( ) in node js.
Well I m trying to access, objectId of one collection into another collection.
for eg., i have collections called Project and events,
where i have schema like this.
Project schema:
const projectSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
projectName: {
type: String,
required: true,
unique: true
},
dimensions: {
type: [],
required: false
},
events: {
[type: mongoose.Schema.Types.ObjectId],
ref: 'EnrichedEvent'
},
});
module.exports = mongoose.model('Project', projectSchema);
Events Schema:
const enrichedEventSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
projectId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
required: true
},
name: {
type: String,
required: true
},
type: {
type: String,
enum: ["Enriched"],
required: true
},
source: {
type: String,
required: true
},
});
and the routing code for projects to :
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Project = require("../models/project");
router.get("/", (req, res, next) => {
Project.find()
.populate('source', 'EnrichedEvents') //When i use this populate method, I m not able to GET any events on to browser..
.exec()
.then(docs => {
const response = {
count: docs.length,
projects: docs.map(doc => {
return {
projectName: doc.projectName,
dimensions: doc.dimensions,
events: doc.events,
_id: doc._id
};
})
};
res.status(200).json(response);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
router.post("/", (req, res, next) => {
const project = new Project({
_id: new mongoose.Types.ObjectId(),
projectName: req.body.projectName,
events: req.body.events
});
project
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Created project successfully",
createdProject: {
projectName: result.projectName,
_id: result._id,
events: result.events,
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
module.exports = router;
my problem is I can't auto populate the enriched eventsId in projects page.
for eg. Whenever i update events they should appear in projects page.but its not happening. In events page also i am not getting corresponding projectId. please point me in right direction.
Populate method takes FIELD_TO_POPULATE and FIELDS_TO_RETURN_FROM_POPULATED_DOC and you are passing it the reversed way. Also in your project schema you have used events for EnrichedEvent, so while populating use events not EnrichedEvent;
Try the following:
From:
.populate('source', 'EnrichedEvents')
TO:
.populate('events', 'EnrichedEvent')
Edit:
update your schema for EnrichedEvent:
events: [{
mongoose.Schema.Types.ObjectId,
ref: 'EnrichedEvent'
}]
It should work now.
I tried to use a similar .populate('...', '...') method but unsuccessfully. I would use
.populate('source').populate('EnrichedEvent')
or ('EnrichedEvents') depending on what you have defined in your schema.
I have the following files. I have a recipe.js file which outlines the Mongoose Schema for a recipe and the comments for a recipe. The code for it goes as follows:
const express = require('express');
const mongoose = require('mongoose');
const User = require('../models/user');
let Schema = mongoose.Schema;
let commentSchema = Schema({
rating: {
type: Number,
// required: true,
min: 1,
max: 5,
},
recipeItem: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Recipe'
},
comment: {
type: String,
// required: true
},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
likedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
favouredBy: {
type: mongoose.Schema.Types.ObjectId
}
});
let Comment = mongoose.model('Comment', commentSchema);
let recipeSchema = Schema({
name: {
type: String,
required: true
},
description: {
type: String,
},
steps: {
type: String,
required: true,
},
ingredients: {
type: Array,
required: true
},
comments: [commentSchema],
category: {
type: String,
required: true,
},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
/// So I learnt that by defining the string as "Recipe" in the model function, I will have to lower case it
/// and pluralize it when I use it with res.json and other such things (i.e. "Recipe" => recipes).
let Recipe = mongoose.model('Recipe', recipeSchema);
module.exports = Recipe;
module.exports = Comment;
/// refactor this so that these are in the router, not in the models file
/*
module.exports.getRecipeByName = (name, callback) => {
let nameQuery = {name: name};
Recipe.findOne(nameQuery, callback);
};
module.exports.getRecipesByCategory = (category, callback) => {
Recipe.find({'category': category});
};
*/
I also have a user.js file where I outline a a User model and the relation it has to the other models/schemas. The code in the file is as follows:
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const passportLocalMongoose = require('passport-local-mongoose');
const passport = require('passport');
let Schema = mongoose.Schema;
let User = Schema({
name: {
type: String
},
// The passport plugin already inputs username and password into our Schema
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true,
},
profilePic: {
type: String
},
email: {
type: String,
unique: true,
required: true
},
admin: {
type: Boolean,
defualt: false
},
usersRecipes: [{type: Schema.Types.ObjectId, ref:'Recipe'}],
userComments: [{type: Schema.Types.ObjectId, ref: 'Comment'}],
usersFavouriteRecipes: [{type: Schema.Types.ObjectId, ref: 'Recipe'}],
usersLikedRecipes: [{type: Schema.Types.ObjectId, ref: 'Recipe'}]
});
let options = ({missingPasswordError: "Incorrect password, try again"});
User.plugin(passportLocalMongoose, options);
module.exports = mongoose.model('User', User);
And here is recipeRouter.js, the file where I define all the HTTP requests and routes:
const express = require('express');
const passport = require('passport');
const Recipe = require('../models/recipe');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const verification = require('../verification');
const Comment = require('../models/recipe');
// I temporarily removed verification.checkIfUserExists to see if all this database stuff works
router = express.Router();
router.use(bodyParser.json());
router.get('/', (req, res) => {
res.json('Here are the recipes!')
});
router.get('/showrecipes', (req, res) => {
Recipe.find({}).populate('Comment').exec((err, recipes) => {
if (err) throw err;
res.json(recipes);
})
});
router.get("/showrecipes/:recipeId", (req, res) => {
let nameQuery = {_id: req.params.recipeId};
Recipe.findOne(nameQuery, (err, recipes) => {
if (err) throw err;
res.json(recipes);
})
//// Don't know if this is correct
.populate('comment.recipeItem');
});
router.get('/showrecipes/category/:categoryname', (req, res) => {
let nameQuery = {category: req.params.categoryname};
Recipe.find(nameQuery, (err, recipes) => {
if (err) throw err;
res.json(recipes);
});
});
router.post('/addrecipe', (req, res, next) => {
Recipe.create({
name: req.body.name,
description: req.body.description,
steps: req.body.steps,
ingredients: req.body.ingredients,
category: req.body.category
}, (err, recipes) => {
if (err) throw err;
res.json(recipes);
});
});
// See if this works
router.put("/showrecipes/:recipeId", (req, res) => {
let query = {_id: req.params.recipeId};
Recipe.findByIdAndUpdate(query, {
$set: req.body
}, {
new: true
}, (err, recipe) => {
if (err) throw err;
res.json(recipe)
})
});
router.delete("/showrecipes/:recipeId", (req, res) => {
let query = {_id: req.params.recipeId};
Recipe.findByIdAndRemove(query, (err, recipe) => {
if (err) throw err;
res.send('Recipe was succesfully deleted');
})
});
router.get("/showrecipes/:recipeId", (req, res) => {
let nameQuery = {_id: req.params.recipeId};
Recipe.findOne(nameQuery, (err, recipes) => {
if (err) throw err;
res.json(recipes);
})
.populate('comments')
.exec((err) => {
if (err) throw err;
})
});
router.post("/showrecipes:/:recipeId/addcomment", (req, res, next) => {
Comment.create({
rating: req.body.rating,
comment: req.body.comment,
postedBy: postedBy,
date: Date.now(),
recipeItem: recipeId
})
});
router.get('/showrecipes/byuser/:username', (req, res) => {
let query = {postedBy: req.params.username};
Recipe.find(query, (err, recipes) => {
if (err) throw err;
res.json(recipes)
})
});
module.exports = router;
Now, at some point I was able to create recipes and store them in my database without a problem. But now this weird thing happens.
Here, I make my post request as you can see in the screenshot below:
But for some strange reason, everytime I make a get request, none of the key/value pairs I specified in my json body request are there. Each recipe object now only as the _id in it.
Can anyone help me? This just seems so weird.
Looks like you're simply missing the application/json Content-Type header.
In Postman, just set the header, as shown in this gif.