Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Nest.js way of creating static and instance functions for a model?

Does such a thing exist or do I follow standard Mongoose procedure?

I read the docs, I spent the whole day yesterday for this, but I could only find relative ones that placed the functions inside the service component. This is not effective as if I would like to use a static model function outside of the service component (say, a custom decorator), it wouldn't reach it as DI is private.

I would have created an Issue on Github for documentation request, but I feared I may have overlooked something.

Edit 2: Please do not change the title of the post. "Nest" is not a typo for "best". It is referring to a Node.js framework called Nest.js. (See post tags and referenced documentation link)

Edit: In the MongoDB section of the docs, there's this piece of code:

constructor(@InjectModel(CatSchema) private readonly catModel: Model<Cat>) {}

but specifically, this Model<Cat> part, imports Cat from an interface that extends Mongoose Document interface. Wouldn't it be better if this Cat interface was a class instead which was capable of functions (even after transpilation)?

like image 457
msamprz Avatar asked Nov 26 '25 11:11

msamprz


1 Answers

I use the following approach:

When defining the schema, add static methods to the Mongoose schema:

UserSchema.methods.comparePassword = async function(candidatePassword: string) {
  return await bcrypt.compare(candidatePassword, this.password);
};

Also include the method in the object's interface definition:

export interface User {
  firstName: string;
  ...
  comparePassword(candidatePassword: string): Promise<boolean>;
}

as well as the UserDocument interface

export interface UserDocument extends User, Document { }

So now my UsersService:

export class UsersService {
  constructor(@InjectModel(Schemas.User) private readonly userRepository: Model<UserDocument>,
              private readonly walletService: WalletsService,
              @Inject(Modules.Logger) private readonly logger: Logger) {}

  async findByEmail(email: string): Promise<UserDocument> {
    return await this.userRepository.findOne({ email }).select('password');
  }
  ...
}

And to tie it all together, when a user tries to log in, the Auth service retrieves a user object by id, and invokes that user object's instance method of comparePassword:

@Injectable()
export class AuthService {
  constructor(
    private readonly usersService: UsersService,
    private readonly jwtService: JwtService,
  ) { }

  async signIn({ email, password }: SignInDto): Promise<LoginResponse> {
    const user = await this.usersService.findByEmail(email);
    if (!user) { throw new UnauthorizedException('Invalid Username or Password'); }

    if (await user.comparePassword(password)) {
      const tokenPayload: JwtPayload = { userId: user.id };
      const token = this.jwtService.sign(tokenPayload);
      return ({ token, userId: user.id, status: LoginStatus.success });
    } else {
      throw new UnauthorizedException('Invalid Username or Password');
    }
  }
}
like image 103
pantera Avatar answered Nov 29 '25 01:11

pantera



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!