What is `Cat.name` in NestJS mongoose docs? - nestjs

Reading the NestJS docs on the MongoDB technique, I've came along a confusing example:
#Injectable()
export class CatsService {
constructor(#InjectModel(Cat.name) private catModel: Model<CatDocument>) {}
async create(createCatDto: CreateCatDto): Promise<Cat> {
const createdCat = new this.catModel(createCatDto);
return createdCat.save();
}
async findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}
}
The line that confuses me is the constructor one; where to #InjectModel is given Cat.name. But, in the cats.schema.ts file, there's no inheritance from another class, nor any valorised static property with that name:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type CatDocument = Cat & Document;
#Schema()
export class Cat {
#Prop()
name: string;
#Prop()
age: number;
#Prop()
breed: string;
}
export const CatSchema = SchemaFactory.createForClass(Cat);
Am I missing something or could it be a "bug" in the docs?

Cat.name in this case refers to the inherent static property name that all classes have (really all functions have). This gives us a built in constant that we can refer to without having to write our own, but if you'd prefer you can use the string 'Cat' as well. In this case, Cat.name is the string 'Cat', just a different way to reference it that is built in to all classes (and functions).
JavaScript docs on Function.name

Related

How to serialize Prisma Object in NestJS?

I have tried using Class Transformer, but it doesn't make any sense since Prisma doesn't need Entity, and Prisma Type cannot be Exclude()
Is there any way to exclude key from Prisma object e.g. createdAt or password? Thank you
I did it this way
In file: user.entity.ts
import { Role, User as UserPrisma } from '#prisma/client';
import { Exclude } from 'class-transformer';
export class User implements UserPrisma {
id: string;
name: string;
email: string;
#Exclude()
password: string;
#Exclude()
role: Role;
createdAt: Date;
updatedAt: Date;
}
There are few options
select specific fields (not ideal, lots of duplication and leaves room for error)
add user.password = undefined before returning (not very clean, also room for error)
create entity mirroring prisma schema and use class-transformer (imo this misses the point of using prisma in general. In this situation you might as well use TypeORM or Sequelize to avoid duplication and multiple sources of truth)
create custom interceptor (imo the best solution atm)
Interceptor example:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '#nestjs/common';
import { map, Observable } from 'rxjs';
#Injectable()
export class RemovePasswordInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((value) => {
value.password = undefined;
return value;
}),
);
}
}
This issue is being discussed atm, so I suggest looking into this thread

How to make parameter type-safe for method that uses Sequelize in TypeScript

I'm working with sequelize-typescript in a NestJS project and I have service classes that use sequelize models to run CRUD operations. This is an example:
import { Injectable, Inject } from "#nestjs/common"
import { Platform } from "./platform.model"
import { Game } from "../game/game.model"
import { QueryOptionsDto } from "./dto/queryOptions.dto"
import { FindOptions } from "sequelize/types"
import { PLATFORMS_REPOSITORY } from "src/core/constants"
#Injectable()
export class PlatformService {
constructor(
#Inject(PLATFORMS_REPOSITORY) private platformRepository: typeof Platform,
) {}
// the payload parameter is the problem here
public async addPlatform(payload) {
try {
const platform = await this.platformRepository.create(payload)
return platform
} catch (err) {
return Promise.reject(err)
}
}
}
But I can't assign any type to the payload parameter. Naturally, I want to make this type-safe, but TypeScript seems to want the type of the object passed to .create() to be the same type as the corresponding model and have all the same properties and methods. This is the error I get if I try to give it an object type with the properties it should have ({ name: string, year: number }):
Argument of type '{ name: string; year: number; }' is not assignable to parameter of type 'Platform'.
Type '{ name: string; year: number; }' is missing the following properties from type 'Platform': games, $add, $set, $get, and 33 more.
The only solution is to just leave the type as inferred any but this obviously defeats the purpose of using TypeScript.
Any suggestions?

Nestjs how to make extend partialtype(createDto) make nested properties of dtos inside createDto also optional

I have UpdateUserDto:
export class UpdateUserDto extends PartialType(CreateUserDto) {
}
CreateUserDto:
export class CreateUserDto {
#ValidateNested({ each: true })
#IsOptional()
Point: CreateUserPointDto;
}
CreateUserPointDto:
export class CreateUserPointDto{
#IsString()
name: string
#IsString()
color: string
}
Now partial type makes all properties of CreateUserDto optional, the problem is, it doesn't create all properties of Point that is inside CreateUserDto optional.
How do I go about solving this issue?
Also another unrelated problem, any validation to Point in UpdateUser only works with { PartialType } from '#nestjs/mapped-types'
If I use import { PartialType } from '#nestjs/swagger', For the same code it says Point.property name/color should not exist.
I'm sure you may have moved on from this, but here's something that may resolve the issue in case you come around in the future.
You need to use #Type from class-transformers to ensure you get the types for the nested Point attribute.
Sample code
import { Type } from 'class-transformer';
export class CreateUserDto {
#ValidateNested({ each: true })
#IsOptional()
#Type(() => CreateUserPointDto) // -> this line
Point: CreateUserPointDto;
}

NestJS - Creating Business Objects From Other Services

Nowadays I'm playing around with NestJS and trying to understand the best practices of NestJS world.
By following official documentations I have created service/dto/entity/controller for my business object called 'Cat'.
(also using SequelizeJS)
cat.entity.ts
#Table({
freezeTableName: true,
})
export class Cat extends Model<Cat> {
#AllowNull(false)
#Column
name: string;
#Column
breed: string;
}
create-cat.dto.ts
export class CreateCatDto {
#IsString()
readonly name: string;
#IsString()
readonly breed: string;
}
cat.service.ts
export class CatService {
constructor(#Inject('CAT_REPOSITORY') private readonly CAT_REPOSITORY: typeof Cat) {}
async create(createCatDto: CreateCatDto): Promise<Cat> {
const cat = new Cat();
cat.name = createCatDto.name;
cat.breed = createCatDto.breed;
return await cat.save();
}
}
Now I can make POST requests to my controller and create Cat objects successfully. But I want to create a 'Cat' from other service and the create() method only takes CreateCatDto which I cannot/shouldn't initialize (it's read-only).
How can I call create(createCatDto: CreateCatDto) from other services? How you are handling this kind of requirements with NestJS? Should I create one more method like createFromEntity(cat: Cat) and use it?
try this:
const cat: CreateCatDto = { name: 'Miau', breed: 'some' };

Node.js and sequelize-typescript - data access objects and business objects

I am using the sequelize-typescript in my Node.js service
I have the Category class which maps to category table
import { Model, Table, Column } from "sequelize-typescript";
#Table
export class Category extends Model<Category>{
#Column
name: string
}
I also have CategoryController and CategoryService
export class CategoryController {
...
async getAll(request: Request, response: Response) {
let categories = await this.categoryService.getCatergories();
response.json(categories)
}
}
export class CategoryService {
async getCatergories(): Promise<Category[]> {
let categories = await Category.findAll<Category>()
return categories
}
}
And everything is as it should be.
But returning a Category to the controller allows it to use the use the inherited methods from the model class like:
export class CategoryController {
...
async getAll(request: Request, response: Response) {
let categories = await this.categoryService.getCatergories();
// Remove associated row in the database
categories[0].destroy()
response.json(categories)
}
}
I was thinking to create a CategoryModel class like this:
export class CategoryModel {
id : number
name : string
}
And modify all methods in CategoryService to return CategoryModel instances instead of Category and rename Category to CategoryEntity
What is the best way to deal with such a problem?
Use toJSON() of Category instance to get "a JSON representation" of the instance.
See sequelize docs for more information: http://docs.sequelizejs.com/class/lib/model.js~Model.html#instance-method-toJSON
Additionally you could add an interface to achieve type safety for the return value of toJSON() instead of defining another class:
interface ICategory {
id: number;
name: string;
}
#Table
export class Category extends Model<Category> implements ICategory{
#Column
name: string
}
Using toJSON():
Category.findOne(result => {
const category: ICategory = result.toJSON();
});

Resources