I have multiple connections using typeorm getConnections as follows
await createConnections([
{
name: 'main',
type: 'mysql',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASS,
database: process.env.DB,
logging: true,
entities: [Property],
},
{
name: 'favourites',
type: 'postgres',
url: process.env.DATABASE_URL,
logging: false,
synchronize: true,
migrations: [path.join(__dirname, './migrations/*')],
entities: [User],
},
]);
In my Property entity as defined in the 1st connection under the name main I have the following, stating the database the entity is to use.
import { ObjectType, Field } from 'type-graphql';
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm';
#ObjectType()
#Entity({ database: 'main', name: 'property' })
class Property extends BaseEntity {
#Field()
#PrimaryGeneratedColumn()
id!: number;
#Field(() => String)
#Column({ name: 'account_number' })
accountNumber!: string;
}
export default Property;
However, in a GraphQL resolver, when I try
await Property.findOne(1234)
I get the following error
ConnectionNotFoundError: Connection \"default\" was not found.
at ConnectionNotFoundError.TypeORMError [as constructor]
However, when I remove the name property in the first connection in createConnections and remove the database property in the Property Entity constructor, it works.
So, how do I get this to work on a named connection?
Related
I'm struggling in returning only selected fields in my TypeORM find request.
Assuming the following request
const data = await AppDataSource.manager.find(User, {
select: {
id: true,
hash: true,
firstname: true,
lastname: false,
},
take: 10, // Just here to shrink dataset
});
The script works pretty well excepted that it return every field of my model, with default value initialized.
[
User {
prefix: 'usr',
hash: 'usr_835b0ad2-XXXXXX',
email: undefined,
accountValidated: false,
role: 'free',
myKeyOne: true,
myKeyTwo: false,
gender: 'unspecified',
lastConnexion: 2023-01-19T10:11:02.733Z,
pendingDeletion: false,
deletionDate: undefined,
firstname: 'Clément',
lastname: undefined,
password: undefined,
facebookId: undefined,
googleId: undefined,
id: 158
},
...
]
Of course, it's not usable as it, because I have extensive relations, and thus the payload would be extremely heavy.
Are you aware of a method / a way to remove all unnecessary fields ?
i.e. I'm expecting
[
User {
id: 124,
hash: 'urs_XXXX',
firstname: 'Clément',
},
...
]
In older versions of typeorm I think you need to select with an array of strings, try:
select: ["id", "hash", "firstname"],
See this older version of the docs: https://github.com/typeorm/typeorm/blob/bc60dd559ba42af083ddea17f01205c78c83c7e0/docs/find-options.md
After hours of researches I've finally found out why it behaved like this.
TypeORM relies on class definitions and typescript so...
if you have typescript default values OR if you have rewrite your constructor, all the "default" properties are injected.
Assuming a User model
❌ You should not do
#Entity({ name: 'users' })
class User {
#Column()
firstname?: string;
#Column({ nullable: true })
lastname?: string;
#Column({ unique: true, nullable: false })
email!: string;
#Column({ name: 'account_validated', nullable: false})
accountValidated?: boolean = false
//Your other fields...
}
✅ You should do
#Entity({ name: 'users' })
class User {
#Column()
firstname?: string;
#Column({ nullable: true })
lastname?: string;
#Column({ unique: true, nullable: false })
email!: string;
// Use default argument of the decorator
#Column({ name: 'account_validated', nullable: false, default: false})
accountValidated?: boolean
//Your other fields...
}
And if you need in some way to init a default, then create a public static method which return the Entity instead of using the constructor.
#Entity({ name: 'users' })
class User {
//...fields
public static init(...params): User {
let _user = new User()
//...populate your object
return _user
}
}
I have a few entities created on TypeORM and I want to upsert an array of data with all the entities. Here I have the entities:
#Entity({name: "__sales_rep"})
export class SalesRep {
#PrimaryColumn()
ldap: string;
#Column("text")
name: string
#OneToMany(() => ParentCompany, (parent_company) => parent_company.sales_rep, { cascade: ['insert', 'update'] })
parent_companies: ParentCompany[]
}
#Entity({name: "__parent_company"})
export class ParentCompany {
#PrimaryColumn()
id: number;
#Column("text")
name: string
#OneToMany(() => Advertiser, (advertiser) => advertiser.parent_company, { cascade: ['insert', 'update'] })
advertisers: Advertiser[]
#ManyToOne(() => SalesRep, (sales_rep) => sales_rep.parent_companies)
sales_rep: SalesRep
}
#Entity({name: "advertiser"})
export class Advertiser {
#PrimaryColumn()
id: number;
#Column("text")
name: string
#ManyToOne(() => ParentCompany, (parent_company) => parent_company.advertisers)
parent_company: ParentCompany
}
And here is how I am trying to insert the data as cascading the data. I believe the problem is that when I insert two advertisers with the same parent_company for example the constraints of the foreign key aren't allowing me to make the entire insertion.
async function loadData(data) {
console.log("Beggning data insertion");
try{
const insertData = data.rows.map((row) => {
const currentSalesRep ? {
ldap: row.ldap,
name: row.full_name
},
currentParentCompany = {
id: row.parent_company_id,
name: row.parent_company_name,
sales_rep: currentSalesRep
};
return {
id: row.advertiser_id,
name: row.advertiser_name,
parent_company: currentParentCompany
}
})
salesRepRepository
.upsert(insertData, ['id']);
typeorm
}
catch(e){
logger.error(e)
throw e;
}
}
I'm creating a structure where I have Repositories, Contributions and Users.
A repository has multiple contributions done by different users. It is important that I know the count property on the contribution.
But when I create a new Repository entity without connections I get weird behaviour.
Example Code
My index.ts:
import "reflect-metadata";
import {createConnection, getRepository} from "typeorm";
import {Repository} from "./entity/Repository";
createConnection().then(async connection => {
const newRepo = new Repository();
newRepo.name = 'test';
newRepo.description = 'no contributions made';
await connection.manager.save(newRepo);
const RepoRepository = getRepository(Repository);
const repos = await RepoRepository.find();
console.log(repos);
console.log(repos[0].contributions);
await connection.manager.save(repos);
connection.close();
}).catch(error => console.log(error));
my console output:
repos [ Repository {
name: 'test',
description: 'no contributions made',
contributions: [ [Contribution] ] } ]
contributions [ Contribution { count: null, user: null } ]
which is weird because I never created any contributions.
And the connection.manager.save(repos); call errors out on:
{ QueryFailedError: ER_BAD_NULL_ERROR: Column 'count' cannot be null
at new QueryFailedError (D:\GitHub\Issue\src\error\QueryFailedError.ts:9:9)
at Query.<anonymous> (D:\GitHub\Issue\src\driver\mysql\MysqlQueryRunner.ts:164:37)
at Query.<anonymous> (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:502:10)
at Query._callback (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:468:16)
at Query.Sequence.end (D:\GitHub\Issue\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query.ErrorPacket (D:\GitHub\Issue\node_modules\mysql\lib\protocol\sequences\Query.js:90:8)
at Protocol._parsePacket (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Protocol.js:278:23)
at Parser.write (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Parser.js:76:12)
at Protocol.write (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Protocol.js:38:16)
at Socket.<anonymous> (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:91:28)
message: 'ER_BAD_NULL_ERROR: Column \'count\' cannot be null',
code: 'ER_BAD_NULL_ERROR',
errno: 1048,
sqlMessage: 'Column \'count\' cannot be null',
sqlState: '23000',
index: 0,
sql:
'INSERT INTO `contribution`(`count`, `repositoryName`, `employeeGithub`) VALUES (NULL, \'test\', NULL)',
name: 'QueryFailedError',
query:
'INSERT INTO `contribution`(`count`, `repositoryName`, `employeeGithub`) VALUES (?, ?, ?)',
parameters: [ null, 'test', null ] }
My Entities:
#Entity()
export class Repository {
#PrimaryColumn()
name: string;
#Column("text")
description: string;
#OneToMany(type => Contribution, contribution => contribution.repository, {eager: true, cascade: true})
contributions: Contribution[];
}
#Entity()
export class Contribution{
#ManyToOne(type => Repository, repository => repository.contributions, {primary: true})
repository: Repository;
#ManyToOne(type => User, user => user.contributions, {eager: true, cascade: true, primary: true})
#JoinTable()
user: User;
#Column()
count: number;
}
#Entity()
export class User{
#PrimaryColumn()
github: string;
#OneToMany(type => Contribution, contribution => contribution.user)
contributions: Contribution[];
}
What I tried:
Removing the eager on the ManyToOne from Contribution to User fixes the error and I correctly get an empty contributions array. But it isn't really a solution for the repositories that do have contributions.
The main question is: Am I doing it wrong or is this not something that works in typeorm?
I have 2 following entities with Many To Many relationship
User entity (relationship owned by the User entity)
import { Entity, Column, PrimaryGeneratedColumn, UpdateDateColumn, ManyToMany, JoinTable, CreateDateColumn } from 'typeorm';
import { Role } from './role.schema';
#Entity('Users')
export class User {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Email',
length: 100,
unique: true
})
email: string;
#Column({
name: 'FirstName',
length: 30
})
firstName: string;
#Column({
name: 'LastName',
length: 30
})
lastName: string;
#ManyToMany(type => Role, { eager: true })
#JoinTable({
name: 'UserRoles',
joinColumns: [
{ name: 'UserId' }
],
inverseJoinColumns: [
{ name: 'RoleId' }
]
})
roles: Role[];
}
Role entity (with two existing roles: Admin and User)
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import { User } from './user.schema';
#Entity("Roles")
export class Role {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({
name: 'Name',
length: 50,
unique: true
})
name: string;
#ManyToMany(type => User, user => user.roles)
users: User[];
}
the design of application is such that Role name is sent in request in the form of an array e.g. ['Admin', 'User']
Now while inserting a User,
currently I first retrieve Role object from database based on the role name array received in request (to assign a desired role),
then assign it to the User object (roles property) and
then finally call save method on User object to insert the record in User table.
Snippet:
createConnection(connectionConfig as ConnectionOptions).then(async connect => {
let userRepo = connect.getRepository(User);
let roleRepo = connect.getRepository(Role);
let roles = ['Admin', 'User'];
let user = userRepo.create();
return roleRepo.createQueryBuilder('role').where('role.name IN (:roleNames)', { roleNames: roles }).getMany().then((roles) => {
user.email = 'test1#test.test';
user.firstName = 'TestFName';
user.lastName = 'TestLName';
user.roles = roles;
return userRepo.save(user)
})
}).catch(error => {
console.log(error);
});
This results in many database calls. It would be great if some one can enlighten me with smarter and more elegant way ( using fluent QueryBuilder to achieve above result)
Is it possible to use connection class as provide like here?
import { Connection, createConnection } from 'typeorm';
export const databaseProviders = [{
provide: Connection,
useFactory: async () => await createConnection({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'testo',
entities: [
__dirname + '/../**/*.entity{.ts,.js}',
],
logging: true,
synchronize: true,
}),
}];
To make imports work like:
constructor(
private connection: Connection,
) {
this.repository = connection.getRepository(Project);
}
In that case nestjs can't find dependency. I guess the problem is in typeorm, it is compiled to plain es5 function. But maybe there a solution for this?
repository to reproduce
UPDATE:
I found acceptable solution nestjs typeorm module, but don't understand why my Connection class is not worked, but it works well with strings. Hope #kamil-myśliwiec will help to understand.
modules: [
TypeOrmModule.forRoot(
[
Build,
Project,
],
{
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'testo',
entities: [
Build,
],
logging: true,
synchronize: true,
}),
],
// And the n inject like this by entity name
#InjectRepository(Build) private repository: Repository<Build>,