0
votes

Bookstore exercise. A Book can be assigned a single Genre Trying to assign a new book with the genreId supplied in the DTO.

BookRepository

    @EntityRepository(Book)
    export class BookRepository extends Repository<Book> {
      constructor(
        private genreService: GenresService,
      ) {
        super();
      }

      async createBook(createBookDto: CreateBookDto): Promise<Book> {
        const genre = await this.genreService.getOne(createBookDto.genreId);
        const newBook = this.create({
          ...createBookDto,
          genre,
        });
        return newBook.save();
      }
    }

GenreService

    @Injectable()
    export class GenresService {

      constructor(
        @InjectRepository(GenreRepository) private readonly genreRepository: GenreRepository,
      ) {}

      async getOne(id: number): Promise<Genre> {
        return this.getById(id);
      }

      private async getById(id: number): Promise<Genre> {
        const found = await this.genreRepository.findOne(id);

        if (!found) {
          throw new NotFoundException(`Genre with id ${id} not found.`);
        }

        return found;
      }
    }

BookRepository and GenreRepository are imported together by the BookstoreModule, like so:

    imports: [
        TypeOrmModule.forFeature([
          GenreRepository,
          BookRepository,
          AuthorRepository,
        ]),
        // ...etc
    },

NestJS spits out the following error:

[ExceptionsHandler] this.genreService.getOne is not a function +1045981ms
TypeError: this.genreService.getOne is not a function
    at BookRepository.createBook (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/book.repository.js:21:47)
    at BooksService.createBook (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/books.service.js:29:36)
    at BooksController.create (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/books.controller.js:31:33)

Tried to inject GenreRepository into theBookRepository`

constructor(
    @InjectRepository(GenreRepository) private genreRepository: GenreRepository,
){
    super();
}

and .findOne() from there, the error was:

No metadata for "2" was found. +26884ms
EntityMetadataNotFound: No metadata for "2" was found.

(2 being the id for genreId)

Not sure if my approach to saving is correct. Maybe the idea of finding the genre from within BookRepository is wrong and it should be solved in a different way. If so, how?

1

1 Answers

1
votes

To me the createBook method should be lying within the BookService, BookService needs to inject GenreService and then you should call the genreService.getOne(createBookDto.genreId) from within the BookService.
It makes more sense to let the services handle the business logic / orchestration of the data flow imo.

I'd suggest to give it a try try with the following code:

BookService

@Injectable()
    export class BookService {

      constructor(
        @InjectRepository(BookRepository) private readonly bookRepository: BookRepository, 
private genreService: GenreService // <= here you go, you inject the genreService into the bookService to take advantage of its methods
      ) {}

      async getOne(id: number): Promise<Genre> {
        return this.getById(id);
      }

      async createBook(createBookDto: CreateBookDto): Promise<Book> {
        const genre = await this.genreService.getOne(createBookDto.genreId);
        const newBook = this.create({
          ...createBookDto,
          genre,
        });
        return newBook.save();
      }

      private async getById(id: number): Promise<Genre> {
        const found = await this.bookRepository.findOne(id);

        if (!found) {
          throw new NotFoundException(`Book with id ${id} not found.`);
        }

        return found;
      }
    }

BookRepository

    @EntityRepository(Book)
    export class BookRepository extends Repository<Book> {}

GenresService

    @Injectable()
    export class GenresService {

      constructor(
        @InjectRepository(GenreRepository) private readonly genreRepository: GenreRepository,
      ) {}

      async getOne(id: number): Promise<Genre> {
        return this.getById(id);
      }

      private async getById(id: number): Promise<Genre> {
        const found = await this.genreRepository.findOne(id);

        if (!found) {
          throw new NotFoundException(`Genre with id ${id} not found.`);
        }

        return found;
      }
    }

GenreRepository

    @EntityRepository(Genre)
    export class GenreRepository extends Repository<Genre> {}

Let me know if it helps :)