I'm developing a register function with Typescript, but for some reason the method find couldn't search into database.
This is the model(users.ts):
interface UserAttributes {
id: number;
first_name: string;
last_name: string;
password: string;
email:string;
token: string;
description?: string;
createdAt?: Date;
updatedAt?: Date;
deletedAt?: Date;
}
'use strict';
const {
Model, Optional
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model<UserAttributes> {
public id!: number;
public first_name!: string;
public last_name!:string;
public email!: string;
public password!: string;
public token!: string;
// timestamps!
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
public readonly deletedAt!: Date;
static associate(models) {
// define association here
}
}
Users.init({
id: {
type:DataTypes.INTEGER,
primaryKey:true
},
password: DataTypes.STRING,
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
token: DataTypes.STRING,
}, {
sequelize,
modelName: 'Users',
tableName:'users',
});
return Users;
};
This is the register services(Services.ts):
const register: typeof UserAttributes = (first_name, last_name, email, password) => {
return Users.findByPk(email).then(userExist => {
if(userExist) {
console.log('User already register. Please Login')
}
}).then(bcrypt => {
return bcrypt.hash(password, 10);
}).then(hash => {
Users.create({first_name,last_name, email, password: hash})
}).then(user => {
return user;
}).catch((error) => {
throw error;
})
}
And return me the following error message at the moment to make http request with postman:
return Users.findOne({ where: { email: email } }).then(function (userExist)
^
TypeError: Cannot read properties of undefined (reading 'findOne')
I dont think it is due a sequelize or progress setup's errors. Because I can make sucesfully's migrations into database. The problem is when I test the controller
This is the controller(controller.ts):
const userServices = require('./Services');
async function registerController (req:Request, res:Response): Promise<Response>{
await userServices.register(req.body).then(() => {
res.status(200).send('User created succesfully');
}).catch((error) => {
return res.status(500).send(error);
})
}
So, If anyone could bring me some help I'd really be gratefull
i have the CastError, while im trying to update an user, i'm using the following stack:
Database: MongoDB, mongoose, mongoose-delete
Backend: NodeJs, ExpressJs
Frontend: Angular
model class for user:
const mongoose = require("mongoose");
const mongooseDelete = require("mongoose-delete");
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
select: false,
required: true,
unique: true,
},
lastname: {
type: String,
required: true,
},
enabled: {
type: Boolean,
default: true,
},
role: {
type: ["user", "admin", "superadmin"],
default: "user",
},
cellphone: {
type: String,
},
},
{ timestamps: true, versionKey: false }
);
UserSchema.plugin(mongooseDelete, { overrideMethods: "all" });
module.exports = mongoose.model("users", UserSchema);
this way looks the register in the BD in mongo, as the class configuration works
{"_id":{"$oid":"629d23627d4b57d52273634c"},"name":"pruebaadmin","email":"admin#prueba.co","password":"$2a$10$luHE1wG9F2QFpEcbbmyO7eyEU.1pvBMFeqKOvQA3v1hL7rAGbE82a","lastname":"registro","enabled":true,"role":["admin"],"deleted":false,"createdAt":{"$date":{"$numberLong":"1654465378428"}},"updatedAt":{"$date":{"$numberLong":"1654465378428"}}}
in the routes i got middleware for validate the authsession and validate the form, this two pass without error, then the problem is presented in the method updateItem in controller.
controller for user:
const { matchedData } = require("express-validator");
const { usersModel } = require("../models");
const { handleHttpError } = require("../utils/handleError.js");
/**
* update user by id
* #param {*} req
* #param {*} res
*/
const updateItem = async (req, res) => {
try {
const { id, ...body } = matchedData(req);
console.log(body);
const data = await usersModel.findOneAndUpdate(id, body);
res.send({ data });
} catch (e) {
console.log(e);
handleHttpError(res, "Register can not be updated");
}
};
module.exports = { getItems, getItem, createItem, updateItem, deleteItem };
i console log the body, got this from the request:
{
name: 'pruebaadmin',
email: 'admin#prueba.co',
lastname: 'registro',
celphone: undefined,
enabled: true,
role: [ 'admin' ]
}
and the full error in the backend:
CastError: Cast to Boolean failed for value "{ '$ne': true }" (type Object) at path "deleted" because of "CastError"
at model.Query.exec (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\query.js:4716:21)
at model.Query.Query.then (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\query.js:4815:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
messageFormat: undefined,
stringValue: `"{ '$ne': true }"`,
kind: 'Boolean',
value: { '$ne': true },
path: 'deleted',
reason: CastError: Cast to boolean failed for value "{ '$ne': true }" (type Object) at path "undefined"
at castBoolean (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\cast\boolean.js:28:9)
at SchemaBoolean.cast (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\schema\boolean.js:207:12)
at SchemaBoolean.SchemaType.applySetters (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\schematype.js:1189:12)
at SchemaBoolean.SchemaType._castForQuery (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\schematype.js:1623:15)
at SchemaBoolean.castForQuery (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\schema\boolean.js:236:15)
at SchemaBoolean.SchemaType.castForQueryWrapper (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\schematype.js:1590:20)
at castUpdateVal (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\helpers\query\castUpdate.js:544:19)
at walkUpdatePath (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\helpers\query\castUpdate.js:274:22)
at castUpdate (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\helpers\query\castUpdate.js:96:7)
at model.Query._castUpdate (C:\Users\alejandroromero\Projects\rancherbackend\node_modules\mongoose\lib\query.js:4925:10) {
stringValue: `"{ '$ne': true }"`,
messageFormat: undefined,
kind: 'boolean',
value: { '$ne': true },
path: undefined,
reason: undefined,
valueType: 'Object'
},
valueType: 'Object'
}
so, the put petition i made is like this:
**1. i use a profile component for view and edit user data, in the method updateRegister i call a service for user, the services works for get and post petitions but right now don't work for put petition.
**
import { Component, OnInit } from "#angular/core";
import {
FormGroup,
FormBuilder,
Validators,
FormControl,
} from "#angular/forms";
import { UserService } from "../user/services/user.service";
import { Router, ActivatedRoute } from "#angular/router";
import Swal from "sweetalert2";
import { UserModel } from "#models/user";
import { RegisterForm } from "#interfaces/register-form";
#Component({
selector: "app-profile",
templateUrl: "./profile.component.html",
styleUrls: ["./profile.component.scss"],
})
export class ProfileComponent implements OnInit {
title: string = "My profile";
formSubmitted: boolean = false;
public id: string = "";
public user: UserModel = {} as UserModel;
public registerForm: FormGroup = new FormGroup({});
constructor(
private fb: FormBuilder,
private userService: UserService,
private router: Router,
private activatedRoute: ActivatedRoute
) {}
ngOnInit(): void {
this.loadData();
this.registerForm = this.fb.group({
email: new FormControl("", [Validators.required, Validators.email]),
name: new FormControl("", [
Validators.required,
Validators.maxLength(25),
Validators.minLength(3),
Validators.pattern("[a-zA-Z ]*"),
]),
lastname: new FormControl("", [
Validators.required,
Validators.maxLength(25),
Validators.minLength(3),
Validators.pattern("[a-zA-Z ]*"),
]),
cellphone: new FormControl("", [Validators.pattern("[0-9]{10}")]),
});
}
loadData() {
this.id = this.userService.getUserIdFromCookie();
this.userService.getUserById$(this.id).subscribe((resp) => {
this.user = resp;
this.registerForm.patchValue({
email: this.user.email,
name: this.user.name,
lastname: this.user.lastname,
cellphone: this.user.cellphone,
});
});
}
updateRegister(id: string): void {
const { email, name, lastname, cellphone } = this.registerForm.value;
console.log(this.registerForm.value);
const updateId = id;
// console.log("updateId: ", updateId);
// console.log("User: ", this.user);
this.userService
.updateUser$(
updateId,
email,
name,
lastname,
String(cellphone) as string,
this.user.role as ["user", "admin", "superadmin"],
this.user.enabled as boolean
)
.subscribe(
(response) => {
Swal.fire("succes", "Updated register");
this.router.navigate(["/", "component", "profile"]);
// console.log("session started correct: ", response);
},
(err) => {
Swal.fire("Error", err.error.error, "error");
// console.log("session error: ", error);
}
);
}
isValid(controlName: string): boolean {
if (this.registerForm.get(controlName)?.invalid && this.formSubmitted) {
return true;
} else {
return false;
}
}
}
when i console log the values from the form seems ok:
{email: 'admin#prueba.co', name: 'pruebaadmin', lastname: 'registro', cellphone: 3017957267}
the service for user is like this:
import { HttpClient } from "#angular/common/http";
import { Observable, of } from "rxjs";
import { map, mergeMap, tap, catchError } from "rxjs/operators";
import { environment } from "src/environments/environment";
import { CookieService } from "ngx-cookie-service";
import { UserModel } from "#models/user";
import { RegisterForm } from "#interfaces/register-form";
#Injectable({
providedIn: "root",
})
export class UserService {
private readonly URL = environment.api;
endpoint: string = "users";
constructor(private http: HttpClient, private cookieService: CookieService) {}
/**
*
* #returns get all users in order
*/
getAllUsers$() {
return this.http.get(`${this.URL}/${this.endpoint}`).pipe(
map((resp: any) => resp.data),
catchError((err) => {
const { status, statusText } = err;
// console.log(status, statusText); // colocar función para realñizar trazabilidad de errores
return of([]);
})
);
}
/**
*
* #returns get user by id
*/
getUserById$(id: string) {
return this.http.get(`${this.URL}/${this.endpoint}/${id}`).pipe(
map((resp: any) => resp.data),
catchError((err) => {
const { status, statusText } = err;
// console.log(status, statusText); // colocar función para realñizar trazabilidad de errores
return of([]);
})
);
}
/**
*
* #returns get user id from cookie userId
*/
getUserIdFromCookie(): string {
try {
return this.cookieService.get("userId");
} catch (error) {
console.log(error);
return "";
}
}
/**
*
* #returns put user
*/
updateUser$(
updateId: string,
email: string,
name: string,
lastname: string,
cellphone: string,
role: ["user", "admin", "superadmin"],
enabled: boolean
) {
const body = {
updateId,
email,
name,
lastname,
cellphone,
role,
enabled,
};
try {
console.log(body);
return this.http.put(`${this.URL}/${this.endpoint}/${updateId}`, body);
} catch (error) {
console.log(error);
return of([]);
}
}
}
in the user services again console log the data to send and seems ok
{cellphone: "3017957267",email: "admin#prueba.co",enabled: true,lastname: "registro",name:"pruebaadmin",role: ['admin'],updateId: "629d23627d4b57d52273634c"}
thats why i don't understand the issue in this call:
-¿Why in the backend is cellphone as undefined?
-¿Why the reject of put petition if i can do it whit others class whit the same specifications?
-¿What means in the cast error, path "deleted" and path "undefined"?
some images of the procedure in interface:
PUT Submit
Error result
the problem was in backend/controller/user
i wasn't passing the id to update, i change the method:
const updateItem = async (req, res) => {
try {
const { updateId, ...body } = req.body;
// console.log(body);
const data = await usersModel.findOneAndUpdate(updateId, body);
res.send({ data });
} catch (e) {
console.log(e);
handleHttpError(res, "Register can not be updated");
}
};
so, the back way of the controller, didn't get the id to use in the method update, and the info data to update.
Trying to have my the title and description parameters of my database updated.
I've successfully built post and get routes for my data, but keep running into general 500 error for the put route when I test it on Postman. I know it's got to be something wrong with my controller.
My Imports:
import { NextFunction, Request, Response, Router } from 'express'
import { isEmpty } from 'class-validator'
import { getRepository } from 'typeorm'
import multer, { FileFilterCallback } from 'multer'
import path from 'path'
import fs from 'fs'
import User from '../entities/User'
import Sub from '../entities/Sub'
My routing:
const router = Router()
router.put('/:name', updateSub)
My controller:
const updateSub = async (req: Request, res: Response) => {
const name = req.params.name
const { description, title } = req.body
try {
const subUpdate = await Sub.update(name, { description, title })
return res.json(subUpdate)
} catch (err) {
console.log(err)
return res.status(500).json({ error: 'Something went wrong' })
}
}
EDIT Added Sub.ts entity:
import {
Entity as TOEntity,
Column,
Index,
ManyToOne,
JoinColumn,
OneToMany,
} from 'typeorm'
import Entity from './Entity'
import User from './User'
import Post from './Post'
import { Expose } from 'class-transformer'
#TOEntity('subs')
export default class Sub extends Entity {
constructor(sub: Partial<Sub>) {
super()
Object.assign(this, sub)
}
#Index()
#Column({ unique: true })
name: string
#Column()
title: string
#Column({ type: 'text', nullable: true })
description: string
#Column({ nullable: true })
imageUrn: string
#Column({ nullable: true })
bannerUrn: string
#Column()
username: string
#ManyToOne(() => User)
#JoinColumn({ name: 'username', referencedColumnName: 'username' })
user: User
#OneToMany(() => Post, (post) => post.sub)
posts: Post[]
I'm doing a nestjs application, with typeorm in postgress
I'm trying to exclude the password and accessToken columns in my JSON return, but it does not work when creating a new user. I tried using #UseInterceptors(ClassSerializerInterceptor) but it did not worked.
Here's the snippet of my code.
user.entity.ts
import { Exclude } from 'class-transformer';
import { IsEmail } from 'class-validator';
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { UserRoles } from './user.interface';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
userId: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column({ unique: true })
#IsEmail()
email: string;
#Column()
role: UserRoles;
#Exclude({ toPlainOnly: true })
#Column()
password: string;
#Column({ default: false })
isActive: boolean;
#Exclude({ toPlainOnly: true })
#Column()
accessToken: string;
user.controller.ts
#Post()
#UseInterceptors(ClassSerializerInterceptor)
async signup(#Body(new ValidationPipe({transform: true})) user: CreateUserDto): Promise<User> {
if (user.password == user.verifyPassword) {
const newuser = await this.userService.createUser(user);
if (newuser.userId) {
await this.mailService.sendVerificationEmail(newuser);
}
return newuser;
} else {
throw new HttpException(
MESSAGES.PASSWORD_NOTMATCH_ERROR,
HttpStatus.BAD_REQUEST,
);
}
}
user.service.ts
async createUser(user: CreateUserDto): Promise<User> {
user.password = await bcrypt.hash(user.password, 12);
user.accessToken = crypto.randomBytes(40).toString('hex');
user.accessTokenExpiry = new Date(
new Date().setDate(new Date().getDate() + 1),
);
return this.userRepository.save(user);
}
main.ts
import { ClassSerializerInterceptor, ValidationPipe } from '#nestjs/common';
import { NestFactory, Reflector } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
await app.listen(process.env.PORT);
}
bootstrap();
Let me know if I missed something or there is something that I did incorrectly
I'm working on some authentication in an express app, using passport, and typescript. I have defined a user model using typegoose. After passing the login request through the passport strategy, I'm calling a login method that returns the token. I'm getting a typescript error on req.user._id Property '_id' does not exist on type 'User'.
I don't have _id explicitly defined on my user model, but from what I understand with typegoose that is not necessary, and there are other places in the code that I do not have this issue. Also, trying things that are explicitly defined on the model (email, zip, etc) still produces the same error.
import { Request, Response, Router } from 'express';
import jwt from 'jwt-simple';
import passport from 'passport';
import 'dotenv/config';
import ControllerInterface from './controller.interface';
class LoginController implements ControllerInterface {
public path = '/api/login';
public router = Router();
constructor() {
this.initRoutes();
}
public initRoutes(): any {
this.router.post(this.path, [this.requireLogin, this.login]);
}
tokenForUser = (id: string) => {
const timestamp = new Date().getTime();
return jwt.encode({ sub: id, iat: timestamp }, process.env.JWT_ENCRYPTION);
};
requireLogin = passport.authenticate('local', { session: false });
login = (req: Request, res: Response) => {
// eslint-disable-next-line #typescript-eslint/ban-ts-ignore
// #ts-ignore
res.send({ token: this.tokenForUser(req.user._id) });
};
}
export default LoginController;
The passport strategy is using the user model I created with typegoose
const local = new LocalStrategy(localOptions, (email, password, done) => {
UserModel.findOne({ email }, async (err: NodeJS.ErrnoException, user) => {
if (err) return done(err);
if (!user) return done(null, false);
const isMatch = await user.comparePassword(password);
if (!isMatch) return done(null, false);
return done(null, user);
});
});
I've searched around quite a bit with no luck on this yet, thanks!
Edit: adding user model code
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import 'dotenv/config';
import { arrayProp, getModelForClass, pre, prop } from '#typegoose/typegoose';
const setUserId = () => {
return Math.random()
.toString(36)
.substr(2, 9);
};
const getUserId = (id: string) => {
return id;
};
#pre<UserClass>('save', async function(next) {
if (this.isModified('password') || this.isNew) {
const hashedPass = await bcrypt.hash(this.password, 10);
this.password = hashedPass;
this.updated = new Date();
next();
}
})
class UserClass {
#prop()
public firstName?: string;
#prop()
public lastName?: string;
public get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
public set fullName(full) {
const [firstName, lastName] = full.split(' ');
this.firstName = firstName;
this.lastName = lastName;
}
#prop({
required: true,
unique: true,
lowercase: true,
trim: true,
validate: {
validator: (email) => {
const emailRegExp = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegExp.test(email);
},
message: 'Email is invalid.',
},
})
public email!: string;
#prop({
required: true,
validate: {
validator: (password) => {
// at least 8 char
// at least 1 number
// at least 1 special character
const passwordRegExp = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[#$!%*#?&])[A-Za-z\d#$!%*#?&]{8,}$/;
return passwordRegExp.test(password);
},
message: 'Password is not in the proper format.',
},
})
public password!: string;
#prop({ default: Date.now })
public created?: Date;
#prop()
public updated?: Date;
#prop({ unique: true, set: setUserId, get: getUserId, default: setUserId() })
public userId?: string;
async comparePassword(candidatePassword: string): Promise<boolean> {
const isMatch = await bcrypt.compare(candidatePassword, this.password);
return isMatch;
}
getJWT(): string {
const expirationTime = parseInt(process.env.JWT_EXPIRATION, 10);
const bearer = jwt.sign({ userId: this.userId }, process.env.JWT_ENCRYPTION, {
expiresIn: expirationTime,
});
return `Bearer ${bearer}`;
}
toWeb(): object {
return this;
}
}
const UserModel = getModelForClass(UserClass);
export { UserClass };
export default UserModel;
You'd have to add line const { _id: id } = await UserModel.create({ .... } as User) while creating your usermodel