Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Repositories with Separate Entity Definitions #3932

Open
3 tasks done
christhekeele opened this issue Apr 2, 2019 · 7 comments
Open
3 tasks done

Custom Repositories with Separate Entity Definitions #3932

christhekeele opened this issue Apr 2, 2019 · 7 comments

Comments

@christhekeele
Copy link

christhekeele commented Apr 2, 2019

Questionnaire

Issue type:

  • documentation issue

Database system/driver:

  • postgres

TypeORM version:

  • latest

Synopsis

Hi! I'm struggling to find the way get a repository instance while using custom repositories alongside separate entity definitions.

The custom repository docs rely on getting a repo by repo class:

import {getCustomRepository} from "typeorm";
import {UserRepository} from "./repository/UserRepository";

const userRepository = getCustomRepository(UserRepository);

The separate entity definitions have you getting a repo by entity schema instance:

import {getCustomRepository} from "typeorm";
import {UserRecord, userSchema} from "./user"

const userRepository = getRepository<UserRecord>(userSchema);

It's not clear what I have to do to get a custom repository for an entity schema, though. No permutations seem to work.

Logically it seems I need to be able to instrument the custom UserRepository with the userSchema I want it to use but there is no API to do so. The EntityRepository decorator expects an Entity decorated class, I can't give it my schema like @EntityRepository(userSchema).

Reproduction

Self-contained attempt to re-create my setup:

import { EntitySchema, EntityRepository, AbstractRepository, createConnection, Connection} from "typeorm";

class UserRecord {
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

const userSchema = new EntitySchema<UserRecord>({
  name: "user",
  columns: {
    name: { type: String, primary: true }
  }
});

@EntityRepository(UserRecord) // Doesn't help
class UserRepository extends AbstractRepository<UserRecord> {
  findByName(name: string) {
    return this.repository.findOne({ name });
  }
}

createConnection({
  type: "postgres",
  url: process.env["DATABASE_URI"],
  // entities: [UserRecord] // Doesn't help
  // entities: [userSchema] // Doesn't help
}).then((conn: Connection) => {

  // const repo = conn.getCustomRepository(UserRepository)
  // UnhandledPromiseRejectionWarning: EntityMetadataNotFound: No metadata for "UserRecord" was found.

  // const repo = conn.getRepository<UserRecord>(userSchema)
  // UnhandledPromiseRejectionWarning: RepositoryNotFoundError: No repository for "UserRecord" was found. Looks like this entity is not registered in current "default" connection?

  // const repo = conn.getRepository<UserRecord>(UserRecord)
  // UnhandledPromiseRejectionWarning: RepositoryNotFoundError: No repository for "UserRecord" was found. Looks like this entity is not registered in current "default" connection?

  repo.findByName("Scooby Doo").then((user?: UserRecord) => {
    if (user) {
      console.log(`Found you, ${user.name}!`)
    } else {
      console.log(`Scooby Doo, where are you?`)
    }
  });
});
@christhekeele
Copy link
Author

christhekeele commented Apr 3, 2019

I'm currently super deep into the metadata code, for which there is little documentation and no API for custom construction. As far as I can see, I don't think this setup can work without major monkey-patching of some private code. But I will keep trying things.

@christhekeele
Copy link
Author

I'm pretty baffled. I think my entire setup described above was a red herring; I can't event get the Hello World working and I'm not sure what I'm doing wrong.

So I guess this is user error, but I'd love any insight into why this trivial case isn't working, seems right according to the docs:

import {
  Connection,
  createConnection,
  Entity,
  getRepository,
  PrimaryColumn,
} from "typeorm";

@Entity()
export class UserRecord {
  @PrimaryColumn("text")
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

createConnection({
  type: "postgres",
  url: process.env["DATABASE_URI"],
  entities: [UserRecord]
}).then((_conn: Connection) => {
  // UnhandledPromiseRejectionWarning: RepositoryNotFoundError: No repository for "UserRecord" was found. Looks like this entity is not registered in current "default" connection?
  getRepository(UserRecord).findOne("Scooby Doo").then((user?: UserRecord) => {
    if (user) {
      console.log(`Found you, ${user.name}!`)
    } else {
      console.log(`Scooby Doo, where are you?`)
    }
  });
});

@nicomouss
Copy link

Have you initialised your database before running your app?

Tested using the setup below, and everything is working fine (typeorm@0.2.16):

createConnection({
    type: "sqlite",
    database: "./devDb.db",
    entities: [UserRecord],
    dropSchema: true,
    synchronize: true,
  }).then((_conn: Connection) => {
    getRepository(UserRecord).findOne("Scooby Doo").then((user?: UserRecord) => {
      if (user) {
        console.log(`Found you, ${user.name}!`)
      } else {
        console.log(`Scooby Doo, where are you?`)
      }
    });
  });

@vlapo
Copy link
Contributor

vlapo commented Apr 13, 2019

Try this:

class UserRecord {
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

const userSchema = new EntitySchema<UserRecord>({
  name: "user",
  target: UserRecord,
  columns: {
    name: { type: String, primary: true }
  }
});

@EntityRepository(UserRecord)
class UserRepository extends AbstractRepository<UserRecord> {
  findByName(name: string) {
    return this.repository.findOne({ name });
  }
}

and this:

const repo = conn.getCustomRepository(UserRepository)

@chrisfls
Copy link

chrisfls commented Apr 15, 2019

Anyway to do it without calling EntityRepository(UserRecord) decorator? (I'm not using them)

@c12i
Copy link

c12i commented Nov 23, 2020

I think this would work:

class UserRecord {
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

const UserSchema = new EntitySchema<UserRecord>({
  name: "user",
  target: UserRecord,
  columns: {
    name: { type: String, primary: true }
  }
});

@EntityRepository(UserSchema)
class UserRepository extends AbstractRepository<UserRecord> {
  findByName(name: string) {
    return this.repository.findOne({ name });
  }
}

and:

const repo = conn.getCustomRepository(UserRepository)

@mtalbert
Copy link

I think this would work:

class UserRecord {
  name: string;
  constructor(name: string) {
    this.name = name
  }
}

const UserSchema = new EntitySchema<UserRecord>({
  name: "user",
  target: UserRecord,
  columns: {
    name: { type: String, primary: true }
  }
});

@EntityRepository(UserSchema)
class UserRepository extends AbstractRepository<UserRecord> {
  findByName(name: string) {
    return this.repository.findOne({ name });
  }
}

and:

const repo = conn.getCustomRepository(UserRepository)

I was able to achieve the same thing, but I abstracted the class and the EntitySchema using a shared interface between the two instead. This has the advantage of being able to be explicit with the schema definition if there are any differences between it and the class, but has the disadvantage of needing to do some transform or casting or the responses in the repository.

I hope that helps if anyone is exploring using interfaces!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants