I am new to Nest js and I wonder what the best place to return your exceptions and success responses is it from the controller or from a service. This is what I have implemented and I want to know what will be the best practice to handle and send API responses to the client. Shall I return every success and error responses from the controller or from the service?
Controller:
#Post('sign-up')
#HttpCode(200)
async signUp(#Body() request: ISignUpDto) {
const user = await this._authService.createUser(request);
console.log(user);
const otpLength = user.otp.toString().length;
return {
message: `We've e-mailed you a ${otpLength} digit OTP. Please check your e-mail and complete the verification.`,
dataset: null,
};
}
Service:
public async createUser(request: ISignUpDto): Promise<User> {
const isUserExist = await this.isUserExist(request.email);
if (isUserExist) {
throw new BadRequestException({
dataset: null,
message: 'The provided e-mail is already in use by an existing user.',
});
} else {
const hashedPassword = await hashPassword(request.password);
const user = await this._prisma.user.create({
data: {
active: false,
otp: generateOTP(6),
email: request.email,
password: hashedPassword,
firstName: request.firstName,
lastName: request.lastName,
gender: request.gender ? request.gender : null,
dateOfBirth: request.dateOfBirth
? request.dateOfBirth
: null,
},
select: {
id: true,
email: true,
firstName: true,
lastName: true,
gender: true,
dateOfBirth: true,
otp: true,
active: true,
createdAt: true,
},
});
await this.sendSignUpVerificationEmail(user.email);
return user as User;
}
}
Related
In my case, if I unselect any field in postman, I got an error in the terminal but I want to print that error message or custom error in the postman response. how to do that?
POST method
router.post("/admin/add_profile", upload.single("image"), async (req, res) => {
try {
const send = new SomeModel({
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
image: req.file.filename,
});
send.save();
const result = await s3Uploadv2(req.files);
res.json({ status: "Everything worked as expected", result });
} catch (err) {
res.status(404).send(err.message);
}
});
schema.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const SomeModelSchema = new Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: ["last name is required"],
},
phone: {
type: Number,
required: ["Phone number is required"],
unique: true,
validate: {
validator: (val) => {
return val.toString().length >= 10 && val.toString().length <= 12;
},
},
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: ["email address is required"],
validate: (email) => {
return /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
},
match: [
/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/,
"Please fill a valid email address",
],
},
image: {
data: Buffer,
contentType: String
}
});
module.exports = mongoose.model("SomeModel", SomeModelSchema);
here I unselected the first_name field but I got an error in the terminal I want to print that error or a custom error in the postman response.
error message in terminal
You should await the save() call, which returns a Promise.
You should then be able to handle the error in the catch block:
router.post('/admin/add_profile', upload.single('image'), async (req, res) => {
try {
const send = new SomeModel({
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
image: req.file.filename,
});
await send.save();
const result = await s3Uploadv2(req.files);
res.json({ status: 'Everything worked as expected', result });
} catch (error) {
if (error.name === "ValidationError") {
let errors = [];
for (const err in error.errors) {
errors.push(err.message)
}
return res.status(400).send(errors);
}
res.status(500).send('Server error');
}
});
I would like to increment points by 1000 when visits of user become 1. I am incrementing visits by 1 whenever loginUser() function gets called.
this is my async loginUser function
async function loginUser(event) {
event.preventDefault()
const response = await fetch('http://localhost:1337/api/login', {
method: 'POST',
headers:{
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
}),
})
server.js
app.post('/api/login', async (req, res) => {
await User.findOneAndUpdate(
{email: req.body.email},
{$inc :{visits : 1}},
)
const isPasswordValid = await bcrypt.compare(
req.body.password,
user.password
)
if (isPasswordValid) {
const token = jwt.sign(
{
name: user.name,
email: user.email,
},
'secret123'
)
return res.json({ status: 'ok', user: token })
} else {
return res.json({ status: 'error', user: false })
}
})
user.model.js
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/poker')
const User = new mongoose.Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true},
password: { type: String, required: true },
points: { type: Number, required: true, default: 0},
visits: { type: Number, required: true, default: 0}
},
{ collectiopn: 'user-data' }
)
const model = mongoose.model('UserData', User)
module.exports = model
I've tried to put
if (req.body.visits == 1){
await User.findOneAndUpdate(
{email: req.body.email},
{$inc :{points: 1000}},
)
}
and it returns undefined for req.body.visits
Can anyone help?
Thank you
I have a customer table which consist few columns along with password column in sequelize mysql
When I edit/update the customer if the user doesn't provide the password in the request it should not update the password column at all (should remain untouched).
How can I accomplish such program inside the customer schema page?
I am using following method to update the table:
db.customers.findOne({
where: {
id: req.body.id
}
}).then(data => {
data.update({
cash_credit: req.body.cash_credit,
name: req.body.name,
address: req.body.address,
state_id: req.body.state_id,
gstin: req.body.gstin,
mobile: req.body.mobile,
phone: req.body.phone,
email: req.body.email,
form_type: req.body.form_type,
pincode: req.body.pincode,
password: req.body.password, // omit
city_id: req.body.city_id,
country: req.body.country || 0,
id: req.body.id
}).then(data2 => {
console.log(data2);
});
});
Here is my customer schema:
const bcrypt = require("bcrypt");
module.exports = function (sequelize, DataTypes) {
const customers = sequelize.define("customers", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
field: "SupplierCode"
},
customer_type: {
type: DataTypes.INTEGER,
},
cash_credit: {
type: DataTypes.STRING,
},
...
}, {
hooks: {
// eslint-disable-next-line no-unused-vars
beforeValidate: function (value, option) {
value.zip = parseInt(value.dataValues.zip);
},
beforeCreate: async (schema) => {
let hashedPassword = await bcrypt.hash(schema.password, saltRounds);
schema.password = hashedPassword;
console.log(schema.password);
},
beforeUpdate: async (schema) => {
if (schema.password) {
let hashedPassword = await bcrypt.hash(schema.password, saltRounds);
schema.password = hashedPassword;
}
}
},
timestamps: false,
defaultScope: {
attributes: {
exclude: ["password"]
}
},
scopes: {
withPassword: {
attributes: {
include: ["password"]
}
}
}
});
}
As far as I remember Sequelize won't update a field that you have not provided. So if the password is not defined, just don't pass it to the update call.
const updateData = {
cash_credit: req.body.cash_credit,
name: req.body.name,
address: req.body.address,
state_id: req.body.state_id,
gstin: req.body.gstin,
mobile: req.body.mobile,
phone: req.body.phone,
email: req.body.email,
form_type: req.body.form_type,
pincode: req.body.pincode,
city_id: req.body.city_id,
country: req.body.country || 0,
id: req.body.id
}
if (req.body.password) {
updateData.password = req.body.password;
}
data.update(updateData).then(console.log);
Im under the assumption that adding unique: true to a field would stop from saving to the database using the same value. But im still allowed to do it.
"mongoose": "^5.4.19",
const SessionSchema = new Schema({
jobId: {
type: String,
required: false,
unique: true,
index: true,
},
productId: {
type: String,
required: true,
},
status: {
type: String,
default: "Pending",
},
mode: {
type: String,
default: "authentication",
},
result: {
type: Schema.Types.Mixed,
},
requests: [RequestSchema],
callback_at: {
type: Date,
},
}, {
timestamps: { createdAt: "created_at", updatedAt: "updated_at" },
});
I have already tried deleting and recreating the collection. See the image below i can create new session with the same jobId being 1.
public store = async (req: Request, res: Response): Promise<any> => {
const input = req.body;
let session = new Session({
productId: input.productId,
jobId: input.jobId,
});
try {
session = await session.save();
const response = {
success: true,
status: 201,
data: { session },
message: "SESSION CREATED",
};
return res.status(response.status).json(response);
} catch (err) {
const response = {
success: false,
status: 500,
errors: [],
message: "UNEXPECTED SESSION ERROR",
};
if (err.code === 11000) {
response.errors.push({
code: 11000,
message: "Duplicate key error jobId",
});
}
return res.status(response.status).json(response);
}
db.sessions.getIndex();
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "myDB.sessions"
}
]
You have to understand that unique is an index configuration option in your schema.
For instance, if the users collection doesn't have a unique index on userName, then you need to wait for the index to build before you start relying on it.
const user = new mongoose.Schema({
userName: { type: 'String', unique: true },
});
const User = db.model('User', user);
const doc = new User({
userName: 'Bob'
});
return User.init() // `User.init()` returns a promise that is fulfilled when all indexes are done
.then(() => User.create(doc))
.then(() => User.create({ userName: 'Bob' }));
}
I was not using unique properly: https://mongoosejs.com/docs/validation.html#the-unique-option-is-not-a-validator
Need to wait until the indexes are built before relying on unique to be a validator.
I changed my mongoose connect options to look like the following
options: {
useNewUrlParser: true,
useCreateIndex: true,
autoIndex: true,
},
I;m not sure if its the most appropriate solution, but its the one ive gone with for now.
For some reason whenever I make a request to the routes to create a new user I get "This email account is already in use" although the user is created, and when I try to log in I get "The login information was incorrect". I console logged the req.body when logging in and I get the object logged and it seems okay.
My AuthenticationController.js
const {User} = require('../models')
const jwt = require('jsonwebtoken')
const config = require('../config/config')
function jwtSignUser (user) {
const ONE_WEEK = 60 * 60 * 24 * 7
return jwt.sign(user, config.authentication.jwtSecret, {
expiresIn: ONE_WEEK
})
}
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
res.status(400).send('This email account is already in use.')
}
},
async login (req, res) {
console.log(req.body)
try {
const {email, password} = req.body
const user = await User.findOne({
where: {
email: email
}
})
if (!user) {
return res.status(403).send({
error: 'The login information was incorrect'
})
}
const isPasswordValid = await user.comparePassword(password)
if (!isPasswordValid) {
return res.status(403).send({
error: 'The login information was incorrect'
})
}
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
res.status(500).send({
error: 'An error has occured trying to log in'
})
}
}
}
console.log(user) gives me the following:
User {
dataValues:
{ id: 41,
username: 'testing',
email: 'testing#gmail.com',
password:
'$2a$08$J6/psKuCltiCpCll2rjz4OaCylud0zq/wKnjKK8Udmm.bFU3c2Tt6',
firstName: 'test',
lastName: 'ing',
createdAt: 2019-02-26T15:47:09.133Z,
updatedAt: 2019-02-26T15:47:09.133Z },
_previousDataValues:
{ id: 41,
username: 'testing',
email: 'testing#gmail.com',
password:
'$2a$08$J6/psKuCltiCpCll2rjz4OaCylud0zq/wKnjKK8Udmm.bFU3c2Tt6',
firstName: 'test',
lastName: 'ing',
createdAt: 2019-02-26T15:47:09.133Z,
updatedAt: 2019-02-26T15:47:09.133Z },
_changed: {},
_modelOptions:
{ timestamps: true,
validate: {},
freezeTableName: false,
underscored: false,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: { email: 'testing#gmail.com' },
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
indexes: [],
name: { plural: 'Users', singular: 'User' },
omitNull: false,
hooks:
{ beforeCreate: [Array],
beforeUpdate: [Array],
beforeSave: [Array] },
sequelize:
Sequelize {
options: [Object],
config: [Object],
dialect: [SqliteDialect],
queryInterface: [QueryInterface],
models: [Object],
modelManager: [ModelManager],
connectionManager: [ConnectionManager],
importCache: [Object],
test: [Object] },
uniqueKeys: { Users_email_unique: [Object] } },
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes:
[ 'id',
'username',
'email',
'password',
'firstName',
'lastName',
'createdAt',
'updatedAt' ] },
__eagerlyLoadedAssociations: [],
isNewRecord: false }
console.log(req.body) gives me this:
{ email: 'testing#gmail.com',
password: 'testing99',
username: 'testing',
firstName: 'test',
lastName: 'ing' }
They have the same information but it gives me an error anyway.
I assume you are using mongoose, according to the documentation https://mongoosejs.com/docs/api.html#model_Model.findOne findOne is returning Query object, so validation may fail.
Check with debugger or even simple console.log to see what's inside of the user variable in login function.
So if user does not exist, you are getting Query object which is not empty, because mongoose filled this object with some functions.
You can try to use lean(), checkout this article http://www.tothenew.com/blog/high-performance-find-query-using-lean-in-mongoose-2/
We have 2 solution here:
Using raw: true
const user = await User.findOne({
where: {
email: email
},
raw: true
})
Using plain: true
user = user.get({
plain: true
});