Can't strip out _id from mongoose objects - nestjs

As per nestjs doc page I am using this code:
#Controller('books')
export class BooksController {
constructor(
private readonly bookService: BooksService,
) {}
#SerializeOptions({
excludePrefixes: ['_'],
})
#Get()
async getAll(): Promise<Book[]> {
return this.bookService.getAll();
}
}
trying to strip _id (and presumably __v too) properties from the documents which are coming from mongodb/mongoose. Is there any other step I need to take to make it work? I have also tried placing the #SerializeOptions decorator over the #Controller - to no avail.
(It's only an exercise, IRL I would probably map id

Related

TypeORM check if column has changed

So, I'm using TypeORM with the ActiveRecord pattern and have this entity
#Entity()
export class User {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public username: string;
#Column()
public password: string;
#BeforeInsert()
public async hashPassword() {
this.password = await hashPassword(this.password);
}
}
now what I want to accomplish is rehashing my password when any given User changes BUT only if the password field changed. I have seen some answers where I should store the tempPassword as a field in the User class but if for some reason the server goes down I would lose that data. I have also seen some people suggest the Subscriber thing typeorm has and im interested in that but not exactly sure how I would implement this with that.
for reference this is how I would do what I want to do with mongoose
UserSchema.pre("save", function(next) {
if (this.isModified("password") || this.isNew) {
// hash the password
}
})
any help is appreciated
According to TypeOrm documentation, You can define a method with any name in the entity and mark it with #BeforeUpdate decorator and TypeORM will call it before an existing entity is updated using repository/manager save.
#BeforeUpdate()
async updatePassword(): Promise<void> {
this.password = await hashPassword(this.password);;
}
You can check inside whatever you want, but I don't think it'll be necessary because if the password field is not changed it will be the same, and ORM will not update this column anyway. It'll only be updated if it's changed.
Mongoose provides us with the isModified() & isNew() methods because it does not handles the checks for you, you need to explicitly check if the field is actually modified or new.

What is the correct way to implement interfaces with node and mongoose?

I have an interface created for my model, where I only want to return specific data from the record
// code.interface.ts
import { Document } from 'mongoose';
export interface CodeI extends Document {
readonly _id: string;
readonly logs: any;
}
But when I get the result from mongo, it completely ignores what is in my interface. (I am using NestJs as framework)
//constructor
constructor(#InjectModel(Coupon.name) private couponModel: Model<CouponDocument>) {}
// function
async findOne(codeId: string): Promise<CodeI> {
const coupon = await this.couponModel.findOne({ _id: codeId }).exec();
if (!coupon) {
throw new NotFoundException([`#${codeId} not found`]);
}
return coupon;
}
TypeScript interfaces don't work this way. They can't limit the fields of an object because they don't exist at runtime, so, we can't use them to guide any runtime behavior. TypeScript interfaces are useful for compile-time type check only.
However, in your case, there are two ways you can achieve the expected behavior.
The first one is to select only the required fields which you need to return (recommended).
In your findOne, you can do something like this
async findOne(codeId: string): Promise<CodeI> {
const coupon = await this.couponModel.findOne({ _id: codeId }, '_id logs').exec();
if (!coupon) {
throw new NotFoundException([`#${codeId} not found`]);
}
return coupon;
}
Here, as you can see, I have passed an additional string type parameter to findOne function which is projection and it will select only the specified fields from the object. This will not only solve your problem but also save query time and have increase query performance. Read more about findOne here.
The other way is to create a DTO where you can define the fields you want to return from the function.
Something like this:
// CouponDto.ts
class CouponDto {
public readonly _id: string;
public readonly logs: any;
constructor(data: CodeI) {
this._id = data._id;
this.logs = data.logs;
}
}
Then, in your service file, you can do something like
return new CouponDto(coupon);
(make sure to change the return type of the function to CouponDto as well)
You can use any of these two ways. While I would recommend going with the first one, it's up to you and how you wanna structure your project.
External Links:
Mongoose FindOne Docs

Does nestjs support the pattern multiple DTO's are used to "Infer" which controller method to use on a single route?

Disclaimer: My aspiration has obvious and good alternatives, but from what little I know about Nestjs I would not be surprised if supported the following "pattern" somehow.
Given these 2 DTOs:
export class AddExistingMemberDto {
#IsInt()
memberId: number;
}
export class AddNonExistingMemberDto {
#IsString()
username: string;
#IsEmail()
email: string;
}
Can Nestjs check which DTP the request body fulfils, and switch between different controller methods?
I tried:
#Controller('group')
export class GroupController {
#Patch(':id/members/add')
addExistingMember(
#Body() addExistingMemberDto: AddExistingMemberDto,
) {
console.log('Existing', addExistingMemberDt);
}
#Patch(':id/members/add')
addNonExistingMember(
#Body() addNonExistingMemberDto: AddNonExistingMemberDto,
) {
console.log('Non-existing', addNonExistingMemberDto);
}
}
But this always invokes with the first method.
Rationale: Having one controller method that checks the DTO is a fine alternative, but it does require me to merge my two different DTO classes. So I was just curious.

Typeorm find returns 404

i have a find() query to my database, which i want to be able return a list of all my questions in that table.
The serivce
async findAll() : Promise<Question[] >{
return await this.questionRepository.find();
}
The controller
#Get('/all')
async returnAllQuestions(): Promise<Question[] > {
return await this.questionsService.findAll();
}
The question entity
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity({name: 'Questions'})
export class Question {
#PrimaryGeneratedColumn()
questionID: number;
#Column()
question: string;
}
There are records inside my table
and the types are set as follows
However when i call the /all endpoint i get a 404 not found error.
I have no clue what is wrong, I can call an findById endpoint, using uestionRepository.findOneOrFail(id); which is working fine, but the all method keeps failing'
Thanks in advance!

Schema Generator cannot determine GraphQL output type for custom object within DTO

Coming from a laravel background I try to get into node.js/graphQL/MongoDB and stumbled over nest.js framework which looks pretty nice. So I tried to set up a simple GraphQL API for testing and understanding how it all works. Therefore I created a mongoose Schema for a user as well as a model (type-graphql) and a DTO for creating such a user via mutation.
This works pretty fine but then I wanted to add a nested object called settings within the user to try how this would work and I simply don't get it and also don't find any nest.js or type-graphql specific examples for such an implementation. Is this simply not feasible using the "code first" approach of nest.js? Because the schema generator always gives me an error while compilation saying :
"UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL output type for settings".
user.model.ts
import { Field, ID, ObjectType } from 'type-graphql'
import UserSettingsType from './user.settings.type'
#ObjectType()
export class User {
#Field((type) => ID)
id: string
#Field()
email: string
#Field((type) => UserSettingsType)
settings: {}
}
user.settings.type.ts
import { Field, ObjectType } from 'type-graphql'
#ObjectType()
export class UserSettings {
#Field()
theme: string
#Field()
description?: string
#Field((type) => [String])
visited: string[]
}
new-user.input.ts
import { IsOptional, IsEmail, IsBoolean, Length, MaxLength, IsArray } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import UserSettingsType from '../graphql/user.settings.type'
#InputType()
export class NewUserInput {
#Field()
#IsEmail()
email: string
#Field((type) => UserSettingsType)
#IsOptional()
#IsArray()
settings: {}
}
As you can see I defined a new type UserSettings in a separate file (following the best practices of nest.js) and allocated it as a type within the User model as well as the new-user DTO class. The error is thrown for the DTO class (NOT for the model) and if I change it there to something like [String] instead of UserSettingsType it is compiling.
I'm not sure if you need the resolver or the service/mongoose logic for this actual problem if so I can also post that of course!
Had similar issue migrating from express to nestjs.
After reading the docs realised that problem isn't the nested objects, but wrong imports.
You should use #nestjs/graphql imports instead of type-graphql.
E.G.
// changes this
import { Field, ID, ObjectType } from 'type-graphql'
// to
import { Field, ID, ObjectType } from '#nestjs/graphql'
Same applies to any other import from type-graphql

Resources