Fellow Devs. I know this is a common issue and trust me I've searched and tried to find a solution but to no avail. At least yet.
I'm currently building a rest API that retrieves movie data, basically Netflix and through this API you can register an account. Now, this is where the problem arises for me.
The Problem: When I test my API on Postman and try to add/POST a user I get an error message: " Error: data and salt arguments required" followed by more error messages which I post an image down below
Expected Output: I want to be able to add a user and successfully test it on Postman
Question: What exactly is the problem and how can I go about fixing this?
I've looked at the reference errors and everything seems to look ok but clearly not.
Any help would be appreciated.
Index.js
const express = require('express'),
bodyParser = require('body-parser'),
uuid = require('uuid');
const morgan = require('morgan');
const app = express();
const mongoose = require('mongoose');
const Models = require('./models/models');
const passport = require('passport');
const cors = require('cors');
const {
check,
validationResult
} = require('express-validator');
app.use(cors());
// SCHEMAS
const Movies = Models.Movie;
const Users = Models.User;
const Genres = Models.Genre;
const Directors = Models.Director;
// const URI = 'mongodb://localhost:27017/movie-api'; // Database Option 1: Local DB
mongoose.connect(process.env.CONNECTION_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
app.use(bodyParser.json());
//LOGS REQUESTS
app.use(morgan('common'))
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({
extended: true
}));
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
//AUTHENTICATION
const auth = require('./auth')(app);
require('./passport');
//ROUTING
//HOME
app.get("/", (req, res) => {
res.send('Hello there! Welcome to myMovies');
})
// USERS DATA
app.get('/users', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Users.find()
.then((allUsers) => {
res.status(201).json(allUsers);
})
.catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
});
// Create User
app.post('/users', [
check('Username', 'Username is required').isLength({
min: 5
}), // sets minimum value of 5 characters
check('Username', 'Username contains non alphanumeric characters - not allowed.').isAlphanumeric(), // specifies tnat a field can only contain letters and numbers
check('Password', ' Password is required').not().isEmpty(),
check('Email', 'Email does not appear to be valid').isEmail(),
],
(req, res) => {
// check the validation object for errors
const validationErrors = validationResult(req);
if (!validationErrors.isEmpty()) {
return res.status(422).json({
errors: validationErrors.array()
});
}
const hashedPassword = Users.hashPassword(req.body.Passsword);
Users.findOne({
Username: req.body.Username
}).then((user) => { // Searches to see if user with the requested username alreadt exists
if (user) {
return res.status(400).send(`${req.body.Username} already exists!`); // if the user is found, send a response that it already exists
} else {
Users.create({
Username: req.body.Username,
Password: hashedPassword,
Email: req.body.Email,
Birthday: req.body.Birthday
})
.then((user) => {
res.status(201).json(user)
}).catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`);
})
}
}).catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`)
});
});
// REMOVE USER
app.delete('/users/:Username', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Users.findOneAndRemove({
Username: req.params.Username
})
.then((user) => {
if (!user) {
res.status(400).send(`${req.params.Username} was not found!`);
} else {
res.status(200).send(`${req.params.Username} was deleted`);
}
}).catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
});
// GET based on username
app.get('/users/:Username', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Users.findOne({
Username: req.params.Username
}).then((user) => {
res.json(user);
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
});
});
// UPDATE: change user's username
app.put('/users/:Username', [
check('Username', 'Username is required').isLength({
min: 5
}),
check('Username', 'Username contains non alphanumeric characters - not allowed.').isAlphanumeric(),
check('Password', 'Password is required').not().isEmpty(),
check('Email', 'Email does not appear to be valid').isEmpty(),
],
passport.authenticate('jwt', {
session: false
}),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({
errors: errors.array()
});
}
Users.findOneAndUpdate({
Username: req.params.Username
}, {
$set: {
Username: req.body.Username,
Password: req.body.Password,
Email: req.body.Email,
Birthday: req.body.Birthday
}
}, {
new: true
}, // This line ensures that the updated document is returned
(err, updateUser) => {
if (err) {
console.error(err);
res.status(500).send(`Error: ${err}`);
} else {
res.json(updateUser); // Return the updated document
}
})
});
// MOVIES
// GET: fetches a list of all movies
app.get('/movies', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Movies.find()
.then((movies) => {
res.status(201).json(movies);
})
.catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`);
});
});
// GET: fetches movies by title
app.get('/movies/:Title', passport.authenticate('jwt', {
sesson: false
}), (req, res) => {
Movies.findOne({
Title: req.params.Title
})
.then((movie) => {
res.json(movie);
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
});
});
// UPDATE: add favorite movies to users
app.post('/users/:Username/movies/:MovieID', [
check('Username', 'Username is required').isLength({
min: 5
}),
check('Username', 'Username contains non alphanumeric characters - not allowed').isAlphanumeric(),
check('MovieID', 'MovieID is required').not().isEmpty(),
check('MovieID', 'MovieID does not appear to be valid').isMongoId(),
],
passport.authenticate({
session: false
}),
(req, res) => {
Users.findOneAndUpdate({
Username: req.params.Username
}, {
$addToSet: {
FavoriteMovies: req.params.MovieID
}
}, {
$push: {
FavoriteMovies: req.params.MovieID
}
}, {
new: true
}, // This line ensures that the updated document is returned
(err, updatedUser) => {
if (err) {
console.error(err);
res.status(500).send(`Error:${err}`)
} else {
res.json(updatedUser)
}
});
});
// DELETE: remove favorite movie from users
app.delete('/users/:Username/movies/:MovieID', passport.authenticate('jwt', {
session: false
}),
(req, res) => {
Users.findOneAndDelete({
FavoriteMovies: req.params.MovieID
}).then((movie) => {
if (!movie) {
res.status(400).send(req.params.MovieID + ' movie was not found!');
} else {
res.status(201).send(req.params.MovieID + ' movie was successfully deleted!');
}
}).catch((err) => {
console.error(err);
res.status(500).send('Error:' + err)
});
});
// DIRECTORS
// GET: returns all directors
app.get('/directors', (req, res) => {
Directors.find().then((director) => {
res.status(201).json(director);
}).catch((err) => {
console.error(err);
res.status(500).send('Error', +err);
})
})
// GET: return director by name
app.get('/directors/:Name', (req, res) => {
Directors.findOne({
Name: req.params.Name
}).then((director) => {
res.json(director);
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err); // 500 is server error
})
})
// GENRES
// GET: returns all genres
app.get('/genres', (req, res) => {
Genres.find().then((genre) => {
res.status(201).json(genre);
}).catch((err) => {
console.error(err);
res.status(400).send('Error: ' + err);
})
})
// GET: returns Genre based on name
app.get('/genres/:Name', (req, res) => {
Genres.findOne({
Name: req.params.Name
}).then((genreName) => {
res.status(201).json(genreName)
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
})
})
app.get('/documentation', (req, res) => {
res.sendFile('public/documentation.html', {
root: __dirname
});
});
// Server & Heroku
const port = process.env.PORT || 8081;
app.listen(port, '0.0.0.0', () => {
console.log(`Listening on Port ${port}`);
});
passport.js
const passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
Models = require('./models/models'),
passportJWT = require('passport-jwt');
let Users = Models.User,
JWTStrategy = passportJWT.Strategy,
ExtractJWT = passportJWT.ExtractJwt;
passport.use(new LocalStrategy({
usernameField: 'Username',
passwordField: 'Password'
}, (username, password, callback) => {
console.log(`${username} ${password}`);
Users.findOne({
Username: username
}, (error, user) => {
if (error) {
console.log(error);
return callback(error);
}
if (!user) {
console.log('incorrect username');
return callback(null, false, {
message: 'Incorrect username or password.'
});
}
console.log('finished');
return callback(null, user);
});
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: 'your_jwt_secret'
}, (jwtPayload, callback) => {
return Users.findById(jwtPayload._id)
.then((user) => {
return callback(null, user);
})
.catch((error) => {
return callback(error)
});
}));
auth.js
const jwtSecret = 'your_jwt_secret'; // This has to be the same key used in the JWTStrategy
const {
check,
validationResult
} = require('express-validator');
const jwt = require('jsonwebtoken'),
passport = require('passport');
require('./passport'); // Your local passport file
let generateJWTToken = (user) => {
return jwt.sign(user, jwtSecret, {
subject: user.Username, // This is the username you’re encoding in the JWT
expiresIn: '7d', // This specifies that the token will expire in 7 days
algorithm: 'HS256' // This is the algorithm used to “sign” or encode the values of the JWT
});
}
/* POST login. */
module.exports = (router) => {
router.post('/login', [
check('Username', 'Username is required').isLength({
min: 5
}),
check('Username', 'Username contains non alphanumeric characters - allowed').isAlphanumeric(),
check('Password', 'Password is required').not().isEmpty(),
],
(req, res) => {
const validatonErrors = validationResult(req);
if (!validatonErrors.isEmpty()) {
return res.status(422).json({
errors: validatonErrors.array()
})
}
passport.authenticate('local', {
session: false
}, (error, user, info) => {
if (error || !user) {
return res.status(400).json({
message: 'Something is not right',
user: user
});
}
req.login(user, {
session: false
}, (error) => {
if (error) {
res.send(error);
}
let token = generateJWTToken(user.toJSON());
return res.json({
user,
token
});
});
})(req, res);
});
}
model.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
let movieSchema = mongoose.Schema({
Title: {
type: String,
required: true
},
Description: {
type: String,
required: true
},
Genre: {
Name: String,
Description: String
},
Director: {
Name: String,
Bio: String,
Birth: String,
Death: String
},
Actors: [String],
ImageURL: String,
Featured: Boolean
});
let userSchema = mongoose.Schema({
Username: {
type: String,
required: true
},
Password: {
type: String,
required: true
},
Email: {
type: String,
required: true
},
Birthday: Date,
FavoriteMovies: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Movie'
}]
});
userSchema.statics.hashPassword = (password) => { // HASHES PASSWORD
return bcrypt.hashSync(password, 10);
};
userSchema.methods.validatePassword = function validatePassword(password) {
return bcrypt.compareSync(password, this.Password);
};
let directorSchema = mongoose.Schema({
Name: {
type: String,
required: true
},
Bio: {
type: String,
required: true
},
Birth: {
type: String,
required: true
}
})
let genreSchema = mongoose.Schema({
Name: {
type: String,
required: true
},
Description: {
type: String,
required: true
}
})
let Movie = mongoose.model('Movie', movieSchema);
let User = mongoose.model('User', userSchema);
let Director = mongoose.model('Director', directorSchema);
let Genre = mongoose.model('Genre', genreSchema);
module.exports.Movie = Movie;
module.exports.User = User;
module.exports.Director = Director;
module.exports.Genre = Genre;
Package.json
Debug Console

