I'm working on a ToDo list application in NodeJS, Koa, and GraphQL.
I wrote an update card mutation but when I run the query to update I get the following error:
Cannot perform update query because update values are not defined. Call "qb.set(...)" method to specify updated values.
The mutation:
import { getRepository } from 'typeorm';
import { Card } from '../../entities/card';
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, patch);
return {
...card,
...patch,
};
},
};
I would like to know what I'm doing wrong and if something more is needed it please notify me so I will edit the question accordingly
card entity:
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
#Entity('cards')
export class Card {
#PrimaryGeneratedColumn('uuid')
id: string;
#CreateDateColumn()
created_at: Date;
#UpdateDateColumn()
updated_at: Date;
#Column('text')
title: string;
#Column('text', {
nullable: true,
})
description: string;
#Column('boolean', {
default: 'false',
})
done: boolean;
}
You need to spread the update Object.
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, {...patch}); // here
return {
...card,
...patch,
};
},
};
The issue was when I was calling the updateMutation, it was creating the patch object of anonymous type. So it just needed to be clean before going to the DB engine
I resolved my issues by adding the following code:
{ ...patch }
Inside the next script:
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, { ...patch }); // Added here
return {
...card,
...patch,
};
},
};
In this way, I was able to update my card.
https://github.com/typeorm/typeorm/blob/master/docs/update-query-builder.md
As an error Call qb.set() , typeorm query builder are different with other orm
await getRepository().createQueryBuilder().update(Card).set(patch)
.where("id = :id", { id })
.execute();
some how patch object may stringify [object], so you can spread it like set({...patch})
I have had this error before with my update query is nestjs & graqhql
Cannot perform update query because update values are not defined
I have fixed it by using the save() function from the repository on the same id, so I have changed from this
async update(
id: number,
updateEmployeeInput: UpdateEmployeeInput,
): Promise<Employee> {
await this.employeeRepository.update(id, updateEmployeeInput);
return this.employeeRepository.findOneOrFail(id);
}
to this
async update(
id: number,
updateEmployeeInput: UpdateEmployeeInput,
): Promise<Employee> {
await this.employeeRepository.save(updateEmployeeInput)
return this.employeeRepository.findOneOrFail(id);
}
Related
So my knowledge of NodeJS and MongoDD are non-existent (just need to do a small code update for a friend) and I'm stuck.
Need to update a single document inside a collection via a unique id but can't seem to do it.
Here's the Model (I've trimmed it down and cut out all unnecessary data). I'm trying to update the field notes inside a transaction.
In short each entry in the given (an Agent) table will have a collection of multiple Transactions & Documents. I need to update a specific Transaction with the unique _id that is auto generated.
import { Schema, model } from 'mongoose';
interface Transaction {
first_name: string;
last_name: string;
type: string;
notes: string;
}
interface Agent {
org_id: number;
transactions: Array<Transaction>;
documents: Array<string>;
}
const transactionSchema = new Schema<Transaction>({
first_name: { type: String },
last_name: { type: String },
type: { type: String },
notes: String,
});
const transactionsSchema = new Schema<Agent>({
org_id: { type: Number },
transactions: [transactionSchema],
documents: [documentTypesSchema],
});
const AgentTransaction = model<Agent>(
'agent_transaction_table',
transactionsSchema
);
export default AgentTransaction;
Here's what I tried but didn't work (obviously), again I've trimmed out all unnecessary data. Just to clarify, the endpoint itself works, but the DB update does not.
import AgentTransaction from '../models/transaction'; // the above model
transaction.put('/notes', async (req, res) => {
const { org_id, transaction_id, notes } = req.body;
try {
const notesResult = await AgentTransaction.updateOne({
'transactions._id': transaction_id,
}, {
$set: {
'notes': notes
},
});
res
.status(200)
.json({ message: 'Updated', success: true, notesResult });
} catch (error) {
res.status(400).send(error);
}
});
So I figured it out. Maybe it'll help someone else as well.
const notesResult = await AgentTransaction.updateOne({
'transactions._id': { $in: [trunc2] },
}, {
$set: {
'transactions.$.notes': notes
},
});
The main issue was that the payload object needed to target the collection folder + the wildcard + the field, not just only the field.
i'm trying to split up my single-files mongoose schemas with statics and methods.
(I found this tutorial for splitting: https://medium.com/swlh/using-typescript-with-mongodb-393caf7adfef ) I'm new to typescript but love the benefits it gives while coding.
I've splitted my user.ts into:
user.schema.ts
user.model.ts
user.types.ts
user.statics.ts
user.methods.ts
When i change this lines in my schema file:
UserSchema.statics.findUserForSignIn = async function findUserForSignIn(
email: string
): Promise<IUserDocument | null> {
const user = await this.findOne({ email: email });
if (!user) {
return user;
} else {
return user;
}
}
to UserSchema.statics.findUserForSignIn = findUserForSignIn;
and copy the Function findUserForSignIn to user.statics.ts, Typescript says "'this' implicitly has type 'any' because it does not have a type annotation" and "An outer value of 'this' is shadowed by this container."
So, how to add this properly? If i add this to findUserForSignIn with IUserModel as Type, add null to Promise return type it would nearly work:
export async function findUserForSignIn(
this: IUserModel,
email: string
): Promise<IUserDocument | null> {
const user = await this.findOne({ "person.email": email });
return user;
}
And if i add this to receiving function parameters: users gets to type IUserDocument, before it was any. I think its nice to have typeclear, not just any.
But: in user.schema.ts the UserSchema.statics.findUserForSignIn gets a red line from typescript. Type can not be assigned to other type. The signature of this is not identical.
If i change the type of this to any, all is okay. But the return is not longer from type IUserDocument. Mabye its okay if i get over an aggregation pipeline and only set the Promise-Return-Type. But that this: any gets hinted in yellow by typescript.
And, another question: if i pass this as first and email as second parameter, why is only one parameter required?
Anyone has an "how to" for me? Or can explain what i've done wrong? Or what is the best way? Or is it not possible to split statics and methods in seperate files from schema?
Original files:
user.schema.ts
import { Schema } from "mongoose";
import { PersonSchema } from "./person.schema";
import { findUserForSignIn } from "./user.statics";
import { IUserDocument } from "./user.types";
const UserSchema = new Schema<IUserDocument>({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
});
UserSchema.statics.findUserForSignIn = findUserForSignIn;
export default UserSchema;
user.types.ts
import { Document, Model } from "mongoose";
import { IPerson } from "./person.types";
export interface IUser {
firstname: string;
lastname: string;
email: string;
}
export interface IUserDocument extends IUser, Document {}
export interface IUserModel extends Model<IUserDocument> {
findUserForSignIn: (email: string) => Promise<IUserDocument>;
}
user.model.ts
import { model } from "mongoose";
import UserSchema from "./user.schema";
import { IUserDocument, IUserModel } from "./user.types";
const User = model<IUserDocument>("User", UserSchema) as IUserModel;
export default User;
user.statics.ts
import { IUserDocument } from "./user.types";
export async function findUserForSignIn(
email: string
): Promise<IUserDocument | null> {
const user = await this.findOne({ email: email });
if (!user) {
return user;
} else {
return user;
}
}
The only way seems to change the user.statics.ts
export async function findUserForSignIn(
this: Model<IUserDocument>,
email: string
): Promise<IUserDocument | null> {
console.log("E-Mail", email);
const user = await this.findOne({ email: email });
return user;
}
this has to be of type Model
Then code seems to be okay.
I'm posting here because I have been stuck on a problem for few hours now.
I am creating an API using Nest JS 8 and MongoDB, and I test it using Postman. When I want to execute a POST request (http://localhost:3000?nom=Antoine) to insert an object in my database, I have an error (500 : Internal server error) message that says "Client validation failed: nom: Path 'nom' is required (nom is the name of my object's property).
I've wandered every topic about this kind of issue, tried to upgrade my version of Nest, to use a middleware, to make sure the right version of every depedency was installed.
I don't want to remove the "required: true" property because i think it is necessary. I tried to set it to "false", which enabled me to insert the object in the database but without my property 'nom' (name in french).
If you guys have any help, here's my schema :
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type ClientDocument = Client & Document;
#Schema()
export class Client {
#Prop({ required: true })
nom: string;
}
export const ClientSchema = SchemaFactory.createForClass(Client);
And here is my controller :
import { Body, Controller, Delete, Get, Param, Post, Put} from '#nestjs/common';
import { ClientService } from './client.service';
import { ClientDto } from './dto/client.dto';
import { CreateClientDto } from './dto/create-client.dto';
import { UpdateClientDto } from './dto/update-client.dto';
#Controller('/client')
export class ClientController {
constructor(private readonly clientService: ClientService) {}
#Get()
async index(){
return await this.clientService.findAll();
}
#Get(':id')
async find(#Param('id') id: string) {
return await this.clientService.findOne(id);
}
#Post()
async create(#Body() createClientDto: CreateClientDto) {
console.log(createClientDto);
return await this.clientService.create(createClientDto);
}
#Put(':id')
async update(#Param('id') id: string, #Body() updateClientDto: ClientDto) {
return await this.clientService.update(id, updateClientDto);
}
#Delete(':id')
async delete(#Param('id') id: string) {
return await this.clientService.delete(id);
}
}
Thanks for looking
I found the solution (i still don't know why it works this way tho).
In my client.service.ts, i updated the create function from this :
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({createClientDto}).save();
}
To this
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({
...createClientDto,
createdAt: new Date(),
}).save();
}
Thanks for taking the time to answer, I hope this will help
I created a NestJS and used TypeORM for the RDBMS(I used postgres in my project).
Post is an #Entity class, PostRepository is a Repository class for Post.
I was trying to create OnModuleInit service to initialize some data.
#Injectable()
export class PostsDataInitializer implements OnModuleInit {
private data: Post[] = [
{
title: 'Generate a NestJS project',
content: 'content',
},
{
title: 'Create GrapQL APIs',
content: 'content',
},
{
title: 'Connect to Postgres via TypeORM',
content: 'content',
},
];
constructor(private readonly postRepository: PostRepository) {}
async onModuleInit(): Promise<void> {
await this.postRepository.manager.transaction(async (manager) => {
// NOTE: you must perform all database operations using the given manager instance
// it's a special instance of EntityManager working with this transaction
// and don't forget to await things here
await manager.delete(Post, {});
console.log('deleted: {} ');
this.data.forEach(async (d) => await manager.save(d as Post));
const savedPosts = await manager.find<Post>(Post);
savedPosts.forEach((p) => {
console.log('saved: {}', p);
});
});
}
}
When starting up the application, I got the following error.
CannotDetermineEntityError: Cannot save, given value must be instance of entity class, instead object literal is given. Or you must specify an entity target to method call.
But the above save was accepting an instance of Post.
I think it is pretty much what the error says. You cannot pass literal objects to .save
private data = [
{
title: 'Generate a NestJS project',
content: 'content',
},
{
title: 'Create GrapQL APIs',
content: 'content',
},
{
title: 'Connect to Postgres via TypeORM',
content: 'content',
},
].map(data => {
const post = new Post();
Object.assign(post, data);
return post;
})
the above might solve this.
You can alternatively specify the target type as per the error message like so:
await manager.save(Post, d))
From the documentation of save() I see there is such a method as:
save<Entity, T extends DeepPartial<Entity>>(targetOrEntity: EntityTarget<Entity>, entity: T, options?: SaveOptions): Promise<T & Entity>;
nest version 9.1.8
I'm unable to perform any kind of upsert or create within Sequelize (v: 6.9.0, PostGres dialect).
Using out-of-the-box id as PK, with a unique constraint on the name field. I've disabled timestamps because I don't need them, and upsert was complaining about them. I've tried manually defining the PK id, and allowing Sequelize to magically create it. Here's the current definition:
const schema = {
name: {
unique: true,
allowNull: false,
type: DataTypes.STRING,
}
};
class Pet extends Model { }
Pet.define = () => Pet.init(schema, { sequelize }, { timestamps: false });
Pet.buildCreate = (params) => new Promise((resolve, reject) => {
let options = {
defaults: params
, where: {
name: params.name
}
, returning: true
}
Pet.upsert(options)
.then((instance) => {
resolve(instance);
})
.catch(e => {
// message:'Cannot read property 'createdAt' of undefined'
console.log(`ERROR: ${e.message || e}`);
reject(e);
});
});
module.exports = Pet;
Upsert code:
// handled in separate async method, including here for clarity
sequelize.sync();
// later in code, after db sync
Pet.buildCreate({ name: 'Fido' });
In debugging, the options appear correct:
{
defaults: {
name: 'Fido'
},
returning:true,
where: {
name: 'Fido'
}
}
I've also tried findOrCreate and findCreateFind, they all return errors with variations of Cannot convert undefined or null to object.
I've tried including id: null with the params, exact same results.
The only way I've succeeded is by providing PK in the params, but that is clearly not scalable.
How can I upsert a Model instance without providing a PK id in params?
class Pet extends Model { }
//...you might have the id for the pet from other sources..call it petId
const aPet = Pet.findCreateFind({where: {id: petId}});
aPet.attribute1 = 'xyz';
aPet.attribute2 = 42;
aPet.save();