In my Express app, I have to insert a new table record to MySQL database. I'm using Sequelize as the ORM. However, with the following setup , I couldn't do that. When I send a POST request to /users/register method(UserController.signup() is set to be executed at this route), it is waiting forever for a response in POSTman. I tried returning a string from UserController.signup() and then the request was working fine.
Why can't I insert a record into database? Every help is highly appreciated.
UserController.js
var User = require('../models/user')
module.exports = {
signUp: (fname,email,password) => {
User.create({ firstName: fname, email: email, password: password }).then(user => {
console.log("New auto-generated ID:", user.id);
});
}
}
models/user.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
User.associate = function(models) {
// associations can be defined here
};
return User;
};
app.js
var createError = require('http-errors');
var express = require('express');
var db = require('./database')
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var Router = require('./routes/index');
var app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(Router);
db.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
app.use(function(req, res, next) {
next(createError(404));
});
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
});
module.exports = app;
database.js
const Sequelize = require('sequelize');
const sequelize = new Sequelize('test', 'root', '', {
host: 'localhost',
dialect: 'mariadb'
});
module.exports = sequelize
EDIT
I changed models/user.js as follows.
'use strict';
var sequelize = require('../database')
var Sequelize = require('sequelize')
const User = sequelize.define('User', {
firstName: Sequelize.STRING,
email: Sequelize.STRING,
password: Sequelize.STRING
}, {});
User.associate = function(models) {
// associations can be defined here
};
module.exports = User;
Add an id attribute to your User model. You're trying to access the value from the User model but it's not declared. It will still be auto-generated for you. Try something like this:
Model user.js
const User = sequelize.define('User', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
firstName: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
Migration migration:generate --name createUser
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
Related
Can someone help me with this problem, mongoose not fetching any data from MongoDB atlas? Everything is fine and I am able to fetch data from cmd, but with mongoose, it returns an empty array.
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const app = express()
app.use(express.json())
app.use(express.urlencoded())
app.use(cors())
const dotenv = require('dotenv');
dotenv.config()
mongoose.connect(process.env.DB_ACCESS, () => {
console.log("db connected successfully..")
})
const hospitalTemplate = new mongoose.Schema({
state: {
type: String,
required: true
},
city: {
type: String,
required: true
},
hospitalname: {
type: String,
required: true
},
mobilenumber: {
type: Number,
required: true
},
image1url: {
type: String,
required: true
},
rating: {
type: Number,
required: true
},
availablebeds: {
type: Number,
required: true
}
})
const Hospital = new mongoose.model('Hospital', hospitalTemplate)
// // Routes
app.get("/search", async (req, res)=> {
await Hospital.find({}, (err, data)=> {
if (data) {
res.send(data);
console.log(data)
} else {
res.send({ message: "data problem" });
console.log("data problem");
}
}).clone().catch(function(err){ console.log(err)})
})
app.listen(4000,() => {
console.log("server is running")
})
After running this program, I get an empty array when I visit "localhost:4000/search"
I am trying to create login page. SO for this, I am making a call. After I get the response, I am storing cookie and with the cookie I am getting the user info. What I am trying to do is store this user info in Vuex store. Here is whole of my logic for this:
export default {
name: 'Login',
mounted() {
EventBus.$on(GENERAL_APP_CONSTANTS.Events.CheckAuthentication, () => {
this.authenticated = authHelper.validAuthentication();
if (this.authenticated) {
this.email = this.password = "";
this.authenticationFailed = false;
}
});
EventBus.$on(GENERAL_APP_CONSTANTS.Events.LoginFailed, () => {
this.authenticationFailed = true
});
},
data () {
return {
authenticated: false,
authenticationFailed: false,
email: '',
password: '',
rememberMe: false,
cookieName: "_token",
cookie: "",
cookieValue: "",
}
},
validations: {
email: {
required,
email,
},
password: {
required,
minLength: minLength(0)
},
},
methods: {
signIn: function () {
authHelper.signIn(this.email, this.password, () => {
this.$router.push({name: 'home'});
});
this.cookie = cookieHelper.getCookie(this.cookieName);
this.cookieValue = cookieHelper.getCookieValue(this.cookie);
this.storeUser();
},
storeUser: function () {
apiHelper.getRequest(
`/users/${cookieHelper.parseJwt(this.cookie).user_id}`,
(response) => {
this.$store.dispatch('storeUser', {
user: response.data,
})
}
)
},
}
}
So, as you see below, my user in Vuex store is still empty:
my mutation is:
export const STORE_USER = (state, {user}) => {
state.user = user;
}
my action is:
export const storeUser = ({commit}, {user}) => {
commit('STORE_USER', {user});
}
Could you have a look and let me know, where do you think I am making something wrong.
I'm trying to get my login component on the server side to work, but it keeps giving me this one error over and over. "TypeError: User.comparePassword is not a function" I had it working in postman before, but I can't figure out why it broke now. I'm making an ecommerce site, so I'm excluding code that has stuff to do with the rest of the site.
[user.js - my routing and where the error is being presented.]
const { User, validateUser } = require("../models/user.js");
const bcrypt = require("bcrypt");
const config = require("config");
const jwt = require("jsonwebtoken");
const auth = require("../middleware/auth");
const express = require("express");
const router = express.Router();
//get users
router.get("/", async (req, res) => {
try {
const users = await User.find();
return res.send(users);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//get a user
router.get("/:userId", async (req, res) => {
try {
const user = await User.findById(req.params.userId);
return res.send(user);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//new user
router.post("/register", async (req, res) => {
try {
const { error } = validateUser(req.body);
if (error) return res.status(500).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
const salt = await bcrypt.genSalt(10);
user = new User({
name: req.body.name,
email: req.body.email,
password: await bcrypt.hash(req.body.password, salt),
});
await user.save();
const token = jwt.sign(
{ _id: user._id, name: user.name },
config.get("jwtSecret")
);
return res
.header("x-auth-token", token)
.header("access-control-expose-headers", "x-auth-token")
.send({ _id: user._id, name: user.name, email: user.email });
} catch (ex) {
return res.status(500).send(`InternalServerError:${ex}`);
}
});
router.get("/auth", auth, (req, res) => {
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
role: req.user.role,
image: req.user.image,
cart: req.user.cart,
history: req.user.history,
});
});
router.post("/login", (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if (!User)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found",
});
});
User.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "Wrong password" });
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
res.cookie("w_authExp", User.tokenExp);
res.cookie("w_auth", User.token).status(200).json({
loginSuccess: true,
userId: user._id,
});
});
});
router.get("/logout", auth, (req, res) => {
User.findOneAndUpdate(
{ _id: req.user._id },
{ token: "", tokenExp: "" },
(err, doc) => {
if (err) return res.json({ success: false, err });
return res.status(200).send({
success: true,
});
}
);
});
});
[User.js - Userschema]
const mongoose = require('mongoose');
const Joi = require('joi');
const cors = require('cors');
const config = require('config');
const jwt = require('jsonwebtoken');
const { productSchema } = require('./Product');
const { reviewSchema } = require('./review');
const userSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 5, maxlength: 50},
email: {type: String, unique: true, required: true, minlength: 5, maxlength: 255},
password: {type: String, required: true, maxlength: 1024, minlength: 5},
timestamp: { type: Date, default: Date.now() },
cart: {type: [productSchema], default: []},
newSalePost: {type: [productSchema], default: []},
review: {type: [reviewSchema], default: []},
image: {type: String, required: true}
});
const User = mongoose.model('User', userSchema);
userSchema.methods.generateAuthToken = function () {
return jwt.sign({_id: this._id, name: this.name, isAdmin: this.isAdmin}, config.get('jwtSecret'));
};
function validateUser(user){
const schema = Joi.object({
name: Joi.string().min(5).max(50).required(),
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(5).max(1024).required(),
});
return schema.validate(user);
}
userSchema.statics.findByToken = function (token, cb) {
var user = this;
jwt.verify(token, 'secret', function (err, decode) {
user.findOne({ "_id": decode, "token": token }, function (err, user) {
if (err) return cb(err);
cb(null, user);
})
})
}
exports.User = User;
exports.validateUser = validateUser;
exports.userSchema = userSchema;
I tried to record sessions in the database through sequelize, to make it serverless, but req.user is always undefined, I tried every manual which I found on the internet, I do not understand why it not working.
I tried passport.js manual, express manuals, github gists.
Records in the database are successfully created, on successful authentication, but when I try hit /graphql endpoint, it does not fill req.user with user.
req.user should be restored based on session hash which is stored in database.
#!/usr/bin/env node
import express from 'express';
import session from 'express-session';
import { ApolloServer } from 'apollo-server-express';
import { typeDefs, resolvers } from './graphql';
import orm from './orm';
import compose from './dataloader/status.dataloader';
import passport from 'passport';
import { Strategy as GitHubStrategy } from 'passport-github';
import cors from 'cors';
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const user = req.user;
console.log({ user });
return {
user,
orm,
dataloader: compose(orm),
};
},
});
passport.use(
new GitHubStrategy(
{
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: `/auth/github/callback`,
},
async (accessToken, refreshToken, profile, done) => {
const { provider, id: externalId, profileUrl, username, displayName, photos } = profile;
const photo = photos && photos[0] && photos[0].value;
const user = await orm.User.findOne({
include: [
{
attributes: [],
model: orm.UserProvider,
where: {
provider,
externalId,
},
},
],
raw: true,
}).then(async (v) => {
if (null !== v) {
return v;
}
v = await orm.User.create({
displayName,
photo,
});
await orm.UserProvider.create({
provider,
internalId: v.id,
externalId,
username,
profileUrl,
});
return v;
})
const session = await orm.UserSession.create({
internalId: user.id,
hash: accessToken,
});
return done(null, session);
}
)
);
passport.serializeUser(({ hash }, done) => {
console.log({ hash });
done(null, hash);
});
passport.deserializeUser(async (hash, done) => {
console.log({ hash });
const user = await orm.User.findOne({
include: [
{
attributes: [],
model: orm.UserSession,
where: {
hash,
},
},
],
raw: true,
});
done(null, user);
});
app.use(
cors({
origin: "*",
methods: "GET,POST",
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: true,
})
);
app.use(session({ secret: 'test' }));
app.use(passport.initialize());
app.use(passport.session());
app.get(
'/auth/github',
passport.authenticate('github', { session: true })
);
app.get(
'/auth/github/callback',
passport.authenticate('github', { session: true }),
(req, res) => res.redirect('/')
);
app.use('/graphql', passport.authenticate('session', { session: true }));
// (req, res, next) => {
// debugger;
// // passport.
// console.log({
// req,
// session: JSON.stringify(req.session, ',', 4),
// cookie: JSON.stringify(req.cookie),
// user: req.user,
// });
// return next();
// });
server.applyMiddleware({ app, path: '/graphql' });
app
.listen(process.env.PORT, () => {
console.log(`GraphQL ready on: http://localhost:${process.env.PORT}/graphql`);
});
Is there a way to check all the required fields
without the need of a test each field.
validation rules
const Confirmation = Joi.any().valid(Joi.ref('password')).required().options({ language: { any: { allowOnly: 'must match password' } } });
const Email = Joi.string().email();
const Firstname = Joi.string().regex(/^[a-zA-Z\']+$/).min(2).max(30);
const Lastname = Joi.string().regex(/^[a-zA-Z\']+$/).min(2).max(30);
const Password = Joi.string().min(3).max(30);
const Username = Joi.string().regex(/^[a-zA-Z\-\.]+$/).min(3).max(30);
exports.create = {
payload: {
email: Email.required(),
firstname: Firstname.required(),
lastname: Lastname.required(),
password: Password.required(),
password_confirmation: Confirmation,
username: Username.required()
}
};
test
'use strict';
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const Code = require('code');
const Server = require('../../index');
lab.experiment('User', function() {
lab.test('create firstname should be required', function (done) {
const options = {
method: 'POST',
url: '/api/users',
payload: {
email: 'me#mydomain.com',
password: 'mysecret'
}
};
Server.inject(options, function(response) {
const result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
//AND SO ON
lab.test('create firstname should be required', function (done) {
const options = {
method: 'POST',
url: '/api/users',
payload: {
email: 'me#mydomain.com',
password: 'mysecret',
firstname: 'me'
}
};
Server.inject(options, function(response) {
const result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "lastname" fails because ["lastname" is required]');
done();
});
});
});
The answer from #simon-p-r would be a possible solution. But I do not understand why you want to validate the Joi Schemas by checking each required field with a test in the first place. As far as I can tell Joi has a test-coverage of 100% and can be considered thoroughly tested - so why do that again?
I would just test the success and failure case as well as some edge cases (like confirmation of password missing, wrong, etc.)...
'use strict';
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const Code = require('code');
const Server = require('../../index');
lab.experiment('User', function() {
//Failure case
lab.test('create should fail if required fields are missing', function(done) {
const options = {
method: 'POST',
url: '/api/users',
payload: {
email: 'me#mydomain.com',
firstname: 'foo',
lastname: 'bar'
}
};
Server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(400);
done();
});
});
//Success case
lab.test('create should succeed if all fields are valid', function(done) {
const options = {
method: 'POST',
url: '/api/users',
payload: {
email: 'me#mydomain.com',
firstname: 'foo',
lastname: 'bar',
password: 'secret',
password_confirmation: 'secret',
username: 'foo.bar'
}
};
Server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(200);
//maybe do some more checks on the response here...
done();
});
});
//Edge cases
lab.test('create should succeed if all fields are valid', function(done) {
const options = {
method: 'POST',
url: '/api/users',
payload: {
email: 'me#mydomain.com',
firstname: 'foo',
lastname: 'bar',
password: 'secret',
password_confirmation: 'something_else',
username: 'foo.bar'
}
};
Server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(400);
//maybe do some more checks on the response here...
done();
});
});
//And so on...
});
I hope this helps.
If you want to test all the field's validation you may want to set abortEarly to false in the options of validate method. If you are using built-in validation via route config set options object like so
{
method: 'POST',
path: '/api/users',
config: {
handler: handlerFunc,
validate: {
payload: Joi.schema(),
options: {
abortEarly: false
}
}
}
},
This should catch all errors.