mongoose Document doesn't map a property correctly - node.js

I have a nodeJS Server with express project with a mongoDB.
I want to map this object, coming from an endpoint:
{
userId: '5ca14305e73fc8453843d3e1',
bookingId: '29b0c5e0-e504-43bc-b5a1-9326d7d41d45'
}
Its defined through a class like this:
export class CreateBookerDto {
#IsString() readonly userId: string;
#IsString() readonly bookingId: string;
}
This is my actual code doing the mapping:
export interface IBooker extends Document {
userId: string;
bookingId: string;
createdAt?: number;
}
constructor(
#InjectModel('Booker') private readonly bookerModel: Model<IBooker>
) { }
async createBooker(booker: CreateBookerDto) {
let createdBooker = new this.bookerModel(booker);
createdBooker.createdAt = moment.now();
return createdBooker.save();
}
This is my output from createdBooker before the save:
{
_id: 5ca146e4ba2c08380c435453,
bookingId: '29b0c5e0-e504-43bc-b5a1-9326d7d41d45'
}
Where and why does he drop the properties userId and createdAt?

Related

How to validate Dynamic key -> value DTO validation in nest js?

import { ApiProperty } from '#nestjs/swagger';
import { IsString, ValidateNested } from 'class-validator';
export class TestDto {
#ApiProperty()
test: string;
}
export class UserReqDto {
#ApiProperty()
#IsString()
id: string;
#ApiProperty()
#ValidateNested({ each: true })
data: object;
}
const sampleData = {
id: 'asbd',
data: {
['any dynamic key 1']: {
test: '1',
},
['any dynamic key 2']: {
test: '2',
},
},
};
Here UserReqDto is my main DTO and TestDto is child DTO.
I need to validate sampleData type of data.
How can I do that?
in data field i need to validate object of TestDto type's objects
You can use Map<string, TestDto> as type for data field:
#ApiProperty()
#ValidateNested({ each: true })
data: Map<string, TestDto>
Note: Nested object must be an instance of a class, otherwise #ValidateNested won't know what class is target of validation so you can use class-transformer to transform data value to instance of TestDto.
#ApiProperty()
#ValidateNested({ each: true })
#Type(() => TestDto)
data: Map<string, TestDto>

Nest.JS validate object where keys are enum and values ​have the same shape

I need to validate an object where each key is an enum and each value have the same shape.
Right now I was able to validate the object by explicitly setting each key and validate each nested object, but I would like something more usable so when I need to change the enum I don't have to update the dtos too.
This is the enum I would like to use
enum Countries {
ES = 'ES',
IT = 'IT'
}
This is my actual work.
class Country {
#IsString({ each: true })
#IsOptional()
readonly region?: string[];
#IsString({ each: true })
#IsOptional()
readonly province?: string[];
#IsString({ each: true })
#IsOptional()
readonly zipcode?: string[];
}
class Locations {
#IsObject()
#ValidateNested()
#Type(() => Country)
#IsOptional()
readonly ES?: Country;
#IsObject()
#ValidateNested()
#Type(() => Country)
#IsOptional()
readonly IT?: Country;
}
export class CreateDto {
... other props
#IsObject()
#ValidateNested()
#Type(() => Locations)
#IsOptional()
readonly locations?: Locations;
}
As you can see if I add a new country to the enum I have to update the class Locations with the corresponding prop.
## Example Payload
{
... other props
locations: {
IT: {
region: ['Lazio']
}
}
}
Data structures that frequently need to change keys are not good. I think the data structure should be designed like this:
enum Country {
ES = 'ES',
IT = 'IT'
}
class Location {
#IsEnum(Country)
country: Country;
#IsString({ each: true })
#IsOptional()
readonly region?: string[];
#IsString({ each: true })
#IsOptional()
readonly province?: string[];
#IsString({ each: true })
#IsOptional()
readonly zipcode?: string[];
}
export class CreateDto {
... other props
#ValidateNested({ each: true })
#Type(() => Location)
#IsOptional()
readonly locations?: Location[];
}

nestjsx/crud typeorm relations query

How do I have to write the query in order to get relations of the entity populated with?
Checked the below options, but none of them works.
localhost:3000/reservations?join=concert_id
localhost:3000/reservations?join=concert
If I change the concert_id key within the join object of #Crud() options to concert (as the name of the property in the Reservation.entity.ts file is just concert, not concert_id), then I get an error error: column reference "Reservation_id" is ambiguous.
Any help is much appreciated. Thanks in advance!
Reservation.entity.ts
#Entity('reservations')
export class Reservation {
#PrimaryGeneratedColumn()
id: number;
#Column({ name: 'booking_reference' })
bookingReference: string;
#ManyToOne(() => Concert, (concert) => concert.reservations)
#JoinColumn({ name: 'concert_id' })
concert: Concert;
}
Concert.entity.ts
#Entity('concerts')
export class Concert {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(() => Reservation, (reservation) => reservation.concert)
reservations: Reservation[];
}
Reservation.controller.ts
#Crud({
model: {
type: Reservation
},
dto: {
create: CreateReservationDto
},
validation: { always: true },
query: {
join: {
concert_id: {
eager: true,
alias: "concert"
}
}
}
})
#ApiTags('Reservation')
#Controller('reservation')
export class ReservationController implements CrudController<Reservation> {
constructor(public service: ReservationService) {}

Nest js authorization where should i give user calss property of Roles

in nest js Documentation i've read about Basic RBAC implementation but the last thing of this section says
"To make sure this example works, your User class must look as follows"
class User {
roles: Role[];
}
where should this line is going to be in
Check out authentication part of documentation. In implementing passport strategies paragraph you have UsersService defined like this:
import { Injectable } from '#nestjs/common';
// This should be a real class/interface representing a user entity
export type User = any;
#Injectable()
export class UsersService {
private readonly users = [
{
userId: 1,
username: 'john',
password: 'changeme',
},
{
userId: 2,
username: 'maria',
password: 'guess',
},
];
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
}
You can create user.ts file near this service and import it here instead of defining type. How this class should look depends on source from which you get it. In this example users are hard-coded but usually that would be some kind of database entity.
Hard-coded example
For this hard-coded example I would do User class like this:
user.ts
import { Role } from "./role.enum";
export class User {
userId: number;
username: string;
password: string;
roles: Role[];
}
Where roles are in enum defined in authorization part of documentation
role.enum.ts
export enum Role {
User = 'user',
Admin = 'admin',
}
All this is joined inside service like this:
users.service.ts
import { Injectable } from '#nestjs/common';
import { User } from './user.entity';
import { Role } from "./role.enum";
#Injectable()
export class UsersService {
private readonly users: User[] = [
{
userId: 1,
username: 'john',
password: 'changeme',
roles: [Role.Admin]
},
{
userId: 2,
username: 'maria',
password: 'guess',
roles: [Role.User]
},
];
async findOne(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
}
Database example
Usually you would use some kind of database (more on database integration here), when using TypeOrm those classes would look like this:
user.entity.ts
import { Role } from "../role.enum";
import { Injectable } from '#nestjs/common';
import { UserEntity } from '../hard-coded/user';
import { InjectRepository } from "#nestjs/typeorm";
import { Repository } from "typeorm";
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn() userId: number;
#Column() username: string;
#Column() password: string;
// should have some kind of join table
#ManyToMany() roles: Role[];
}
users.service.ts
#Injectable()
export class UsersService {
constructor(#InjectRepository(UserEntity) private usersRepository: Repository<UserEntity>){}
async findOne(username: string): Promise<UserEntity | undefined> {
return this.usersRepository.findOne({ username });
}
}

How to correctly configure and count a belongsToMany association in Sequelize?

I'm having problems this belongsToMany thing in Sequelize.
I'm creating a video content site. One video can be in many lists and obviously, a list can have many videos(n:n). A video can only belong to one channel (1:n).
A list, video and channel are associated with an account (all 1:n).
I want to get the all the lists with the number of videos in each one.
So I created the models this way:
Video:
export class Video extends Model {
public static associations: {
channel: BelongsTo;
account: BelongsTo;
list: BelongToMany;
};
public id: string;
public title: string;
public desc: string;
public createdAt: Date;
public updatedAt: Date;
public channelId: string;
public channel: Channel;
public getChannel: BelongsToGetAssociationMixin<Channel>;
public setChannel: BelongsToSetAssociationMixin<Channel, string>;
public accountId: string;
public account: Conta;
public getAccount: BelongsToGetAssociationMixin<Account>;
public setAccount: BelongsToSetAssociationMixin<Account, string>;
}
Video.init(
{
title: STRING(100),
descricao: STRING(500),
},
{
sequelize,
modelName: 'videos',
hooks: {
beforeCreate: (model, options) => {
model.id = uuidv4();
}
}
}
);
import { Channel } from './channels';
export const channel = Video.belongsTo(Channel, { foreignKey: 'channelId' });
import { Account } from './accounts';
export const account = Video.belongsTo(Account, { foreignKey: 'accountId' });
import { List } from './lists';
export const lists = Video.belongsToMany(List, {
foreignKey: 'videoId' ,
through: 'videos_list',
timestamps: false
});
List:
export class List extends Model {
public static associations: {
account: BelongsTo;
videos: BelongsToMany;
};
public id: string;
public name: string;
public desc: string;
public createdAt: Date;
public updatedAt: Date;
public accountId: string;
public account: Conta;
public getAccount: BelongsToGetAssociationMixin<Account>;
public setAccount: BelongsToSetAssociationMixin<Account, string>;
public videos: Video[];
public getVideos: BelongsToManyGetAssociationsMixin<Video>;
public setVideos: BelongsToManySetAssociationsMixin<Video, string>;
public addVideo: BelongsToManyAddAssociationMixin<Video, string>;
public addVideos: BelongsToManyAddAssociationsMixin<Video, string>;
public createVideo: BelongsToManyCreateAssociationMixin<string>;
public countVideos: BelongsToManyCountAssociationsMixin;
public hasVideo: BelongsToManyHasAssociationMixin<Video, string>;
public removeVideo: BelongsToManyRemoveAssociationMixin<Video, string>;
public removeVideos: BelongsToManyRemoveAssociationsMixin<Video, string>;
}
Lista.init(
{
name: STRING(100),
desc: STRING(500),
},
{
sequelize,
modelName: 'lists',
hooks: {
beforeCreate: (model, options) => {
model.id = uuidv4();
}
}
}
);
import { Account } from './accounts';
export const account = Lista.belongsTo(Account, {
foreignKey: 'accountId',
as: 'account'
});
import { Video } from './videos';
export const videos = List.belongsToMany(Video, {
foreignKey: 'listId',
through: 'videos_list',
timestamps: false
});
and I'm doing the query like this:
List.findAll({
attributes: [
'name', 'desc',
[ Sequelize.fn("COUNT", Sequelize.col("videos.id")), "countVideos" ]
],
include: [{ model: Video, as: 'videos', attributes: [] }],
group: ['videos.listId'],
order: [['name', 'DESC']]
})
But I'm getting the following error:
DatabaseError [SequelizeDatabaseError]: column videos.listId does not exist
What am I doing wrong here ?
I listed only List and Video models that I think were relevant. If you need any other please let me know and I'll include it.
In List.belongsToMany(Video... you have videoId as FK instead of listId.

Resources