typescript nestjs Mongoose query, find() object inside an object - node.js

In my products schema, the product information is kept in a separate object. In the code below, I am trying to fetch all the products with the trademark Apple, but the code does not work.
async getProductWithStore(productInfoDto: ProductInfoDto) {
try {
const product = await this.productModel
.find({productInfo: productInfoDto})
.populate('store')
return product;
} catch (err) {
return err;
}
}
Here is the request
productInfo.dto.ts
import { IsNotEmpty, IsOptional } from "class-validator";
export class ProductInfoDto {
#IsNotEmpty()
trademark: string;
#IsOptional()
releaseYear: string;
#IsOptional()
model: string;
#IsOptional()
size: string;
#IsOptional()
gender: string;
#IsOptional()
ram: string;
#IsOptional()
screenSize: string;
#IsOptional()
storage: string;
}
product.schema.ts
import { Document, Types } from 'mongoose';
import {Prop, Schema, SchemaFactory} from '#nestjs/mongoose';
import {Store} from '../../store/schemas/store.schema';
import { Category } from 'src/category/schemas/category.schema';
import { ProductInfoDto } from '../dto/product-info.dto';
export type ProductDocument = Product & Document;
#Schema({timestamps:true})
export class Product {
#Prop({required: true})
productName: string;
#Prop({required: true})
description: string;
#Prop({required:true})
stock: number;
#Prop({required:true, type: Types.ObjectId, ref:'Category'})
category: Category;
#Prop({required:true, type:Object})
productInfo: ProductInfoDto
#Prop({required:true})
image:string;
#Prop({required:true, type: Types.ObjectId, ref:'Store'})
store: Store;
#Prop()
sales: number;
#Prop()
rating: number;
#Prop([String])
comments: string[]
}
export const ProductSchema = SchemaFactory.createForClass(Product);
I tried a lot of things from manually typing the information into the code but none of them worked
I found the solution
I replaced the .find code with the code below
async getProductWithStore(productInfoDto: ProductInfoDto) {
try {
const product = await this.productModel
.find({'productInfo.trademark':"Apple"})
return product;
}catch(err){
return err
}
}

Related

How to create migration for entities with properties that have relation #ManyToOne and #OneToMany with TypeORM?

I have 5 entities: User, Todos, Project, Section and Label. Relationships happen as follows:
Each User is associated with multiples Todo.
Each Todo is associated with a single User, Project and Section, and is associated with multiples Label.
Each Project is associated with a single User.
Each Section is associated with a single Project.
Each Label is associated with a single Todo.
The migration code to the #OneToOne relationship is done using the createForeignKey method, as done for project_id column at Section entity:
import {
MigrationInterface,
QueryRunner,
TableColumn,
TableForeignKey,
} from 'typeorm';
export class AddProjectIdToSections1669830961233 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
'sections',
new TableColumn({
name: 'project_id',
type: 'uuid',
isNullable: true,
})
);
await queryRunner.createForeignKey(
'sections',
new TableForeignKey({
name: 'SectionsProject',
columnNames: ['project_id'],
referencedTableName: 'projects',
referencedColumnNames: ['id'],
onDelete: 'SET NULL',
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropForeignKey('sections', 'SectionsProject');
await queryRunner.dropColumn('sections', 'project_id');
}
}
However, how do you use createForeignkey for relations of #OneToMany and #ManyToOne, and especially when the two happen round trip?
Entities code:
// User.entity.ts
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Todo } from './Todo.entity';
#Entity('users')
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
#Column()
email: string;
#Column()
password: string;
#Column({ nullable: true })
photoURL: string;
#Column()
language: string;
#CreateDateColumn()
created_at: Date;
#OneToMany(() => Todo, (todo) => todo.user)
todos: Todo[];
}
// Todo.entity.ts
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { IProject } from '../types';
import { Label } from './Label.entity';
import { Project } from './Project.entity';
import { Section } from './Section.entity';
import { User } from './User.entity';
#Entity('todos')
export class Todo {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
title: string;
#Column()
description?: string;
#Column()
type: string;
#Column({ type: 'timestamptz' })
date: Date;
#Column()
priority: number;
#Column()
isCompleted: boolean;
#OneToOne(() => Project)
#JoinColumn({ name: 'project_id' })
project: IProject;
#OneToOne(() => Section)
#JoinColumn({ name: 'section_id' })
section?: Section;
#OneToMany(() => Label, (label) => label.todo, {
cascade: true,
})
labels: Label[];
#ManyToOne(() => User, (user) => user.todos)
#JoinColumn({ name: 'user_id' })
user: User;
}
// Project.entity.ts
import {
Column,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { User } from './User.entity';
#Entity('projects')
export class Project {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
type: string;
#Column()
title: string;
#Column()
colorName: string;
#Column()
class: string;
#OneToOne(() => User)
#JoinColumn({ name: 'user_id' })
user: User;
}
// Section.entity.ts
import {
Column,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Project } from './Project.entity';
#Entity('sections')
export class Section {
#PrimaryGeneratedColumn('uuid')
readonly id: string;
#Column()
index: number;
#Column()
type: string;
#Column()
title: string;
#Column({ type: 'timestamptz' })
readonly date: Date;
#OneToOne(() => Project)
#JoinColumn({ name: 'project_id' })
project: Project;
}
// label.entity.ts
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Todo } from './Todo.entity';
#Entity('labels')
export class Label {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
type: string;
#Column()
title: string;
#Column()
colorName: string;
#Column()
class: string;
#ManyToOne(() => Todo, (todo) => todo.labels)
#JoinColumn({ name: 'todo_id' })
todo: Todo;
}

NestJS - TypeORM save() not updating document in the database

I'm in the process of learning NestJS and TypeORM with a simple project where users can book offices. I have defined the office and bookings types and entities, and written a function that searches for an existing office and adds a booking subdocument to its bookings array. Save() returns the office document with the updated bookings field, but for some reason the database is not updated.
The office type looks like this:
import { Field, ID, ObjectType } from '#nestjs/graphql';
import { BookingDto } from '../dto/booking.dto';
#ObjectType()
export class OfficeType {
#Field(() => ID)
id: string;
#Field()
title: string;
#Field()
description: string;
#Field(() => [BookingDto], { nullable: true })
bookings: string[];
#Field()
price: number;
#Field()
owner: string;
}
The booking type inside the 'bookings' field:
import { Field, ObjectType } from '#nestjs/graphql';
import { IsString, IsUUID } from 'class-validator';
#ObjectType()
export class BookingDto {
#IsUUID()
#Field()
id: string;
#IsUUID()
#Field()
officeId: string;
#IsUUID()
#Field()
userId: string;
#IsString()
#Field()
date_from: string;
#IsString()
#Field()
date_until: string;
}
And the function for booking an office:
async bookOffice(bookingInput: BookingInput): Promise<Office> {
const { officeId, userId, date_from, date_until } = bookingInput;
const booking = { id: uuid(), officeId, userId, date_from, date_until };
const office = await this.officeRepository.findOne({
id: officeId,
});
if (!office) {
throw new NotFoundException(`Office not found`);
}
office.bookings
? office.bookings.push(booking)
: (office.bookings = [booking]);
return await this.officeRepository.save(office);
}
According to the TypeORM docs, save() can be used to update documents, so I'm not sure what the problem is here.
You have to save first the booking object and then save the office with the bookings.
booking = await this.bookingRepository.save(booking);
office.bookings ? office.bookings.push(booking)
: (office.bookings = [booking]);
this.officeRepository.save(office);

I need to create a relationship with NestJs, GraphQL and prisma

I need to create one to many relationship between transaction and the user.
so the user can have multiple transactions, but the transaction can only have one user. I made the relationship in prisma, but the thing is how do I achieve this in nestjs with prisma that is the problem I'm getting. I added to the code section so I'll appreciate any help thanks.
schema.prisma :
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Crowdfund {
id String #id #default(uuid())
title String
description String
location String
target Int
image String?
}
model Institution {
institutionId String #id #default(uuid())
institutionName String
phone String
email String
location String
}
model User {
id String #id #default(uuid())
userName String
userPhone String?
password String
email String #unique
location String?
transactions Transaction[]
}
model Transaction {
id String #id #default(uuid())
amount Int
createdAt DateTime #default(now())
userId String
user User #relation(fields: [userId], references: [id])
}
create-dto from transaction :
import { InputType, Int, Field } from '#nestjs/graphql';
#InputType()
export class CreateTransactionInput {
#Field(() => Int)
amount: number;
#Field({nullable: true})
createdAt?: Date;
#Field()
userId: string;
}
entites from transaction :
import { ObjectType, Field, Int} from '#nestjs/graphql';
import { User } from 'src/user/entities/user.entity';
#ObjectType()
export class Transaction {
#Field()
id: string;
#Field(() => Int)
amount: number;
#Field()
createdAt: Date;
#Field(() => User)
user: User;
#Field()
userId: string;
}
transaction.service.ts :
import { Injectable } from '#nestjs/common';
import { PrismaService } from 'prisma/prisma.service';
import { CreateTransactionInput } from './dto/create-transaction.input';
import { UpdateTransactionInput } from './dto/update-transaction.input';
#Injectable()
export class TransactionService {
constructor(
private prismService: PrismaService
) {}
create(createTransactionInput: CreateTransactionInput) {
return this.prismService.transaction.create({data: createTransactionInput})
}
findAll() {
return this.prismService.transaction.findMany();
}
findOne(id: string) {
return this.prismService.transaction.findUnique({where: {id: id}})
}
update(id: string, updateTransactionInput: UpdateTransactionInput) {
return this.prismService.transaction.update({data: updateTransactionInput, where: {id: id}})
}
remove(id: string) {
return this.prismService.transaction.delete({where: {id: id}})
}
}
create-dto from user:
import { InputType, Int, Field } from '#nestjs/graphql';
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
#InputType()
export class CreateUserInput {
#IsString()
#IsNotEmpty()
#MinLength(3)
#Field()
userName: string;
#IsString()
#IsNotEmpty()
#Field({nullable: true})
userPhone?: string;
#IsString()
#IsNotEmpty()
#MinLength(3)
#Field()
password: string
#IsString()
#IsNotEmpty()
#MinLength(3)
#IsEmail()
#Field()
email: string;
#IsString()
#IsNotEmpty()
#Field({nullable: true})
location?: string;
}
entities from user
import { ObjectType, Field, Int } from '#nestjs/graphql';
import { Transaction } from '../../transaction/entities/transaction.entity';
#ObjectType()
export class User {
#Field()
id: string;
#Field()
userName: string;
#Field({nullable: true})
userPhone?: string;
#Field()
email: string;
#Field({nullable: true})
location?: string;
#Field(() => [Transaction], {nullable: true})
transaction: Transaction
}
user.service.ts :
import { Injectable } from '#nestjs/common';
import { PrismaService } from 'prisma/prisma.service';
import { CreateUserInput } from './dto/create-user.input';
import { UpdateUserInput } from './dto/update-user.input';
#Injectable()
export class UserService {
constructor(
private prismService: PrismaService
){}
create(createUserInput: CreateUserInput) {
return this.prismService.user.create({data: createUserInput});
}
findAll() {
return this.prismService.user.findMany({include: {transactions: true}});
}
findOne(id: string) {
return this.prismService.user.findUnique({where: {id: id}})
}
update(id: string, updateUserInput: UpdateUserInput) {
return this.prismService.user.update({data: updateUserInput, where: {id: id}})
}
remove(id: string) {
return this.prismService.user.delete({where: {id: id}})
}
}

Exclude() method is not working for post method in Nestjs

I have tried adding toJSON(){return classtoPlain(this)} in my entity still its not working.
Here is my controller
#Controller('users')
#UseInterceptors(ClassSerializerInterceptor)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
#HttpCode(200)
#Post()
async create(#Body() user: User): Promise<User> {
return await this.usersService.create(user).catch((err) => {
throw new HttpException({ message: err.message }, HttpStatus.BAD_REQUEST);
});
}
Here is my Entity
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { Gender } from '../Constants/enum';
import { Exclude, instanceToPlain } from 'class-transformer';
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName?: string;
#Column({
type: 'enum',
enum: Gender,
})
gender: Gender;
#Column({ unique: true })
email: string;
#Column()
#Exclude({ toPlainOnly: true })
password: string;
constructor(partial: Partial<User>) {
Object.assign(this, partial);
}
toJSON() {
return instanceToPlain(this);
}
}
Here is my service
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(User)
private readonly user_detailsRepository: Repository<User>,
) {}
create(user: User) {
return this.user_detailsRepository.save(user);
}
I have tried many solutions but still nothing works.
For GET its working but for post its showing the password field.If any one can provide any solution it would be great.
You're mixing between Entity definition/validation, which typeORM functions/decorators (in your case) should handle. And DTO definition, which class-transformer/validator should handle
So you should define a createUserDTO.ts file like below, and use it as in the controller/service file:
export class createUserDTO {
#IsNotEmpty()
#IsString()
firstName: string;
#IsNotEmpty()
#IsString()
lastName?: string;
#IsNotEmpty()
#IsEnum(Gender)
gender: Gender;
#IsNotEmpty()
email: string;
#Exclude({ toPlainOnly: true })
password: string;
}
The entity file shouldn't have the password column (just omit the field completely), should look like this:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName?: string;
#Column({
type: 'enum',
enum: Gender,
})
gender: Gender;
#Column({ unique: true })
email: string;
}
Then, when you call return this.user_detailsRepository.save(userDto);, it should work fine

How to define ObjectId in DTO & what is the right query to get relational data in NestJS Mongoose?

I'm setting up an app using NestJS, Angular, GraphQL and MongoDB and new to these stacks
I have 2 collections called Tasks & Statuses with sample data
Tasks
[{
"_id": {
"$oid": "5f9138f71163a739c43fc9b3"
},
"TaskDesc": "Meeting with Brian",
"StartedDate": "2020-10-22T07:42:40Z",
"EndDate": "2020-10-22T10:42:40Z",
"StatusId": "5f91375d1163a739c43fc9af"
}]
Statuses
[{
"_id": {
"$oid": "5f91375d1163a739c43fc9af"
},
"StatusDesc": "Done"
}]
Here are schemas defined in NestJS
import { Schema, Types } from 'mongoose';
import { MongooseModule } from '#nestjs/mongoose';
export const TaskSchema = new Schema({
TaskDesc: String,
StartedDate: String,
EndDate: String,
StatusId:
{
type: Types.ObjectId,
ref: 'Statuses'
}
});
export const StatusSchema = new Schema({
StatusDesc: String
});
export const SchemaGroups = MongooseModule.forFeature([
{name: 'Tasks', schema: TaskSchema, collection: 'Tasks'},
{name: 'Statuses', schema: StatusSchema, collection: 'Statuses'}
]);
The DTO
import { ObjectType, Field, ID } from '#nestjs/graphql';
#ObjectType()
export class TasksDTO {
#Field(() => ID)
id?: string;
#Field()
TaskDesc: string;
#Field()
StartedDate: string;
#Field()
EndDate: string;
#Field()
StatusId: string;
}
#ObjectType()
export class StatusDTO {
#Field(() => ID)
readonly id?: string;
#Field()
readonly StatusDesc: string;
}
The model
import { Document, Schema } from 'mongoose';
export interface Tasks extends Document {
readonly TaskDesc : string,
readonly StartedDate: string,
readonly EndDate: string,
readonly StatusId: Schema.Types.ObjectId
}
export interface Status extends Document {
readonly StatusDesc : string
}
The resolver
#Resolver('Tasks')
export class ListTodoResolver {
constructor(private readonly todoItemsService: TodolistService){
}
#Query(() => [TasksDTO])
async Tasks(): Promise<TasksDTO[]> {
return await this.todoItemsService.getAllTasks();
}
#Query(() => [StatusDTO])
async Statuses(): Promise<StatusDTO[]>{
return await this.todoItemsService.getAllStatuses();
}
}
The service
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
#Injectable()
export class TodolistService {
constructor(
#InjectModel('Tasks') private readonly todoItemsModel: Model<Tasks>,
#InjectModel('Statuses') private readonly statusItemsModel: Model<Status>
) { }
async getAllTasks() : Promise<Tasks[]>{
let tasks = await this.todoItemsModel.find().exec(); // how should we query in the service to get relational data from another collection?
console.log(tasks);
return tasks;
}
async getAllStatuses() : Promise<Status[]>{
return await this.statusItemsModel.find().exec();
}
}
And I had these errors
Type 'Tasks' is not assignable to type 'TasksDTO'. Types of property 'StatusId' are incompatible. Type 'ObjectId' is not assignable to type 'string'.
How do we define the ObjectId type in the DTO ?
My expected output for getAllTasks() method would be
[{
"_id": "5f9138f71163a739c43fc9b3",
"TaskDesc": "Meeting with Brian",
"StartedDate": "2020-10-22T07:42:40Z",
"EndDate": "2020-10-22T10:42:40Z",
"StatusDesc": "Done"
}]
The error is already show where you should fix it.
Type 'Tasks' is not assignable to type 'TasksDTO'. Types of property 'StatusId' are incompatible. Type 'ObjectId' is not assignable to type 'string'.
Which mean your DTO should be
#ObjectType()
export class TasksDTO {
#Field(() => ID)
id?: string;
#Field()
TaskDesc: string;
#Field()
StartedDate: string;
#Field()
EndDate: string;
#Field()
StatusId: objectId; // change here
}
Not sure the object Id type in your DTO, try to look up in the documentation to see if there are Object ID type for the DTO if not you should change StatusId for all place to string and that should work

Resources