Could not save entity EntityMetadataNotFoundError: No metadata for "User" was found - nestjs

Seeder is running, but not saving data in DB. I checked all the Stack Overflow solutions and applied them all, but none worked for me.
Seeder code
import { Seeder, Factory } from "typeorm-seeding";
import { Connection } from "typeorm";
import { User } from "../../../entities";
// import { Factory } from "typeorm-seeding/dist/types";
export class UserSeed implements Seeder{
public async run(factory: Factory, connection: Connection): Promise<void>{
console.log("helloo1")
await factory(User)().create()
}
}
Factory code
import { define } from 'typeorm-seeding';
import { User } from "../../entities";
import { randEmail, randFullName, randPassword } from "#ngneat/falso";
define(User, () => {
console.log("hello2");
const user = new User();
user.first_name = randFullName();
user.email = randEmail();
user.password = randPassword();
return user;
});
ORM config file
import { DataSource } from 'typeorm';
import { ConfigService, registerAs } from '#nestjs/config';
import { config } from 'dotenv';
config();
const configService = new ConfigService();
export default {
type: 'mysql',
host: configService.get('DATABASE_HOST'),
port: configService.get('DATABASE_PORT'),
username: configService.get('DATABASE_USER'),
password: configService.get('DATABASE_PASSWORD'),
database: configService.get('DATABASE_NAME'),
entities: [__dirname + '/entities/*.entity{.ts,.js}'],
migrations: ['src/database/migrations/*{.ts,.js}'],
migrationsTableName: 'migrations_table',
seeds: ['src/database/seeding/seeds/*{.ts,.js}'],
factories: ['src/database/factories/*{.ts,.js}'],
};
Changes I did in my package.sjon file
"seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config",
"seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed ",
"db:seed": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed -n src/database/seeding/seeds/create-products.seed.ts"
enter image description here

Related

NestJS / TypeORM: Custom repository method is not accessible in service

New to NestJS and TypeORM, and the similar questions on SO didn't solve my problem.
I have a custom TypeORM repository in NestJS using it in service, but it fails with error:
TypeError: this.tenantRepository.createTenant is not a function.
tenants.module.ts:
import { TenantRepository } from './tenant.repository';
#Module({
imports: [
TypeOrmModule.forFeature([TenantRepository]),
],
controllers: [TenantsController],
providers: [TenantsService],
})
export class TenantsModule { }
tenant.repository.ts:
// ...
import { TenantEntity } from './entities/tenant.entity';
#EntityRepository(TenantEntity)
export class TenantRepository extends Repository<TenantEntity>{
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
const { name, email } = createTenantDto;
const newTenant = new TenantEntity()
newTenant.name = name;
newTenant.email = email;
await newTenant.save()
return newTenant;
}
}
And here's where the error is triggered (tenants.service.ts)
// ...
import { TenantEntity } from './entities/tenant.entity';
import { TenantRepository } from './tenant.repository';
#Injectable()
export class TenantsService {
constructor(
#InjectRepository(TenantRepository)
private tenantRepository: TenantRepository
) { }
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
return await this.tenantRepository.createTenant(createTenantDto); // <-- ERROR
}
}
I can inject entity in service and use it for simple CRUD, but I want to separate concerns and use the repository pattern.
This is a POST endpoint and the error is only after submission from Swagger.
Also, VS Code autocomplete is suggesting createTenant after typing this.tenantRepository
Where am I going wrong?
EntityRepository decorator was deprecated, and as far as I know, you need to define a custom class that extends Repository and decorate it with #Injectable. Hence, you need to have some changes as follows:
tenant.repository.ts:
import { Injectable } from '#nestjs/common';
import { DataSource, Repository } from 'typeorm';
#Injectable()
export class TenantRepository extends Repository<TenantEntity>{
constructor(private dataSource: DataSource) {
super(TenantEntity, dataSource.createEntityManager());
}
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
const { name, email } = createTenantDto;
const newTenant = this.create({ name, email });
await this.save(newTenant);
return newTenant;
}
}
tenants.module.ts:
import { TenantRepository } from './tenant.repository';
#Module({
imports: [
TypeOrmModule.forFeature([TenantRepository]),
],
controllers: [TenantsController],
providers: [TenantsService, TenantRepository],
})
export class TenantsModule { }
tenants.service.ts:
import { TenantEntity } from './entities/tenant.entity';
import { TenantRepository } from './tenant.repository';
#Injectable()
export class TenantsService {
constructor(
private tenantRepository: TenantRepository
) { }
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
return await this.tenantRepository.createTenant(createTenantDto);
}
}
You also have access to built-in typeorm methods like save, create, find, etc. since the custom repository is derived from Repository class.

Error injecting dependency into controller with tsyringe

I am using express and tsyringe for dependency injections.
When I try to use the service dependency on my controller I get an error UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'addressService' of undefined in file addressController.ts
I've searched for several videos and articles and I couldn't find a way to solve them. I will leave my code below
addressRepository.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
export interface IAddressRepository {
getAddresses(): Array<AddressResponseDTO>;
}
addressRepositoryImpl.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import { IAddressRepository } from '../declarations/addressRepository';
export default class AddressRepositoryImpl implements IAddressRepository {
getAddresses(): Array<AddressResponseDTO> {
const address1: AddressResponseDTO = {
id: 'ADR123',
postalCode: '17014273',
address: 'Rua José Aiello',
district: 'Centro',
number: '347',
city: 'Bauru',
state: 'São Paulo',
phoneNumber: '16997102842',
clientName: 'João Mário Marcelo Campos',
};
const address2: AddressResponseDTO = {
id: 'ADR456',
postalCode: '07135290',
address: 'Rua Mariano Moya Peramos',
district: 'Jardim Adriana',
number: '1103',
city: 'Guarulhos',
state: 'São Paulo',
phoneNumber: '16997102842',
clientName: 'João Mário Marcelo Campos',
};
return [address1, address2];
}
}
addressService.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import ResultResponseDTO from '../../dtos/response/resultResponseDTO';
export interface IAddressService {
getAddresses(): ResultResponseDTO<Array<AddressResponseDTO>>;
}
addressServiceImpl.ts
import { StatusCodes } from 'http-status-codes';
import { inject, injectable } from 'tsyringe';
import logger from '../../configurations/logger';
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import ResultResponseDTO from '../../dtos/response/resultResponseDTO';
import { IAddressRepository } from '../../repositories/declarations/addressRepository';
import { IAddressService } from '../declarations/addressService';
#injectable()
export default class AddressServiceImpl implements IAddressService {
private addressRepository: IAddressRepository;
constructor(#inject('AddressRepository') addressRepository: IAddressRepository) {
this.addressRepository = addressRepository;
}
getAddresses(): ResultResponseDTO<AddressResponseDTO[]> {
try {
logger.info('AddressService.getAddresses - start');
const addresses = this.addressRepository.getAddresses();
logger.info('AddressService.getAddresses - end');
return ResultResponseDTO.ok(StatusCodes.OK, '', addresses);
} catch (error) {
logger.error(`AddressService.getAddresses - error - message ${error}`);
return ResultResponseDTO.fail(
StatusCodes.INTERNAL_SERVER_ERROR,
'Error getting addresses',
error,
);
}
}
}
addressController.ts
Error at line: const result = this.addressService.getAddresses();
import { Request, Response } from 'express';
import { inject, injectable } from 'tsyringe';
import { IAddressService } from '../services/declarations/addressService';
#injectable()
export class AddressController {
constructor(#inject('AddressService') private addressService: IAddressService) {}
public async getAddresses(request: Request, response: Response): Promise<Response> {
const result = this.addressService.getAddresses();
return response.status(result.httpStatus).json(result.getResponse());
}
}
routes.ts
import { Router } from 'express';
import { container } from 'tsyringe';
import { AddressController } from './controllers/addressController';
import AddressRepositoryImpl from './repositories/implementations/addressRepositoryImpl';
import AddressServiceImpl from './services/implementations/addressServiceImpl';
const routes = Router();
container
.register('AddressRepository', {
useClass: AddressRepositoryImpl,
})
.resolve(AddressRepositoryImpl);
container
.register('AddressService', {
useClass: AddressServiceImpl,
})
.resolve(AddressServiceImpl);
const addressController = container.resolve(AddressController);
routes.get('/', (request, response) => {
response.send('It works!');
});
// AddressController
routes.route('/v1/address').get(addressController.getAddresses);
export default routes;
You register service inside IoC as 'AddressServiceImpl' and after that try to call to it as 'AddressService'.
// service file
#injectable()
export default class AddressServiceImpl implements IAddressService {
...
// controller file
#injectable()
export class AddressController {
constructor(#inject('AddressService') private addressService: IAddressService) {}

NestJS serving JSON and adds a "default" section repeating the JSON

I have a strange behaviour on an endpoint in NestJS serving a piece of JSON.
The JS with the JSON object is exporting
module.exports = Object.freeze({
translation: {
TestMessage: 'Bienvenue à React et react-i18next'
}
});
The result on the Client is:
{
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
},
"default": {
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
}
}
}
The question is where is the "default" coming from?
To paint the whole picture, below the module, controller and service:
Module
import { Module } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nController } from './i18n.controller';
import { I18nService } from './i18n.service';
#Module({
controllers: [I18nController],
providers: [I18nService, LoggerService],
exports: [I18nService]
})
export class I18nModule {}
Controller
import { Controller, Get, Param } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nService } from './i18n.service';
#Controller('i18n')
export class I18nController {
constructor(private logger: LoggerService, private i18nService: I18nService) {
this.logger.setContext(I18nController.name);
}
#Get('/:lang')
async getLanguage(#Param('lang') lang: string) {
console.log(lang);
return await this.i18nService.findOneByLanguageCode(lang);
}
}
Service
import { Injectable } from '#nestjs/common';
import { access } from 'fs/promises';
import { constants as fsconstants } from 'fs';
#Injectable()
export class I18nService {
async findOneByLanguageCode(language: string): Promise<any | null> {
const languagefile = __dirname + '/../../public/languages/' + language + '.js';
await access(languagefile, fsconstants.R_OK);
return await import(languagefile);
}
}
From the Client I do a simple http://localhost:3001/i18n/fr-FR
and get the above result.
Again, where is the 'default' section coming from?
There should be esModuleInterop enabled in your tsconfig.json
https://www.typescriptlang.org/tsconfig#esModuleInterop
a default import like import moment from "moment" acts the same as const moment = require("moment").default
That's why you have default object exist.
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true, // change it to false or remove it
}
}

Inversify - Nodejs- Container binding results in max call size exceeded

I am trying to use inversify for DI injection in a small project in typescript. I wanted to create a logging middleware that logs every request with a pino logger customized. I did establish the dependencies manually when the app is created but was wooed by inversify and wanted to try it out.
But I am stuck and unable to proceed. THis is the error that I am facing. Not sure what is making the call to exceed the stack size. Please help me out
public set app_port(app_port: number) {
^
RangeError: Maximum call stack size exceeded
PinoLoggingService.ts
import * as pino from 'pino';
import * as os from 'os';
import * as momentTimeZone from 'moment-timezone';
import { DEFAULT_TIMEZONE, DEFAULT_TIME_FORMAT } from '../../shared/constants/app.constants';
import { AppConfigModel } from './../../shared/model/app.config.model';
import { isNullOrUndefined } from 'util';
import httpContext = require('express-http-context');
import { injectable, inject } from 'inversify';
import { TYPES } from '../../shared/constants/app.types';
import { AppConfigService } from '../../shared/service/app.config.service';
#injectable()
export class PinoLoggingService {
private appConfig: AppConfigModel;
constructor(#inject(TYPES.AppConfigService) private appConfigService: AppConfigService){
this.appConfig = appConfigService.appConfigModel;
}
private getTimeStamp(){
return momentTimeZone().tz(DEFAULT_TIMEZONE).format(DEFAULT_TIME_FORMAT);
}
public infoLogService (fileName): pino.Logger {
return pino({
level: 'info',
name: this.appConfig.app_name,
messageKey: 'feedback-Logs',
base: {pid: process.pid, hostname: os.hostname,
timestamp: this.getTimeStamp(),
appName: this.appConfig.app_name,
fileName: fileName,
request_id: isNullOrUndefined(httpContext.get('reqId')) ? 'Not an actual request ' : httpContext.get('reqId')
},
enabled: true,
useLevelLabels: true,
});
}
}
my AppConfigService.ts - AppConfigModel is just a plain ts with getters and setters.
#injectable()
export class AppConfigService implements IAppConfigService{
private appModel: AppConfigModel;
constructor() {
console.log('parg is:'+path.resolve(__dirname));
}
public get appConfigModel(): AppConfigModel {
if(isNullOrUndefined(this.appModel)) {
this.appModel = new AppConfigModel();
this.appModel.app_port = isNullOrUndefined(process.env.PORT) ? DEFAULT_PORT : parseInt(process.env.PORT);
this.appModel.app_lcp = process.env.LCP;
this.appModel.app_name = process.env.APP_NAME;
this.appModel.app_host = process.env.HOST;
this.appModel.app_node_env = process.env.NODE_ENV;
this.appModel.app_logging = JSON.parse (process.env.PINO_ENABLE_LOGGING);
this.appModel.app_version = process.env.VERSION;
this.appModel.app_context_path = process.env.CONTEXT_PATH;
}
return this.appModel;;
}
}
This is my loggingMiddleware.ts
import * as express from 'express';
import { AppUtilService } from './../util/app.util.service';
import { file } from '#babel/types';
import { inject, injectable } from 'inversify';
import { TYPES } from '../constants/app.types';
import { PinoLoggingService } from '../../logging/service/logging.service';
interface ILoggingMiddleware {
loggingMiddlewareMethod () : express.Router;
}
#injectable()
export class LoggingMiddleware implements ILoggingMiddleware {
constructor(#inject(TYPES.AppUtilService) private utilService: AppUtilService,
#inject(TYPES.PinoLoggingService) private pinoLoggingService: PinoLoggingService){
}
public loggingMiddlewareMethod() {
const appRouter = express.Router();
return appRouter.use((req:express.Request, res:express.Response, next) => {
const fileName = this.utilService.getFileName(__filename);
this.pinoLoggingService.infoLogService(fileName).info('req.url:', req.url);
this.pinoLoggingService.infoLogService(fileName).info('req.headers:', req.headers);
this.pinoLoggingService.infoLogService(fileName).info('req.body:', req.body);
next();
});
}
}
THis is my request tracing middleware
const httpContext = require('express-http-context');
import * as express from 'express';
import { injectable } from 'inversify';
#injectable()
export class RequestTracerMiddleware {
public requestTracer() {
const app = express();
app.use(httpContext.middleware);
}
}
THis is my inversify config
import { Container } from "inversify";
import { TYPES } from "./module/shared/constants/app.types";
import { AppConfigService } from "./module/shared/service/app.config.service";
import { AppUtilService } from "./module/shared/util/app.util.service";
import { LoggingMiddleware } from "./module/shared/middlewares/logging.middleware";
import { CommonMiddleware } from "./module/shared/middlewares/common.middlewares";
import { RequestTracerMiddleware } from "./module/shared/middlewares/request.tracer.middleware";
import { PinoLoggingService } from "./module/logging/service/logging.service";
import { StudentController } from "./module/shared/test.controller";
const DIContainer = new Container({defaultScope: "Singleton"});
DIContainer.bind<AppConfigService>(TYPES.AppConfigService).to(AppConfigService);
DIContainer.bind<AppUtilService>(TYPES.AppUtilService).to(AppUtilService);
DIContainer.bind<LoggingMiddleware>(TYPES.LoggingMiddleware).to(LoggingMiddleware);
DIContainer.bind<CommonMiddleware>(TYPES.CommonMiddleware).to(CommonMiddleware);
DIContainer.bind<RequestTracerMiddleware>(TYPES.RequestTracerMiddleware).to(RequestTracerMiddleware);
DIContainer.bind<PinoLoggingService>(TYPES.PinoLoggingService).to(PinoLoggingService);
DIContainer.bind<StudentController>(TYPES.StudentController).to(StudentController);
export default DIContainer;
This is my server.ts
import 'reflect-metadata';
import * as DIContainer from './inversify.config';
import {InversifyExpressServer } from 'inversify-express-utils'
import { inject } from 'inversify';
import { TYPES } from './module/shared/constants/app.types';
import { CommonMiddleware } from './module/shared/middlewares/common.middlewares';
import { LoggingMiddleware } from './module/shared/middlewares/logging.middleware';
import { RequestTracerMiddleware } from './module/shared/middlewares/request.tracer.middleware';
const container = DIContainer.default;
let server = new InversifyExpressServer(container);
const commMiddleware = container.resolve(CommonMiddleware);
// const requestTracingMiddleware = container.resolve(RequestTracerMiddleware);
const loggingMiddleware = container.resolve(LoggingMiddleware);
server.setConfig(appRouter => {
appRouter.use(commMiddleware.enableBodyParser());
appRouter.use(loggingMiddleware.loggingMiddlewareMethod());
});
// set errorConfig if needed
const app = server.build();
app.listen(2000, () => {
console.log('listening to port');
});
This is a sample controller that I setup.
import {controller, httpGet, httpPost, requestHeaders, requestParam, BaseHttpController, HttpResponseMessage, StringContent} from 'inversify-express-utils';
#controller("/IStudentController")
export class StudentController extends BaseHttpController {
constructor( ) {
super();
console.log('value of stdservuce:');
}
#httpGet("/Iget")
public getStudentDetails(#requestHeaders() reqHeaders: string[] ) {
// sample returning json resuilt - IHttpAcctionResults
const val = 43;
console.log('val from the servuc:', val);
return this.json(val, 200);
}
}

How to use connection as standalone object with types?

Not working code just to illustrate what I'm trying to achieve
Some connection file
import { ConnectionManager } from 'typeorm';
const c = new ConnectionManager();
// user ormconfig.conf file
export const connection = c.createAndConnect();
using in some model
#Entity()
#Table("annual_incomes")
export class AnnualIncome
{
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 75 })
variant: string;
#Column("int")
sort: number;
#Column()
is_active: boolean;
}
Later somewhere in the code, I want to get connection with all methods, something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.getRepository(AnnualIncome).find();
});
Usually, I'm getting an error from tsc that .getRepository() method was not found in connection. However if I do something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.then(async connection => {
return await connection.getRepository(AnnualIncome).find();
}
});
the above code works with definitions and tsc does not complain about not-existing methods.
I'd like to avoid an extra definition connection.then() and get plain connection with all methods defined in <Connection> type.
just use createConnection method to create your connection when you bootstrap your application. Later you can access your connection from anywhere using getConnection() method:
import { AnnualIncome } from 'entities';
import { createConnection, getConnection } from 'typeorm';
// somewhere in your app, better where you bootstrap express and other things
createConnection(); // read config from ormconfig.json or pass them here
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await getConnection().getRepository(AnnualIncome).find();
});
Also you can simply use getRepository method also avalible from anywhere:
import { AnnualIncome } from 'entities';
import { getRepository } from 'typeorm';
// some code here
api.get('/incomes', async (ctx) => {
ctx.body = await getRepository(AnnualIncome).find();
});

Resources