typeorm composition with type-graphql : need mixins? - composition

I'm trying to use TypeOrm composition together with type-graphql, but I can not find a way to declare the object types.
The goal is to have a final entity NewBudgetEntity composed with 3 entities : MyBaseEntity, BudgetHeaderEntity and BudgetHeaderEntity
I have run the code below and it works fine, but the graphql schema is not reflecting the table.
In the table, as expected, there are all the columns of the 3 entities.
But in type-graphql, because I've add the #ObjectType in the main entity, the fields header and corps are in the Graphql schema, whereas they are not in the table.
I think I need to use mixins to make sure that the final #ObjectType extends the 3 other #ObjectTypes related to the 3 other entities, but I'm quite confused how to do it. I read the doc of type-graphql about mixins, but can not get it work on my project.
Here is the code that is running well, but I don't want 'header' and 'corps' to be part of the graphql query
Ths for your help
import { Field, Float, ID, InputType, ObjectType } from "type-graphql";
import { BaseEntity, Column, Generated, Index, PrimaryColumn } from "typeorm";
export interface IIdentifier {
id: string;
}
export interface IBudgetHeader {
codeBudget: string;
natureBudget: string;
}
export interface IBudgetCorps {
montant: number;
nom: string;
isAffectable: boolean;
}
#ObjectType()
#InputType("myBaseEntityInputType")
export class MyBaseEntity extends BaseEntity implements IIdentifier {
#Field(() => ID, { nullable: false })
#PrimaryColumn({ type: "uuid" })
#Index()
#Generated("uuid")
id: string;
}
#ObjectType()
#InputType("BudgetHeaderEntityInputType")
export class BudgetHeaderEntity implements IBudgetHeader {
#Field({ nullable: false })
#Column({
type: "character varying",
length: 20,
nullable: false,
unique: true,
default: "",
})
codeBudget: string;
#Field({ nullable: true })
#Column({
type: "character varying",
length: 20,
nullable: true,
default: "",
})
natureBudget: string;
}
#ObjectType()
#InputType("BudgetCorpsEntityInputType")
export class BudgetCorpsEntity implements Partial<IBudgetCorps> {
#Field({ nullable: true })
#Column({
type: "character varying",
length: 250,
nullable: true,
default: "",
})
nom: string;
#Field(() => Boolean, { nullable: true })
#Column({
type: "bool",
default: false,
nullable: true,
})
isAffectable: boolean;
#Field(() => Float, { nullable: true })
#Column({
type: "float",
default: 0.0,
nullable: true,
})
montant: number;
}
#ObjectType()
#InputType("NewBudgetEntityInputType")
#Entity({
name: "newbudget",
})
export class NewBudgetEntity extends MyBaseEntity {
#Field(() => BudgetHeaderEntity)
header: BudgetHeaderEntity;
#Field(() => BudgetCorpsEntity)
corps: BudgetCorpsEntity;
}
#Resolver(NewBudgetEntity)
export default class NewBudgetEntityResolvers extends apiNewBudgetEntity {
#Query()
dummy(): string {
console.log("test");
return "test";
}
}
Example of query but I want to avoid 'header{} and corps{} and just having data {id codeBudget nom}

If you want to hide some property from being exposed in GraphQL schema, just don't put #Field decorator above the property. If the property should be mapped to db column, just use #Column decorator only.

Related

Multiple OneToOne relation with TypeOrm doesn't work

I currently using NestJS 8.4.7 and TypeORM 0.3.10
I want to make two (or more) OneToOne relations in my entity class based on an existing SQL Database
Here is my code :
// Article Entity
#Entity({ name: 'node' })
export class Article {
#PrimaryGeneratedColumn('increment')
public articleId: number
#OneToOne(() => ArticleBody)
#JoinColumn({ name: 'articleId', referencedColumnName: 'bodyId' })
public body: ArticleBody
#OneToOne(() => ArticleTitle)
#JoinColumn({ name: 'articleId', referencedColumnName: 'titleId' })
public title: ArticleTitle
}
// Body Entity
#Entity({ name: 'article_body' })
export class ArticleBody {
#PrimaryGeneratedColumn()
public bodyId: number
#Column({ nullable: false })
public bodyValue: string
}
// Title Entity
#Entity({ name: 'article_title' })
export class ArticleTitle {
#PrimaryGeneratedColumn()
public titleId: number
#Column({ nullable: false })
public titleValue: string
}
And I have this error:
sqlMessage: ERROR [ExceptionsHandler] Unknown column 'Article__Article_body.titleId' in 'on clause'
sql: SELECT
`Article`.`articleId` AS `Article_nid`,
`Article__Article_body`.`bodyValue` AS `Article__Article_body_bodyValue`,
`Article__Article_title`.`titleValue` AS `Article__Article_title_titleValue`
FROM `Article` `Article`
LEFT JOIN `article_body` `Article__Article_body` ON Article__Article_body.titleId=`Article`.`articleId`
LEFT JOIN `article_title` `Article__Article_title` ON `Article__Article_title`.`titleId`=`Article`.`articleId`
WHERE (`Article`.`articleId` = 1)
It looks like if the last JoincolumnOptions overwrites all previous JoincolumnOptions
// Article Entity
#Entity({ name: 'node' })
export class Article {
#PrimaryGeneratedColumn('increment')
public articleId: number;
#OneToOne(() => ArticleBody)
#JoinColumn({ name: 'articleBodyId' })
public body: ArticleBody;
#OneToOne(() => ArticleTitle)
#JoinColumn({ name: 'articleTitleId' })
public title: ArticleTitle;
}
// Body Entity
#Entity({ name: 'article_body' })
export class ArticleBody {
#PrimaryGeneratedColumn("increment")
public bodyId: number;
#Column({ name: 'body_value', nullable: false })
public bodyValue: string;
}
// Title Entity
#Entity({ name: 'article_title' })
export class ArticleTitle {
#PrimaryGeneratedColumn("increment")
public titleId: number;
#Column({ name: 'title_value', nullable: false })
public titleValue: string;
}
In Article Entity your joinColumn name is same that's why the error is giving

How to filter the find method for a field on its relations?

The structure of my application is that I have:
Contacts
Groups
Lanes (belong to groups)
GroupContacts (join table to assign a contact to a group and assign a lane)
My entities are:
lane.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
Unique,
OneToMany,
} from 'typeorm';
import { Group } from './group.entity';
import { GroupContact } from './groupcontact.entity';
#Entity('lanes')
#Unique('UQ_NAMES', ['group', 'name'])
export class Lane {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
sequence: number;
#Column({ nullable: false, default: 30 })
updateFrequencyDays: number;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
createdAt: Date;
#ManyToOne(
type => Group,
group => group.lanes,
)
group: Group;
#OneToMany(
type => GroupContact,
groupcontact => groupcontact.lane,
)
groupContacts: GroupContact[];
}
groupcontact.entity.ts
import { Contact } from '../contacts/contact.entity';
import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm';
import { Group } from './group.entity';
import { Lane } from './lane.entity';
#Entity('groups_contacts')
export class GroupContact {
#PrimaryColumn()
groupId: number;
#PrimaryColumn()
contactId: number;
#Column()
laneId: number;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
createdAt: Date;
#ManyToOne(
type => Group,
group => group.contactConnection,
)
#JoinColumn({ name: 'groupId' })
group: Group;
#ManyToOne(
type => Contact,
contact => contact.groupConnection,
)
#JoinColumn({ name: 'contactId' })
contact: Contact;
#ManyToOne(
type => Lane,
lane => lane.groupContacts,
)
#JoinColumn({ name: 'laneId' })
lane: Lane;
}
contact.entity.ts
import { Group } from 'src/groups/group.entity';
import { GroupContact } from '../groups/groupcontact.entity';
import {
Entity,
Column,
PrimaryGeneratedColumn,
JoinTable,
ManyToMany,
OneToMany,
} from 'typeorm';
#Entity('contacts')
export class Contact {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public firstName: string;
#Column()
public lastName: string;
#Column({
nullable: false,
default: () => 'CURRENT_TIMESTAMP',
type: 'timestamp',
})
public createdAt: Date;
#OneToMany(
type => GroupContact,
gc => gc.contact,
)
groupConnection: GroupContact[];
}
What I am trying to do is that I am trying to return all contacts that belong to a particular group. But when returning those contacts, I'd also like to return the groupConnection object.
Here is the code I am using:
async getContacts(groupId) {
const group = await this.findOneByIdOrThrow(groupId);
const contacts = await this.contactRepository.find({
//where: { groupConnection: { groupId: groupId } },
relations: ['groupConnection'],
});
return contacts;
}
So far, am I able to return what I wanted, which is a list of contacts together with their relations. But I have not found a way to filter those results to retrieve only the ones that belong to a particular groupId.
Where clause commented.
How do I filter for that?
I was able to fix it by using QueryBuilder
const contacts = await this.contactRepository
.createQueryBuilder('contacts')
.innerJoinAndSelect('contacts.groupConnection', 'groupConnection')
.where('groupConnection.groupId = :groupId', { groupId })
.getMany();

TypeORM throws an error after insert with the queryBuilder in Nest.js

I have an entity and service defined as follows.
user.entity.js
#Entity({ name: 'users' })
export class User {
#Column({
type: 'binary',
length: 16,
select: false,
primary: true,
default: () => `(UUID_TO_BIN(UUID(), 1))`,
})
id: string;
#Index()
#Column({ type: 'varchar', length: 225, nullable: false })
name: string;
#Column({ type: 'varchar', length: 15, unique: true, nullable: false })
MSISDN: string;
#Column({ type: 'varchar', length: 225, unique: true, nullable: false })
email_address: string;
#Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
created_at: Date;
#Column({ type: 'datetime', nullable: true, onUpdate: 'CURRENT_TIMESTAMP' })
updated_at: Date;
}
user.service.js
#Injectable()
export class UserService {
constructor(
#InjectRepository(User)
private usersRepository: Repository<User>,
) {}
create(user: DeepPartial<User>): Promise<User> {
return this.usersRepository
.createQueryBuilder()
.insert()
.values({ ...user})
.execute();
}
}
Whenever I attempt to create a user from the Postman client, the record is in fact inserted into the database but I get a 500 response from the postman client.
Postman response
{
"statusCode": 500,
"message": "Internal server error"
}
The CLI error looks like this:
[Nest] 9084 - 15/03/2021, 11:17:47 [ExceptionsHandler] Cannot update entity because entity id is not set in the entity. +331142ms
Error: Cannot update entity because entity id is not set in the entity.
at C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\query-builder\ReturningResultsEntityUpdator.js:128:39
at Array.map (<anonymous>)
at ReturningResultsEntityUpdator.<anonymous> (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\query-builder\ReturningResultsEntityUpdator.js:122:46)
at step (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\node_modules\tslib\tslib.js:141:27)
at Object.next (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\node_modules\tslib\tslib.js:122:57)
at C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\node_modules\tslib\tslib.js:115:75
at new Promise (<anonymous>)
at Object.__awaiter (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\node_modules\tslib\tslib.js:111:16)
at ReturningResultsEntityUpdator.insert (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\query-builder\ReturningResultsEntityUpdator.js:87:24)
at InsertQueryBuilder.<anonymous> (C:\Users\Mnengwa\workspace\kodisha\kodisha-web-api\node_modules\typeorm\query-builder\InsertQueryBuilder.js:105:76)
I had to specify the reload option as false in my save call (not insert) to make the error go away.
For insert you want to add an updateEntity(false) call to your chain before calling execute.
There's more context here:
https://github.com/typeorm/typeorm/issues/4651
Try defining the id attribute for the User entity as follows -
#PrimaryColumn()
#Generated('uuid')
id: string;

EntityColumnNotFound: No entity column "authors" was found. many to many relations on TypeORM and NestJS

I am trying to get the books per user from the get request
I have the following problem, it turns out that I am doing a many-to-many relationship but it indicates that the authors entity was not found, I have already searched the TypeORM documentation but I cannot find anything, this is my code:
book.controller.ts
#Controller('book')
export class BookController {
constructor(
private readonly _bookService: BookService
) { }
#Get('author/:authorId')
getBooksByAuthor(
#Param('authorId', ParseIntPipe) authorId: number,
): Promise<ReadBookDto[]> {
return this._bookService.getBooksByAuthor(authorId);
}
}
book.service.ts
#Injectable()
export class BookService {
constructor(
#InjectRepository(BookRepository)
private readonly _bookRepository: BookRepository,
#InjectRepository(UserRepository)
private readonly _userRepository: UserRepository
) { }
async getBooksByAuthor(authorId: number): Promise<ReadBookDto[]> {
const books: Book[] = await this._bookRepository.find({
// This is where I have the entity of authors, it should be noted that the entity of books if I have it
where: { status: status.ACTIVE, authors: In([authorId]) },
})
console.log(books)
return books.map(book => plainToClass(ReadBookDto, book));
}
}
book.entity.ts
import {
BaseEntity,
PrimaryGeneratedColumn,
Column,
ManyToMany,
Entity,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { User } from '../user/user.entity';
#Entity('books')
export class Book extends BaseEntity {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar', length: 100, nullable: false })
name: string;
#Column({ type: 'varchar', length: 500 })
description: string;
#ManyToMany(type => User, user => user.books, { eager: true, primary: true})
#JoinColumn()
authors: User[];
#Column({ type: 'varchar', default: 'ACTIVE', length: 8 })
status: string;
#CreateDateColumn({ type: 'timestamp', name: 'created_at' })
createdAt: Date;
#UpdateDateColumn({ type: 'timestamp', name: 'updated_at' })
updatedAt: Date;
}
This is the user entity where I make the many to many relation
user.entity.ts
#Entity('users')
export class User extends BaseEntity {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar', unique: true, length: 25, nullable: false })
username: string;
#Column({ type: 'varchar', nullable: false })
email: string;
#Column({ type: 'varchar', nullable: false })
password: string;
#OneToOne(type => UserDetails, {
cascade: true,
nullable: false,
eager: true,
})
#JoinColumn({ name: 'detail_id' })
details: UserDetails;
#ManyToMany(type => Book, book => book.authors)
#JoinTable({ name: 'user_books' })
books: Book[];
}
If you could help me find the error it would be very helpful
Thanks
You can use queryBuilder to get the books:
#Injectable()
export class BookService {
constructor(
#InjectRepository(BookRepository)
private readonly _bookRepository: BookRepository,
#InjectRepository(UserRepository)
private readonly _userRepository: UserRepository
) { }
async getBooksByAuthor(authorId: number): Promise<ReadBookDto[]> {
const books: Book[] = await this._bookRepository.createQueryBuilder('books')
.leftJoinAndSelect("books.authors", "users")
.where('books.status = :status',{status : status.ACTIVE})
.andWhere("users.id = :id ", { id: authorId })
.getMany();
console.log(books)
return books.map(book => plainToClass(ReadBookDto, book));
}
}

TypeORM getRepository.find() does not include Foreign Key Fields

I am trying to fetch all the columns included on my entity, but I only able to fetch the columns that does not have any relationship from the other entity.
I use this block of codes to fetch the all the rows to this repository.
private translationTextRepository = getRepository(TranslationText);
async all(request: Request, response: Response, next: NextFunction) {
return this.translationTextRepository.find();
}
And here's the entity for this repository.
#Entity('TranslationText')
export class TranslationText {
#PrimaryGeneratedColumn()
ID: number;
#Column()
CreatedBy: string;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
CreatedDate: Date;
#Column()
Status: boolean;
#Column({ nullable: true, default: null })
ModifiedBy: string;
#Column({ type: 'timestamp', nullable: true, default: null })
ModifiedDate: Date;
#Column()
Text: string;
#ManyToOne((type) => Locale, (locale) => locale.ID)
#JoinColumn({ name: 'LocaleID' })
LocaleID: Locale;
#ManyToOne((type) => TranslationTitle, (translationTitle) => translationTitle.ID)
#JoinColumn({ name: 'TranslationTitleID' })
TranslationTitleID: TranslationTitle;
}
But I was only able to fetch all the columns except the LocaleID and the TranslationTitleID.
How can I achieve this?
Check this document:
https://typeorm.io/#/relations-faq/how-to-use-relation-id-without-joining-relation
solution:
define new column:
#column()
LocaleID: number
rename old one to : Locale
But typeOrm cannot sync your table due to foreign key problem.
use eager option in #ManyToOne({eager: true})
The search result will contain relation Locale object, you can take id from it.
Can you try to specify the relations like that:
async all(request: Request, response: Response, next: NextFunction) {
return this.translationTextRepository.find({
relations:["LocaleID","TranslationTitleID"]
});
}
Because you have to make explicit that you want your relations on the query.