Issue with implementing generic inheritance for Repository in NestJS TypeORM: `this.function` is returning an error as it is not

I am attempting to create a specialized repository class that extends Repository in order to incorporate custom logging functions that can be utilized across all repositories. Here is the snippet of code:

user.service.ts:

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private readonly userRepository: BaseRepository<User>,
    ) {}

    async update(id: string, data: UpdateUserDto): Promise<UpdateResult> {
        return await this.userRepository.updateAndLog(id, data);
    }
}

BaseRepository.ts:

import { Repository, UpdateResult } from 'typeorm';


export class BaseRepository<T> extends Repository<T> {

  async updateAndLog(id: string, data: any): Promise<UpdateResult> {
    const entity = await this.findOne(id as any);
    const savedEntity = await this.update(id, data);
    // log the data here
    return savedEntity;
  }
}

Therefore, the outcome of the function always displays:

[Nest] 13820  - 04/11/2023, 12:07:07 PM   ERROR [ExceptionsHandler] this.userRepository.updateAndLog is not a function

I have carefully reviewed the typeorm documentation regarding custom repositories:

Additional resources from StackOverflow:

  • How to do custom repository using TypeORM (MongoDB) in NestJS?
  • NestJS/TypeORM: Can custom repository extend from another custom repository which is inside another project?

View the repository on github: https://github.com/typeorm/typeorm/issues/2097

Despite my efforts, the code provided above is not functioning as expected. Is there an issue I may have overlooked?

Answer №1

The issue arises from using @InjectRepository(User) which injects a Repository instance instead of the desired BaseRepository.

I stumbled upon this particular repository that offers a solution to override the repository provided by the TypeORM module, but only for a specific entity.

We can tweak this approach to provide a generic repository like so:

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [buildCustomRepositoryProvider<User>(User), UserService],
})
export class UserModule {
}

A helper file for this purpose:

import { DataSource, Repository, UpdateResult } from 'typeorm';
import { Provider } from '@nestjs/common';
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import { EntityClassOrSchema } from '@nestjs/typeorm/dist/interfaces/entity-class-or-schema.type';

export interface BaseRepository<T> extends Repository<T> {
  this: Repository<T>;

  updateAndLog(id: string, data: any): Promise<UpdateResult>;
}

export function buildCustomRepositoryMethods<T>(): Pick<BaseRepository<T>, 'updateAndLog'> {
  return {
    async updateAndLog(id: string, data: any): Promise<UpdateResult> {
      const entity = await this.findOne({ where: { id: id as any } });
      const savedEntity = await this.update(id, data);
      // log the data here
      return savedEntity;
    },
  };
}

export function buildCustomRepositoryProvider<T>(entity: EntityClassOrSchema): Provider {
  return {
    provide: getRepositoryToken(entity),
    inject: [getDataSourceToken()],
    useFactory: (dataSource: DataSource) => {
      // Override the default repository with a custom one
      return dataSource.getRepository(entity).extend(buildCustomRepositoryMethods<T>());
    },
  };
}

Consequently, the @InjectRepository(User) will now inject an instance of Repository<User> with the methods from the BaseRepository interface.

(Please note: the use of the extend method to create a custom repository is recommended since TypeORM 0.3, as mentioned here)

Answer №2

Give this a try, I have successfully implemented this solution in my project

import { Type } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ObjectLiteral, Repository } from 'typeorm';

interface IDataService<T extends ObjectLiteral> {
  readonly repository: Repository<T>;
  findById: (id: string | number) => Promise<T | null>;
}

type Constructor<I> = new (...args: any[]) => I;

export function MyDataService<T extends ObjectLiteral>(
   entity: Constructor<T>,
): Type<IDataService<T>> {

   class DataServiceHost implements IDataService<T> {
     @InjectRepository(entity) public readonly repository: Repository<T>;

     public async findById(id: string | number): Promise<T | null> {
        return this.repository.findOneBy({ id } as any);
     }
   }
   return DataServiceHost;
}

Extend your service by inheriting from the DataService class,

class YourService extends MyDataService<YourDatabaseEntity>(YourDatabaseEntity) 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing a dictionary for comparing with an API response in order to generate an array of unique objects by eliminating duplicates

I currently have a React component that utilizes a dictionary to compare against an API response for address state. The goal is to map only the states that are returned back as options in a dropdown. Below is the mapping function used to create an array o ...

Steps for combining a sequence of subsequent subscriptions in RxJS

In my approach, I followed the code below. this.service1.info .subscribe(a => this.service2.getDetails(a.id) .subscribe(b => { this.doStuff(b); }) ); Lately, it has come to my attention that we will be adding more steps that gradu ...

Troubleshooting Ionic 4 IonSlides slideTo and getActiveIndex functionalities encountering issues within IonTab context

I am encountering an issue with my ion slides setup on a page. Here is the code snippet: <ion-slides #schemasliderref [options]="schemaSliderOpts" (ionSlideDidChange)="slideChange()"> <ion-slide *ngFor="let schemaImage of schemaImages; let i ...

Having difficulty adjusting the configuration settings for an embedded report within Angular 7

While attempting to integrate a Power BI report with Angular 7, I encountered an unexpected error when trying to configure the settings of the report. The error message stated: Type '{ filterPaneEnabled: boolean; navContentPaneEnabled: boolean; }&apos ...

Limitations on quantity utilizing typescript

Looking to create a type/interface with generics that has two properties: interface Response<T> { status: number; data: T | undefined; } Specifically, I want to enforce a rule where if the status is not equal to 200, then data must be undefined. ...

Hold off until the observable has finished

map((tasks): any => { return tasks.map(task => ({ ...task, status: this.getStatus(task.owner, task.delegationState, task.assignee, task.id), })); }); I utilize the getStatus method within the map() operator from rxjs. getStatus( ow ...

the category is unspecified

As I try to deploy my code and run the build, TypeScript is throwing an error stating that 'object' is of type unknown. I am currently working on resolving this issue in my specific scenario. export default function Home() { async function send ...

Is it possible to generate a property for an interface by casting a key within a for-in loop?

When I attempt to set a property on an object with a value from a dynamically generated form, I utilize a for-in loop to identify a property in the object and assign it. FormFeatureArray.forEach((el) => { // form handling stuff omitted For(c ...

Do not display large numbers within an HTML card

I have https://i.sstatic.net/DkowD.png this card here and displaying dynamic data inside it. The number is quite large, so I would like it to appear as 0.600000+. If a user hovers over the number, a tooltip should display the full number. How can I achieve ...

Create a dynamic styled component with tags based on props

Looking to craft a dynamic tag using styled components, where the tag is passed in via props. Here's an example of the code: import * as React from 'react'; import styled from 'styled-components'; type ContainerProps = { chi ...

The designated <input type=“text” maxlength=“4”> field must not include commas or periods when determining the character limit

In the input field, there are numbers and special characters like commas and dots. When calculating the maxLength of the field, I want to ignore special characters. I do not want to restrict special characters. The expected output should be: 1,234 (Total ...

Is it possible to use multiple schemas for one collection name?

I am currently working on creating different schemas for a single collection, such as User or subUser. I aim to store both User and subuser data in the same collection but with different schemas. Here is an example of my schema file: export const AryaSchem ...

In relation to the characteristics of an Angular Component (written in TypeScript) Class

I am attempting to create a circle on a canvas using Angular. I have followed the necessary steps and have a basic understanding of how everything works. Although my IDE is not showing any errors, when I run the code, the console displays an error stating ...

The input argument must be of type 'PollModel', as the property 'pollId' is required and missing in the provided 'any[]' type

Hey there! An issue popped up when I tried to pass an empty array as a model in my Angular project. The error message reads: "Argument of type 'any[]' is not assignable to parameter of type 'PollModel'. Property 'pollId' is ...

Having trouble importing a tailwind CSS file into a Remix.js project without TypeScript throwing an error

https://i.sstatic.net/pvyUf.png I've attempted to implement the solution found here but unfortunately, it's still not working for me. Below are my configuration files: remix.config.ts: /** @type {import('@remix-run/dev').AppConfig} * ...

Difficulty organizing nested entities in TypeORM

In my application, I have a hierarchical entity called Category. Each category acts as a folder, capable of containing subfolders. However, the subfolders themselves cannot have any further nested folders. The user has the ability to rearrange these catego ...

Using Typescript: Compiling specific files within a directory

According to the documentation for typescript, we have the option in tsconfig.json to manage input files using either the files property where all files are listed, or with the exclude property. I have organized all my source files within a directory named ...

Exploring the keyof operator in Typescript for object types

Is there a way to extract keys of type A and transfer them to type B? Even though I anticipate type B to be "x", it seems to also include "undefined". Why does the keyof operator incorporate undefined in the resulting type? It's perplexing. I kn ...

What is the best way to incorporate styled components and interpolations using emotion theming?

I've been building a React web application with a dynamic theme feature using the emotion-theming library. This allows users to switch between different environments, each with its own unique theme. To achieve this, I created my own CustomThemeProvide ...

I am currently analyzing a JSON file that contains deeply nested JavaScript Objects. My goal is to rearrange the data so that objects containing a specific field value are placed at the top of the list

Currently, I am parsing a JSON file which contains a map of JavaScript objects. For instance: { offers : { "1":{"id":"1", "category":"a", "offerType":"LS"}, "2":{"id":"2", "category":"a", "offerType":"EX"}, ...