NestJS Prisma - Types of property entities incompatible, missing or not assigned - node.js

I'm trying to update my Postgres database using Prisma ORM in NestJS (Microservices architecture).
The code allows users to interact with invitation requests.
But I keep getting the error:
Argument of type 'Invitation' is not assignable to parameter of type 'Invitation & { work: Work; }'. Property 'work' is missing in type 'Invitation' but required in type '{ work: Work; }'.
The Full error message looks like this:
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type '(Without<InvitationUpdateInput, InvitationUncheckedUpdateInput> & InvitationUncheckedUpdateInput) | (Without<...> & InvitationUpdateInput)'.
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type 'Without<InvitationUncheckedUpdateInput, InvitationUpdateInput> & InvitationUpdateInput'.
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type 'Without<InvitationUncheckedUpdateInput, InvitationUpdateInput>'. Types of property 'workId' are incompatible. Type 'string' is not assignable to type 'never'.
How can I fix this?
Thanks in advance!
The Invitation Interface:
export default class Invitation {
id: string;
workId: string;
work: string;
co?: string;
status: RequestStatus;
createdAt: Date;
respondedAt?: Date;
owner?: string;
note?: string;
}
export enum RequestStatus {
PENDING = 'PENDING',
REJECTED = 'REJECTED',
ACCEPTED = 'ACCEPTED',
}
The prisma invitation schema:
model Invitation {
id String #id #db.Uuid
workId String #db.Uuid
work Work #relation(...)
status RequestStatus
co String #db.Uuid
owner String
note String
createdAt DateTime
respondedAt DateTime
}
The Updating mechanics in Postgres Persistence Infrastructure:
async update(invitation: Invitation): Promise<Invitation> {
const entity = await this.prismaService.invitation.update({
where: {
id: invitation.id,
},
data: {
...invitation,
status: RequestStatusEntity[invitation.status],
work: undefined,
},
include: {
work: true,
},
});
return this.toDomain(entity);
}
private toDomain(
entity: InvitationEntity & {
work: WorkEntity;
},
): Invitation {
return Object.setPrototypeOf(
{
...entity,
status: RequestStatus[entity.status],
},
Invitation.prototype,
);
}

You cannot use TypeScript enums in this case as Prisma uses string literals in the generated type.
I would suggest using Prisma's types directly instead of creating your own like this:
import { RequestStatus } from '#prisma/client'
export default class Invitation {
id: string;
workId: string;
work: string;
co?: string;
status: RequestStatus;
createdAt: Date;
respondedAt?: Date;
owner?: string;
note?: string;
}

Related

Unable to create TypeORM DTO in Nest.js with Foreign Key Column

DB Entity
#Entity()
export class Estimate {
#PrimaryGeneratedColumn()
estimateId: number;
#ManyToOne(() => Customer, (customer) => customer.customerId)
customer: Customer;
#ManyToOne(() => Organization, (organization) => organization.organizationId)
organization: Organization;
#ManyToMany(() => Item)
#JoinTable()
items: Item[];
#ManyToMany(() => Tax)
#JoinTable()
taxes: Tax[];
#Column({ type: 'varchar', length: 36 })
estimateNumber: string;
#Column({ type: 'varchar', length: 36 })
orderNumber: string;
#Column({ type: 'timestamptz' })
estimateDate: string;
#Column({ type: 'timestamptz' })
expiryDate: string;
#Column({ type: 'varchar', length: 75 })
salesPersonName: string;
#Column({ type: 'money' })
subTotal: number;
#Column({ type: 'varchar', length: 12 })
discountType: string;
... trimmed
}
My DTO
export class CreateEstimateDto {
customer: number; // error
organization: number; // error
items: number[]; // error
taxes: number[]; // error
estimateNumber: string;
orderNumber: string;
estimateDate: string;
expiryDate: string;
salesPersonName: string;
subTotal: number;
...trimmed
}
service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateEstimateDto } from './dto/create-estimate.dto';
import { UpdateEstimateDto } from './dto/update-estimate.dto';
import { Estimate } from './entities/estimate.entity';
#Injectable()
export class EstimatesService {
constructor(
#InjectRepository(Estimate)
private estimateRepository: Repository<Estimate>,
) {}
create(createEstimateDto: CreateEstimateDto) {
return this.estimateRepository.insert(createEstimateDto); // type mismatch
}
}
Getting error
Argument of type 'CreateEstimateDto' is not assignable to parameter of type 'QueryDeepPartialEntity<Estimate> | QueryDeepPartialEntity<Estimate>[]'.
Type 'CreateEstimateDto' is not assignable to type 'QueryDeepPartialEntity<Estimate>'.
Types of property 'customer' are incompatible.
Type 'number' is not assignable to type '(() => string) | QueryDeepPartialEntity<Customer>'.ts(2345)
The following columns are foreign key columns
customer, organization, items, taxes
Workaround 1:
I can make DTO field's type to any - that will come as {} object in swagger spec
What is actually happening here is that it's trying to convert integer to category type and error happens here. You can fetch foreign relations and then pass it through insert method. As I know there is no straight way of doing this in nest.
Edit
for example for your customer relation you can do this:
const customer = customerRepository.findOne(createEstimateDto.customer);
and then pass it to insert method:
return this.estimateRepository.insert(Object.assign(createEstimateDto, { customer }));

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);

How to change the interface key: value: string to [{ [key: string]: string }

I have interface
IUser {
name: string;
surname: string;
cityLocation: string;
}
the value will be
const user: IUser = {
"name": "John";
"surname": "Smith";
"cityLocation": "LA";
}
I have adapter, they have method which receive
function adapter(columns: { [key: string]: string }) {...}
when I try to do:
adapter(user);
I have error:
Argument of type 'IUser' is not assignable to parameter of type '{ [key: string]: string; }'.   Index signature is missing in type 'IUser'.
How can I fix it?
(funny, but when I dont use signature IUser for object user - adapter works...)
Hope for your help. Big Thx!
As the error says, Index signature is missing in type 'IUser' so add it:
interface IUser {
name: string;
surname: string;
cityLocation: string;
[key: string]: string;
}
Playground link

Issue with Date and Mongoose Typescript

I'm facing the issue with the Mongoose official document founded here.
import { Schema, Model, model } from 'mongoose';
export interface IUser {
name: string;
email: string;
avatar?: string;
created: Date;
}
const schema = new Schema<IUser, Model<IUser>, IUser>({
name: { type: String, required: true },
email: String,
avatar: String,
created: { type: Date, default: Date.now },
});
export const UserModel = model<IUser>('User', schema);
My problem is the created type in IUser is not the same as in schema and got the error:
Type '{ type: DateConstructor; default: () => number; }' is not assignable to type 'typeof SchemaType | Schema<any, any, undefined, unknown> | Schema<any, any, undefined, unknown>[] | readonly Schema<any, any, undefined, unknown>[] | Function[] | ... 6 more ... | undefined'.
Types of property 'type' are incompatible.
Type 'DateConstructor' is not assignable to type 'Date | typeof SchemaType | Schema<any, any, undefined, unknown> | undefined'.
Type 'DateConstructor' is missing the following properties from type 'typeof SchemaType': cast, checkRequired, set, getts(2322)
(property) created?: typeof SchemaType | Schema<any, any, undefined, unknown> | Schema<any, any, undefined, unknown>[] | readonly Schema<any, any, undefined, unknown>[] | Function[] | ... 6 more ... | undefined
Please let me know how to fix this.
Date.now() is a function which returns number. Instead of it, try using new Date() only. Also need to make changes in the type of createdAt to Number.
In the given doc link, createdAt field type is number but here you have written Date.
interface User {
name: string;
email: string;
avatar?: string;
createdAt: number;
}
OR
createdAt and updatedAt are timestamps which can be used directly without specifying in the schema.
const schema = new Schema<IUser, Model<IUser>, IUser>({
name: { type: String, required: true },
email: String,
avatar: String
},{
timestamps: true
});
I think this is working
import { Schema,Document } from 'mongoose';
export interface IUser {
name: string;
email: string;
avatar?: string;
created: Date;
}
const schema = new Schema<IUser>({
name: { type: String, required: true },
email: String,
avatar: String,
created: { type: Date, default: Date.now },
});
export const UserModel = model<IUser>('User', schema);

No overload matches this call error with typescript, nodejs and mongoose

I have a service in an application that was previously working but all of a sudden I am getting this typescript error and I am unsure how to go about solving it. Please see my model file:
import mongoose from "mongoose";
import { AccessType, TicketType } from "./types";
interface TicketAttrs {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketDoc extends mongoose.Document {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketModel extends mongoose.Model<TicketDoc> {
build(attrs: TicketAttrs): TicketDoc;
}
const ticketSchema = new mongoose.Schema(
{
name: {
type: String,
},
type: {
type: String,
enum: Object.values(TicketType),
},
access: {
type: String,
enum: Object.values(AccessType),
},
price: {
type: Number,
},
uniqueId: {
type: String,
},
quantity: {
type: Number,
},
creator: {
type: String,
},
},
{
timestamps: true,
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
ticketSchema.statics.build = (attrs: TicketAttrs) => {
return new Ticket(attrs);
};
const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
export { Ticket };
I get an error on ticketSchema on this line const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
The Error Message is:-
const ticketSchema: mongoose.Schema<mongoose.Document<any>, mongoose.Model<mongoose.Document<any>>>
No overload matches this call.
Overload 1 of 6, '(name: string, schema?: Schema<TicketDoc, TicketModel> | undefined, collection?: string | undefined, skipInit?: boolean | undefined): TicketModel', gave the following error.
Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of type 'Schema<TicketDoc, TicketModel>'.
Types of property 'methods' are incompatible.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ....' is not assignable to type '{ name: string; type: TicketType; access: AccessType; uniqueId: string; price: number; quantity: number; creator: string; _id?: any; __v?: number | undefined; $ignore: (path: string) => void; ... 50 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ...; }'.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] |
I am also getting an error saying that there is no build function, I don't understand what broke the code and it was working without any changes.

Resources