I am a beginner in NestJS and I want to write a DTO for below structure -
{
something: {
info: {
title: string,
score: number,
description: string,
time: string,
DateOfCreation: string
},
Store: {
item: {
question: string,
options: {
item: {
answer: string,
description: string,
id: string,
key: string,
option: string
}
}
}
}
}
}
I want to write a DTO for that nested Data object. I can't find a solid example for writing nested DTO in NestJS. I am a beginner in NestJS and I have never worked with DTO before. So please don't assume that I know something. I am using it with Mongoose.
You will have to create separate classes for each object in your schema and a main class which will import all the classes.
class Info {
readonly title:string
readonly score:number
readonly description:string
readonly dateOfCreation:Date
}
export class SampleDto {
#Type(() => Info)
#ValidateNested()
readonly info: Info
...Follow same for the rest of the schema
}
Refer: https://github.com/typestack/class-validator#validating-nested-objects
//example :
export class UserBaseDto {
#ApiProperty({
type: String,
required: true,
description: 'email user, minlength(4), maxlength(40)',
default: "test#email.com",
})
#IsString()
#MinLength(4)
#MaxLength(40)
#IsNotEmpty()
email: string;
//....enter code here
}
Related
I have an entity
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
autogeneratedCode: string;
I'm getting name and string only in my GET request response which is expected.
But when I'm hit my POST Api with body, it is returning name, email, autogeneratedCode as well.
I need to hide autogeneratedCode in all CRUD responses.
Is there anyway to do that? or Am I missing something here?
You can use #Exclude() from 'class-transformer'
Example
import { Exclude } from 'class-transformer';
#Entity()
export class User {
#Column()
name: string;
#IsEmail()
email: string;
#Column({ select: false })
#Exclude()
autogeneratedCode: string;
constructor(entity: Partial<User>) {
Object.assign(this, entity);
}
}
Then you can use the constructor to create a new object excluding the #Exclude() properties.
export class UserService {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>
) {}
public async createUser(user: User): Promise<User> {
return new User(
await this.userRepository.save(user)
);
}
}
NestJS Doc on Serialization
https://docs.nestjs.com/techniques/serialization#exclude-properties
I have an issue properly exposing the _id using the Serializer.
I use:
#UseInterceptors(ClassSerializerInterceptor)
#SerializeOptions({ strategy: 'excludeAll' })
The defined Class:
export class UpdatedCounts {
#Expose()
_id: ObjectId;
#Expose()
aCount: number;
#Expose()
bCount: number;
constructor(partial: Partial<MyDocument>) {
Object.assign(this, partial);
}
}
The object in console.log() before it runs through the Serializer
{
_id: new ObjectId("61c2256ee0385774cc85a963"),
bannerImage: 'placeholder2',
previewImage: 'placeholder',
aCount: 1,
bCount: 0,
}
The object being returned:
{
"_id": {},
"aCount": 1,
"bCount": 0
}
So what happened to my _id?
I tried using string type instead of ObjectId but that also does not work
I do not want to use #Exclude since there are 10 more props which I left out in the example console.log(), and it should be easier to exclude all and just use these 3
Just use #Transform:
#Expose()
#Transform((params) => params.obj._id.toString())
_id: ObjectId;
You can not just send ObjectId with JSON. You must convert it to a string.
I am learning how to use the Adonis framework (v5) and as the tradition dictates it, I am creating a todo list api to test it out.
The problem I am having is regarding the relationship between the User and Todo entities.
Here are the two models:
// file: app/Models/Todo.ts
export default class Todo extends BaseModel {
#column({ isPrimary: true })
public id: number
#belongsTo(() => User, {
foreignKey: 'id',
})
public author: BelongsTo<typeof User>
#column()
public completed: boolean
#column()
public title: string
#column()
public description: string | null
#column.dateTime({ autoCreate: true })
public createdAt: DateTime
#column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
}
// file: app/Models/User.ts
export default class User extends BaseModel {
#column({ isPrimary: true })
public id: number
#column()
public username: string
#column({ serializeAs: null })
public password: string
#hasMany(() => Todo, {
foreignKey: 'author',
})
public todos: HasMany<typeof Todo>
#column.dateTime({ autoCreate: true })
public createdAt: DateTime
#column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
#beforeSave()
public static async hashPassword(user: User) {
if (user.$dirty.password) {
// Only hash password if required
user.password = await Hash.make(user.password)
}
}
}
I did not include the migrations files but I will edit this question if they are needed.
What I am expecting is that I should be able to create and save Users into my database, the same for Todo entries and link the todo entries to their author. The documentation provides us with an example but I don't see where I am doing something wrong.
So to test it out I am using the node ace repl command as follow:
// log of running the commands in the AdonisJS v5 REPL
> loadModels()
recursively reading models from "app/Models"
Loaded models module. You can access it using the "models" variable
> undefined
> const testUser = await models.User.create({ username: 'testUser', password: 'password' })
undefined
> await testUser.related('todos').create({ title: 'Example todo entry' })
Uncaught:
Exception: E_MISSING_MODEL_ATTRIBUTE: "User.todos" expects "author" to exist on "Todo" model, but is missing
at <my-app-directory>\REPL23:1:39
at Proxy.related (<my-app-directory>\node_modules\#adonisjs\lucid\build\src\Orm\BaseModel\index.js:1436:18)
at HasMany.boot (<my-app-directory>\node_modules\#adonisjs\lucid\build\src\Orm\Relations\HasMany\index.js:74:12)
at KeysExtractor.extract (<my-app-directory>\node_modules\#adonisjs\lucid\build\src\Orm\Relations\KeysExtractor.js:28:39)
at Array.reduce (<anonymous>)
at <my-app-directory>\node_modules\#adonisjs\lucid\build\src\Orm\Relations\KeysExtractor.js:32:23
>
I don't understand the error message since author actually exists on the Todo model.
How can I solve this problem and get my todo app up and running?
Thank you by advance!
The error is that you are missing a field inside your model.
You need to define all fields with the #column() decorator. In your example, you are not defining the column author.
When creating a relation, you must have one column (the FK) and one relation.
If we assume that you have a column user_id inside your todos table, then you need to add the column user_id inside your Todo model.
Here is a correct example:
class User extends BaseModel {
// ...
#hasMany(() => Todo)
todos: HasMany<typeof Todo>
}
class Todo extends BaseModel {
#column()
user_id: number
#belongsTo(() => User)
author: BelongsTo<typeof User>
}
I have a lot of UUIDs in various places throughout my database, and the readability would be significantly improved if they contained a prefix to denote the type of entity they are for, such as user-09a9c8fb-dcb6-5d18-9697-e1d10c552c14 or comment-c80c8502-4cd9-483f-9b81-705a22dba3a8. Is there any way to accomplish this purely with TypeORM syntax?
Here is an example of my current user model:
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
} from 'typeorm';
#Entity()
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({ unique: true })
username: string;
#Column()
email: string;
#CreateDateColumn()
createdAt: Date;
}
I'm having trouble populating my user model with typescript. I have a property of the user's scheme which is the reference to his profile. To implement mongoose with typescript I had to define a tpo / interface in addition to the schema. My problem is that I do not know what type I have to assign to the profile property. Because if I run the query without populating it, it will be an "ObjectId", but if I populate it will contain the whole profile document.
export type UserModel = mongoose.Document & {
name: string;
username: string;
email: string;
password: string;
profile: Schema.Types.ObjectId; // If I leave this property with this type, in case I populate a typescript query it would give me an error.
};
export const UserSchema = new Schema({
name: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
profile: {
type: Schema.Types.ObjectId,
ref: "Profile"
}
});
export default model<UserModel>("User", UserSchema);
My problem is that I do not know what type I have to assign to the
profile property.
You are going to need to define 2 types/interfaces.
export default model<UserModel>("User", UserSchema);
This export will have to use a different interface in which you have the populated "Profile" document.
For the schema itself you can leave it as an objectID type.
Try to set the populate pointing to the model, this should do the trick
User.find({}).populate([{"path":"profile", "model":"Profile" }])