why MongoDB returns a CastError when I am trying to delete an Item by id? - mongodb

I am trying to make the DELETE Rest API method but I get CastError.
The problem is with the id it's of type ObjectID and I made it as a number, even when I chose string I got the same error.
Error
[Nest] 15504 - 22/12/2021, 21:34:11 ERROR [ExceptionsHandler] Cast to ObjectId failed for value "{ id: '61c32ba552a7cec272037b12' }" (type Object) at path "_id" for model "City"
CastError: Cast to ObjectId failed for value "{ id: '61c32ba552a7cec272037b12' }" (type Object) at path "_id" for model "City"
at model.Query.exec (C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\mongoose\lib\query.js:4594:21)
at CitiesService.deleteCity (C:\Users\ouss\Desktop\coffeeit-assessment\src\cities\cities.service.ts:46:64)
at CitiesController.deleteCity (C:\Users\ouss\Desktop\coffeeit-assessment\src\cities\cities.controller.ts:25:31)
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-execution-context.js:38:29
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-execution-context.js:46:28
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-proxy.js:9:17
cities.service.ts
#Delete(':id')
async deleteCity(id) {
const result = await this.cityModel.deleteOne({ _id: id }).exec();
if (!result) {
throw new NotFoundException('Could not find city.');
}
}
cities.controller.ts
#Delete(':id')
deleteCity(#Param() id: number) {
return this.citiesService.deleteCity(id);
}
city.model.ts
import * as mongoose from 'mongoose';
export const CitySchema = new mongoose.Schema({
name: String,
weather: mongoose.SchemaTypes.Mixed,
});
export interface City {
id: number;
name: string;
weather: mongoose.Schema.Types.Mixed;
}
city.entity.ts
import { BaseEntity, Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class City extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
weather: any;
}

cities.service.ts
import { ObjectId } from 'mongodb';
...
#Delete(':id')
async deleteCity(id) {
const cityId = new ObjectId(id); // Cast id to MongoDB Object id
const result = await this.cityModel.deleteOne({ _id: cityId }).exec();
if (!result) {
throw new NotFoundException('Could not find city.');
}
}

Related

Knex / Objection / PostgreSQL - Update of foreign key not working

I use Knex/Objection with PostgreSQL and I'm trying to update the foreign key but it returns null without any error.
Car model:
export class CarModel extends Model implements Car {
public static readonly tableName: string = 'cars';
public static readonly idColumn: string = 'car_id';
public static readonly jsonSchema: object = {
type: 'object',
properties: {
name: { type: 'string' },
carBrandId: { type: ['number', 'null'] },
lastUpdated: { type: ['string', 'null'], format: 'date-time' }
}
};
public static readonly relationMappings: RelationMappings = {
carBrandId: {
relation: Model.BelongsToOneRelation,
modelClass: 'brand.model',
join: {
from: 'brands.brand_id',
to: 'cars.fk_cars_brands'
}
}
};
public carId: number;
public name: string;
public carBrandId: number;
}
Creation of cars table:
import { Knex } from 'knex';
export function up(knex: Knex): Promise<boolean | void> {
return knex.schema.hasTable('cars').then(function(exists) {
if (!exists) {
return knex.schema.createTable('cars', (table) => {
table.specificType('car_id', 'INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY');
table.specificType('name', 'TEXT').notNullable();
table.specificType('car_brand_id', 'INTEGER').references('brand_id').inTable('brands').withKeyName('fk_cars_brands');
table.specificType('last_updated', 'TIMESTAMP');
});
}
})
}
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTableIfExists('cars');
}
And then I use patch method in my service to update car_brand_id value from default to new added brand:
Database.getConnection().then((connection) => {
return CarModel.query(connection)
.patch({
carBrandId: 1, // row with brandId=1 exists in table brands!
lastUpdated: new Date().toISOString()
})
.where('carId', 10)
.returning('*')
.first()
});
And it returns the row where lastUpdated was updated, but carBrandId is still null:
{ carId: 10, name: 'Ferrari', carBrandId: null, lastUpdated: '<some date>' }
I would appreciate any help!

Can not Query all users because of MongoDB id

I am coding a CRUD API built in TypeScript and TypeGoose.
I get an error saying,
CannotDetermineGraphQLTypeError: Cannot determine GraphQL output type for '_id' of 'User' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper output value?
I have a User entity.
import { Field, ObjectType } from 'type-graphql';
import { ObjectId } from 'mongodb';
import { prop as Property, getModelForClass } from '#typegoose/typegoose';
#ObjectType()
export class User {
#Field()
readonly _id: ObjectId;
#Field()
#Property({ required: true })
email: string;
#Field({ nullable: true })
#Property()
nickname?: string;
#Property({ required: true })
password: string;
constructor(email: string, password: string) {
this.email = email;
this.password = password;
}
}
export const UserModel = getModelForClass(User);
And this is how my query resolver looks like.
#Query(() => [User])
async users() {
const users = await UserModel.find();
console.log(users);
return users;
}
How can I solve this? It seems to be like TypeGraphQL doesn't understand what the MongoDB ID is?
Im not sure about this, but maybe ObjectId.toString() help you.
MongoDB doc about ObjectId.toString()

Type 'string' is not assignable to type 'Condition<UserObj>' when making mongoose query by ID

I have the following API route in Next:
import {NextApiRequest, NextApiResponse} from "next";
import dbConnect from "../../utils/dbConnect";
import {UserModel} from "../../models/user";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "GET") return res.status(405);
if (!req.query.id || Array.isArray(req.query.id)) return res.status(406).json({message: "No ID found in request"});
try {
await dbConnect();
const user = await UserModel.findOne({ _id: req.query.id });
if (!user) return res.status(404).json({message: "No user found"});
return res.status(200).json({data: user});
} catch (e) {
return res.status(500).json({message: e});
}
}
Typescript says that the line const user = await UserModel.findOne({ _id: req.query.id }); contains error Type 'string' is not assignable to type 'Condition<UserObj>'. Creating an ObjectId instead of a string (const user = await UserModel.findOne({ _id: mongoose.Types.ObjectId(req.query.id) });) throws the same error.
I've looked through the type files/docs but I'm struggling to figure out why this is invalid. Shouldn't querying by ID with a string or ObjectId be a valid condition object? Querying by other fields works fine.
Why is this invalid, and how should I fix it?
The proposed solution by #Tim is good and solves this punctual situation, but it doesn't get you to the root of the problem. What if you have to use the findOne method because you are going to use another field in the filter? For example:
You want to get the user with that id and that the deletedAt attribute is null.
const user = await UserModel.findOne({ _id: req.query.id, deletedAt: null});
You will get the same error cause the mistake is in the userModel definition. I guess your user class is basically as shown below:
import { ObjectId, Types } from 'mongoose';
#Schema({ versionKey: false, timestamps: true })
export class User {
#Field(() => ID, {name: 'id'})
readonly _id: ObjectId;
#Field(() => Date, {nullable: true, name: 'deleted_at'})
#Prop({type: Date, required: false, default: null})
deletedAt?: Date;
#Field()
#Prop({required: true, index: true})
name: string;
...
}
The problem is that you are directly accessing the Schema user when you should be accessing the model (repository pattern).
[SOLUTION]: Create the model or the repository for your user class, and use it to interact with your database.
In my case I just added the following lines:
import { ObjectId, Types, Document } from 'mongoose';
#Schema({ versionKey: false, timestamps: true })
export class User {
...
}
export type UserDocument = User & Document;
OR
import { ObjectId, Types, Document } from 'mongoose';
#Schema({ versionKey: false, timestamps: true })
export class User extends Document{
...
}
and in my service I instantiated an object of type model:
import { Model } from 'mongoose';
private userModel: Model<UserDocument>;
and then I was able to make the following method call:
...
await dbConnect();
const user = await UserModel.findOne({ _id: req.query.id });
if (!user) return res.status(404).json({message: "No user found"});
...
Use .findByID for id based queries.

Why DTOs are not throwing Validation error in nestjs?

I am using DTO in my code, and I am getting the response as expected but in code DTOs are not throwing error for example
export class CreateCatDto {
readonly name: string;
readonly age: number;
readonly breed: string;
}
In this name, age, the breed is a required field and each has their data type but while running on the postman when I am not passing all the required field or only one field into postman body I am not getting any errors like age is required if I have passed other two fields or I have given value of the parameter not according to data type like:- age : twenty five then also it should throw error but I am not getting.
So, This is class created for
import { ApiProperty } from '#nestjs/swagger';
export class Cat {
#ApiProperty({ example: 'Kitty', description: 'The name of the Cat' })
name: string;
#ApiProperty({ example: 1, description: 'The age of the Cat' })
age: number;
#ApiProperty({
example: 'Maine Coon',
description: 'The breed of the Cat',
})
breed: string;
}
This is controller in which I am importing class and Dto.
import { Body, Controller, Get, Param, Post } from '#nestjs/common';
import {
ApiBearerAuth,
ApiOperation,
ApiResponse,
ApiTags,
} from '#nestjs/swagger';
import { CatsService } from './cats.service';
import { Cat } from './classes/cat.class';
import { CreateCatDto } from './dto/create-cat.dto';
#ApiBearerAuth()
#ApiTags('cats')
#Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
#Post()
#ApiOperation({ summary: 'Create cat' })
#ApiResponse({ status: 403, description: 'Forbidden.' })
async create(#Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}
I don't know why you selected nestjs-swagger tag, DTO by itself will not validate inputs, maybe you need to use a ValidationPipe with the class-validator package as suggested on docs https://docs.nestjs.com/techniques/validation#validation
It's as simple as putting a decorator on your code now:
import { IsEmail, IsNotEmpty } from 'class-validator';
export class CreateCatDto {
#IsNotEmpty()
#IsString()
readonly name: string;
#IsNotEmpty()
#IsInt()
readonly age: number;
#IsNotEmpty()
readonly breed: string;
You can see all the items here: https://github.com/typestack/class-validator#validation-decorators
And if you want to sanitize the request body, you should use a serializer to help:
https://docs.nestjs.com/techniques/serialization#serialization
This will show or hide your DTO properties based on decorators of each field. You need to install class-transformer package.
import { Exclude } from 'class-transformer';
export class UserEntity {
id: number;
firstName: string;
lastName: string;
#Exclude()
password: string;
constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial);
}
}
It's important to remember that interceptors will run on your request and response.

Angular 2: Create objects from a class

Hello I'm wondering if it's possible to create a class where you implement an interface and from there you send the data get from .get service to create a new object. Something like this
import { Component, OnInit } from '#angular/core';
import { User} from '../interfaces/user';
import {UserService} from '../services/user.service';
import { UserClass } from '../classes/user-class'
#Component({
selector: 'up-pros',
templateUrl: './pros.component.html',
providers: [UserService]
})
export class ProsComponent implements OnInit {
public users :User[];
public term: string;
constructor(private _httpService: UserService) { }
ngOnInit() {
console.log(UserClass)
this.term= 'INSTRUCTOR';
this._httpService.searchUsers(this.term)
.subscribe(
data => {this.users = new UserClass(data), console.log(data)},
error => alert(error + ' Error Get')
);
}
}
where my UserClass code is something like next one
import { User } from '../interfaces/user';
import { Address } from "../interfaces/address";
export class UserClass implements User {
public id: number
public name: string
public password: string
public lastNameA: string
public lastNameB: string
public photo: string
public telephone: string
public email: string
public userType: string
public active: string
public score: number
public createdAt: string
public updatedAt: string
public Address: Address
constructor ( id: number,
password: string,
name: string,
lastNameA: string,
lastNameB: string,
photo: string,
telephone: string,
email: string,
userType: string,
active: string,
score: number,
createdAt: string,
updatedAt: string,
Address: Address) {
this.name = name
this.password = password
this.lastNameA = lastNameA
this.lastNameB = lastNameB
this.photo = photo
this.telephone = telephone
this.email = email
this.userType = userType
this.active = active
this.score = score
this.createdAt = createdAt
this.updatedAt = updatedAt
this.Address = Address
}
}
and by the last, the interface:
import { Address } from "./address"
export interface User {
name: string;
password: string;
lastNameA: string;
lastNameB: string;
photo: string;
telephone: string;
email: string;
userType: string;
active: string;
score: number;
createdAt: string;
updatedAt: string;
Address: Address;
}
Is this possible? because if I try to do this Im getting the next error at pros-component.ts:
Supplied parameters do not match any signature of call target.
[default] Checking finished with 1 errors
My service:
import {Injectable} from '#angular/core';
import {Http, Headers} from '#angular/http';
import 'rxjs/add/operator/map';
import { User } from '../interfaces/user';
#Injectable()
export class UserService {
url= 'http://localhostapi/users';
constructor(private _http: Http){}
getUsers(){
return this._http.get(this.url)
.map(res => res.json());
}
searchUsers(term : string ){
return this._http.get('http://localhostapi/listas?user='+term)
.map(res => res.json());
}
searchUser(term : string ){
return this._http.get('http://localhostapi/users/'+term)
.map(res => res.json());
}
postUsers(user: User){
var headers = new Headers ();
headers.append('Content-Type','application/json');
return this._http.post(this.url, user, {headers: headers})
.map(res => res.json());
}
updateUsers(user: User, term: string){
var headers = new Headers ();
headers.append('Content-Type','application/json');
return this._http.put(this.url+"/"+term, user, {headers: headers})
.map(res => res.json());
}
}
If the structure of data matches the list of UserClass, you can simply do
this._httpService.searchUsers(this.term)
.subscribe(
data => {
this.users = data as User[];
console.log(data)
},
error => alert(error + ' Error Get')
);