Nestjs ClassSerializerInterceptor doesn't display _id - javascript

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.

Related

NestJs: DTO showing all query parameters

I'm trying to use 2 separate dto for query parameters in NestJs. However, on printing both dtos are showing same properties. Is there any way to separate them.
// dto code
export class PageDto {
#Type(() => Number)
#IsInt()
page: number;
#Type(() => Number)
#IsInt()
limit: number;
}
export class MyDto {
#ApiPropertyOptional()
#IsString()
#IsOptional()
status: string;
#ApiPropertyOptional()
#IsString()
#IsOptional()
source: string;
}
Api request:
GET url?page=1&limit=10&status='active'&source='firebase'
controller:
#Get()
async get(
#Query() myDto: MyDto,
#Query() pageDto: PageDto,
) {
console.log({pageDto}, {myDto});
}
Console statement prints:
{
pageDto: {
page: 1,
limit: 10,
status: 'active',
source: 'firebase'
}
}
{
myDto: {
page: 1,
limit: 10,
status: 'active',
source: 'firebase'
}
}
Required:
{
pageDto: {
page: 1,
limit: 10
}
}
{
myDto: {
status: 'active',
source: 'firebase'
}
}
Ideally, pageDto and myDto should show their respective properties.
You'll need to set whitelist: true in your ValidationPipe options to tell class-transformer and class-validator to strip out the properties that don't exist on the DTO.
Reference to the docs

How to write nested DTOs in NestJS

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
}

Why TypeORM repository findOne method are returning plain objects?

I recently realised on using typeorm that when I separate my entity definition from my model class and use the respective repository some methods as findOne are returning plain objects instead of model class instances. I would like to know if this is the expected behavior or if I'm losing something on my implementation.
The following code reproduce the described circunstance:
import { EntitySchema, createConnection } from 'typeorm'
class Nameable {
id: number
name: string
}
const NameableSchema = new EntitySchema<Nameable>({
name: 'nameable',
columns: {
id: { type: Number, primary: true, generated: 'increment' },
name: { type: String }
}
})
createConnection({
type: "postgres",
host: "localhost",
port: 5432,
username: "logbook",
password: "logbook",
database: "logbook",
entities: [ NameableSchema ],
synchronize: true
})
.then(databaseConnection => databaseConnection.getRepository(NameableSchema))
.then(nameableRepository => nameableRepository.findOne({ where: { id: 1 }}))
.then(findedNameable => console.log(findedNameable))
In this case, considering that I have a persisted tuple in my database which id value is equal to 1, console.log(findedNameable) is printing out the following:
{ id: 1, name: 'NameableName' }
However, I was expecting something like that:
Nameable { id: 1, name: 'NameableName' }
I have done some tests using decorators in my model class and, for what I could see, in this case, all the instances returned by the repository methods were instances from my correspondent model class. This example using decorators indeed show the expected behavior:
import { createConnection, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
#Entity()
class Nameable {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
}
createConnection({
type: "postgres",
host: "localhost",
port: 5432,
username: "username",
password: "password",
database: "database",
entities: [ Nameable ],
synchronize: true
})
.then(databaseConnection => databaseConnection.getRepository(Nameable))
.then(nameableRepository => nameableRepository.findOne({ where: { id: 1 }}))
.then(findedNameable => console.log(findedNameable))
What happens is that I really would like o keep my entity definitions separated from my models, so I have not considered to use decorators to address this problem yet.
Also, I could not find anything in the documentation pointing out that having a separated entity definition would interfere in repository classes behavior.
It looks like when using EntitySchema definitions separated from the model classes you need to specify a target value inside the options parameter indicating the class you want to map to. Also the name value should be equal to the model class name.
The following EntitySchema definition should make the question's code work as expected:
const NameableSchema = new EntitySchema<Nameable>({
name: Nameable.name, // Or 'Nameable', as you wish
target: Nameable,
columns: {
id: { type: Number, primary: true, generated: 'increment' },
name: { type: String }
}
})

How to initialize a typed Object in TypeScript/Angular?

I'm new to Angular & TypeScript and trying to figure out how to instantiate an object (before an api request is returned with the real data).
For example, my model looks like this:
//order.model.ts
export class Order {
constructor(public id: number, currency: string, public contact: Object, public items: Array<Object>) {}
}
And then I try to instantiate that in one of my components, let's say the App component:
//app.component.ts
export class AppComponent {
#Input()
public order: Order = new Order();
}
Of course, it expected to receive 4 arguments when instantiating new Order() but received 0. Do I actually have to pass in undefined/empty values for each attribute of Order?
In good ol' React (without TS) I would just initialize with an empty object and call it a day:
this.state = {
order: {}
}
What's best practice for this sort of thing in Angular/TS?
Yes as it is currently set up you would have to pass 4 default arguments to the constructor.
public order: Order = new Order(1, '', {}, []);
Or you can set each property as nullable by adding a ? like so:
export class Order {
constructor(public id?: number, currency?: string, public contact?: Object, public items?: Array<Object>) {}
}
If the class doesn't have functionality (you are simply using it for type checking) the best way to do it would be to declare an interface like so (you can also make them nullable here with ?s):
export interface Order {
id: number;
currency: string;
contact: Object;
items: Object[];
}
then in your component do not initialize the value until you have all of the needed values:
//app.component.ts
export class AppComponent {
#Input()
public order: Order;
// just an example
setValues(id: number, currency: string, contact: Object, items: Object[]) {
this.order = {
id: id,
currency: currency,
contact: contact,
items: items
}
}
// example for if you receive object with correct fields from backend
getData() {
this.service.getData().subscribe(result => {
this.order = result;
});
}
}

Uncaught TypeError: Can't add property 12, object is not extensible

I can't seem to understand the error I am getting on my client application. I am subscribing to a graphql subscription and I am able to retrieve the updates but I am not being able to push the changes to the typescript array called "models:ModelClass[]" which is bound to the view.
Is there something I am missing or doing wrong?
models.component.ts
this.apollo.subscribe({
query: gql`
subscription {
newModelCreated{
_id
name
type
train_status
deploy_status
data_path
description
created_at
updated_at
}
}
`
}).subscribe((data) => {
console.log("CREATED: " + JSON.stringify(data.newModelCreated));
console.log(data.newModelCreated);
var temp:ModelClass = data.newModelCreated;
this.models.push(temp);
});
model-class.ts
export interface ModelClass {
_id: string;
name: string;
type: string;
parameters: {
alpha: number;
};
train_status: string;
deploy_status: string;
test_accuracy: string;
created_at: number;
updated_at: number;
}
I suppose this.models is an array returned by Apollo and you want to add new created object to your initial array ? If true, Apollo returns an immutable Object !
You have to clone the initial returned array. Something like in the subscribe function:
this.apollo
.watchQuery({query: INITIAL_GQL_REQUEST})
.subscribe((data) => {
this.models = data.models.map((model) => {
return {
id: model.id,
name: model.name,
another: model.another
}
})
};
});
Then your subscription request will be able to add a created model to this plain javascript array.
PS: Not sure but I suppose Apollo returns immutable objects because they are stored in the store and depending on your fetch policy, it can miss store hits if your are able to mutate them.
Hope it helps

Categories