"Cannot read properties of undefined reading router" Error message using TSYRINGE

370 Views Asked by At

I'm building a class-based restFul API using TypeScript and expressJS with clean architecture, I'm practicing with Typegoose and Tsyringe just to learn how to make dependency injection in this language and framework.

I'm having a problem when trying to access the path, and is that I get a TypeError.

Below is the code of my current classes:

Product: `

import { getModelForClass, prop } from '@typegoose/typegoose';

export class ProductModel {

  @prop()
  private timestamp: number = Date.now();

  @prop({ required: true })
  private title!: string;

  @prop({ required: true })
  private description!: string;

  @prop({ required: true })
  private code!: number;

  @prop({ required: true })
  private picture!: string;

  @prop({ required: true })
  private price!: number;

  @prop({ required: true })
  private stock!: number;

}

export const Product = getModelForClass(ProductModel);

`

Database repository: `

import { Id, Query } from '../commons/commons';

export interface DatabaseRepository<T> {

  getAll(query?: Query): Promise<T[]>;

}

`

MongoDbAdapter: `

import { Product, ProductModel } from '../../../../domain/models/product/Product';
import { DatabaseRepository } from '../../../../application/repository/database.repository';
import { Id, Query } from '../../../../application/commons/commons';
import { injectable } from 'tsyringe';
import { Logger } from '../../../../application/config/logger/logger';

@injectable()
export class ProductMongoDBAdapter implements DatabaseRepository<ProductModel> {

  async getAll(query?: Query): Promise<ProductModel[]> {
    try {
      return Product.find({});
    } catch (error) {
      Logger.error(error);
      throw (error);
    }
  }
}

`

UseCase: `

import { autoInjectable, inject } from 'tsyringe';
import { DatabaseRepository } from '../../../application/repository/database.repository';
import { ProductModel } from '../../models/product/Product';

@autoInjectable()
export class GetAllProductsUseCase {

  constructor(@inject('DatabaseRepository') private databaseRepository: DatabaseRepository<ProductModel>) {
  }

  public async getAllProducts(): Promise<ProductModel[]> {
    return this.databaseRepository.getAll();
  }
}


`

Controller: `

import { autoInjectable } from 'tsyringe';
import * as USE_CASE from '../../../../domain/usecases';
import { Logger } from '../../../../application/config/logger/logger';
import { Request, Response } from 'express';

@autoInjectable()
export class ProductHandler {

  constructor(private readonly getAllProductsUseCase: USE_CASE.GetAllProductsUseCase) {
  }

  public async getAllProducts(req: Request, res: Response): Promise<Response> {
    try {
      const products = await this.getAllProductsUseCase.getAllProducts();
      return res.status(200).json(products);
    } catch (error) {
      Logger.error(error);
      throw(error);
    }
  }
}

`

Product router: `

import { Router } from 'express';
import * as PRODUCT_HANDLER from '../../rest-controller/';
import { autoInjectable } from 'tsyringe';

@autoInjectable()
export class ProductRouter {

  private readonly router: Router = Router();

  constructor(private readonly productHandler: PRODUCT_HANDLER.ProductHandler) {
  }

  public routes() {
    this.router
        .route('/api/products')
        .get(this.productHandler.getAllProducts);
  }
}

`

Router: `

import { Router } from 'express';
import { autoInjectable } from 'tsyringe';
import { ProductRouter } from './product/product.routes';

@autoInjectable()
export default class Routes {
  private router: Router = Router();

  constructor(private readonly productRouter: ProductRouter) {
  }

  public routes() {
    return this.router.use('/', this.productRouter.routes);
  }
}

`

App: `

import 'reflect-metadata';
import express, { Application, json } from 'express';
import morgan from 'morgan';
import cors from 'cors';
import { Server } from 'http';
import { Gen } from './commons/commons';
import { Logger } from './config/logger/logger';
import DbConnection from './config/database/DbConnection';
import Config from './config/Config';
import Routes from '../infrastructure/entry-points/routes/routes';
import { autoInjectable, container } from 'tsyringe';
import { ProductMongoDBAdapter } from '../infrastructure/driven-adapters/mongo-adapter/product/product.mongodb.adapter';

@autoInjectable()
export default class ServerApplication {
  private app: Application = express();
  private routes: Routes;

  constructor() {
    container.registerInstance('DatabaseRepository', new ProductMongoDBAdapter());
    this.routes = container.resolve(Routes);
    this.middlewares();
  }

  public init(PORT: Gen): Server {
    return this.app.listen(PORT, () => {
      Logger.info(`Open server on http://localhost:${PORT}`);
      DbConnection.connect(`${Config.DB_CONNECTION}`)
                  .then(() => Logger.info('Database connection established successfully'))
                  .catch(err => Logger.error(err));
    });
  }

  private middlewares() {
    this.app.use(cors());
    this.app.use(json());
    this.app.use(morgan('dev'));
    this.app.use('/', this.routes.routes);
  }
}

`

Server: `

import 'reflect-metadata';
import ServerApplication from './application/ServerApplication';
import Config from './application/config/Config';
import { Gen } from './application/commons/commons';
import { Logger } from './application/config/logger/logger';

export class Server {
  private static PORT: Gen = Config.PORT;
  private static serverApplication: ServerApplication = new ServerApplication();

  public static start(): void {
    try {
      this.serverApplication.init(`${Server.PORT}`);
    } catch (error) {
      Logger.error(error);
    }
  }
}

Server.start();

`

This is the error that I'm getting: TypeError: Cannot read properties of undefined (reading 'router') at routes (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\src\infrastructure\entry-points\routes\routes.ts:13:17) at Layer.handle [as handle_request] (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:317:13) at C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:335:12) at next (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:275:10) at logger (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\morgan\index.js:144:5) at Layer.handle [as handle_request] (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:317:13) at C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\[email protected]\node_modules\express\lib\router\index.js:284:7

I tried to solve it by changing the @autoinjectable decorator by @injectable, but I'm having the same response.

Anyone can help me please? I'm trying to figure out how to implement this with DI.

Thanks a lot

The API should display the products on my Database, but I'm receiving a TypeError instead

0

There are 0 best solutions below