TypeError: Cannot read property 'myDate' of undefined - node.js

I am trying to make one rather basic code to work but receive a browser error. expressjs logs shows an error as well.
TypeError: Cannot read property 'myDate' of undefined
at getReportTable (XXX\dist\controllers\crm\uploadHealthTable.js:18:70)
at Layer.handle [as handle_request] (XXX\node_modules\express\lib\router\layer.js:95:5)
at next (XXX\node_modules\express\lib\router\route.js:137:13)
import { Request, Response } from "express";
import * as mongoose from "mongoose";
import { uploadHealthTableSchema } from "../../models/crm/uploadHealthTableModel";
const reportTable = mongoose.model("USER_UPLOAD", uploadHealthTableSchema);
export class UploadHealthTable {
private myDate = new Date() ;
public getReportTable(req: Request, res: Response) {
reportTable.find({companyId: "18", uploadTime: { $gte: this.myDate.setDate(this.myDate.getDate() - 5) } },
"_id fileName status uploadTime", { sort: {uploadTime: -1} }, (err, report) => {
if (err) {
res.send(err);
}
res.json(report);
});
}
}
Call to this function -
export class CRMRoutes {
public uploadHealthTable: UploadHealthTable = new UploadHealthTable() ;
public routes(app: any): void {
app.route("/support/reporttable/1/all")
.get(this.uploadHealthTable.getReportTable) ;
}
}

You need to convert this function to an arrow function
public getReportTable = (req: Request, res: Response) => {}
Explanation:
Because you passed the function as an argument, the this value is still at the previous function context
Useful links:
Typescript documentation for functions#this
Typescript arrow functions

Related

In nestjs, how can we change default error messages from typeORM globally?

I have this code to change the default message from typeorm when a value in a unique column already exists. It just creates a custom message when we get an error 23505.
if (error.code === '23505') {
// message = This COLUMN VALUE already exists.
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
throw new BadRequestException(message);
}
throw new InternalServerErrorException();
I will have to use it in other services, so I would like to abstract that code.
I think I could just create a helper and then I import and call it wherever I need it. But I don’t know if there is a better solution to use it globally with a filter or an interceptor, so I don’t have to even import and call it in different services.
Is this possible? how can that be done?
If it is not possible, what do you think the best solution would be?
Here all the service code:
#Injectable()
export class MerchantsService {
constructor(
#InjectRepository(Merchant)
private merchantRepository: Repository<Merchant>,
) {}
public async create(createMerchantDto: CreateMerchantDto) {
try {
const user = this.merchantRepository.create({
...createMerchantDto,
documentType: DocumentType.NIT,
isActive: false,
});
await this.merchantRepository.save(user);
const { password, ...merchantData } = createMerchantDto;
return {
...merchantData,
};
} catch (error) {
if (error.code === '23505') {
// message = This COLUMN VALUE already exists.
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
throw new BadRequestException(message);
}
throw new InternalServerErrorException();
}
}
public async findOneByEmail(email: string): Promise<Merchant | null> {
return this.merchantRepository.findOneBy({ email });
}
}
I created an exception filter for typeORM errors.
This was the result:
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpStatus,
InternalServerErrorException,
} from '#nestjs/common';
import { Response } from 'express';
import { QueryFailedError, TypeORMError } from 'typeorm';
type ExceptionResponse = {
statusCode: number;
message: string;
};
#Catch(TypeORMError, QueryFailedError)
export class TypeORMExceptionFilter implements ExceptionFilter {
private defaultExceptionResponse: ExceptionResponse =
new InternalServerErrorException().getResponse() as ExceptionResponse;
private exceptionResponse: ExceptionResponse = this.defaultExceptionResponse;
catch(exception: TypeORMError | QueryFailedError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
exception instanceof QueryFailedError &&
this.setQueryFailedErrorResponse(exception);
response
.status(this.exceptionResponse.statusCode)
.json(this.exceptionResponse);
}
private setQueryFailedErrorResponse(exception: QueryFailedError): void {
const error = exception.driverError;
if (error.code === '23505') {
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
this.exceptionResponse = {
statusCode: HttpStatus.BAD_REQUEST,
message,
};
}
// Other error codes can be handled here
}
// Add more methods here to set a different response for any other typeORM error, if needed.
// All typeORM erros: https://github.com/typeorm/typeorm/tree/master/src/error
}
I set it globally:
import { TypeORMExceptionFilter } from './common';
async function bootstrap() {
//...Other code
app.useGlobalFilters(new TypeORMExceptionFilter());
//...Other code
await app.listen(3000);
}
bootstrap();
And now I don't have to add any code when doing changes in the database:
#Injectable()
export class MerchantsService {
constructor(
#InjectRepository(Merchant)
private merchantRepository: Repository<Merchant>,
) {}
public async create(createMerchantDto: CreateMerchantDto) {
const user = this.merchantRepository.create({
...createMerchantDto,
documentType: DocumentType.NIT,
isActive: false,
});
await this.merchantRepository.save(user);
const { password, ...merchantData } = createMerchantDto;
return {
...merchantData,
};
}
}
Notice that now I don't use try catch because nest is handling the exceptions. When the repository save() method returns an error (actually it is a rejected promise), it is caught in the filter.

NestJS | Passport: TypeError: Cannot read properties of undefined (reading 'logIn')

Situation:
Developing api in nest & grapqhql
Worked on one laptop, everything was working well
Then cloned my repo on other laptops, installed dependencies, created a new local database.
App is being built with no errors
When following localhost:4000 in browser to open graphql playground I'm receiving 500 error end next message:
ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'logIn')
TypeError: Cannot read properties of undefined (reading 'logIn')
at authenticate (/home/gleb/Projects/artwine-api/node_modules/passport/lib/middleware/authenticate.js:96:21)
at /home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:91:3
at new Promise (<anonymous>)
at /home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:83:83
at JWTAccessAuthGuard.<anonymous> (/home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:49:36)
at Generator.next (<anonymous>)
at fulfilled (/home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:17:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Code of a passport lib function where the error is caught:
return function authenticate(req, res, next) {
req.login =
req.logIn = req.logIn || IncomingMessageExt.logIn;
req.logout =
req.logOut = req.logOut || IncomingMessageExt.logOut;
req.isAuthenticated = req.isAuthenticated || IncomingMessageExt.isAuthenticated;
req.isUnauthenticated = req.isUnauthenticated || IncomingMessageExt.isUnauthenticated;
req._sessionManager = passport._sm;
..............
Link to the repo: https://github.com/Gleb-Gaiduk/artwine-api
Any ideas on what could go wrong after cloning the working repository?
You need to transform the ExecutionContext from Graphql to one Nestjs/Passport can read: https://docs.nestjs.com/graphql/other-features#execution-context
import { ExecutionContext, Injectable } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class MySuperGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
try adding this into "subscriptions" section of GraphQL module initialization:
'subscriptions-transport-ws': {
onConnect: (headersRaw: Record<string, unknown>) => {
// Lowercase each header key
const headers = Object.keys(headersRaw).reduce((dest, key) => {
dest[key.toLowerCase()] = headersRaw[key];
return dest;
}, {});
return {
req: {
headers: headers,
},
};
},
},
I have no idea why it is not documented, but it worked for me.
You should look your Guard and strategy you are using and should handle errors from there for example:
#Injectable()
export class PassportLocalGuard extends AuthGuard('local') {
protected readonly logger = new Logger(PassportLocalGuard.name);
canActivate(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
return super.canActivate(new ExecutionContextHost([req]));
}
handleRequest(err: any, user: any) {
if (err) {
this.logger.error(`Auth Error! ${err.message}`);
throw err;
}
if (!user) {
this.logger.error('Auth Error! User not found');
throw new AuthenticationError('Auth Error! User not found');
}
return user;
}
}

`this` is undefined inside an object

With Nodejs, I am calling a function called customFunction which is a mutation from a GrpahQL Resolver. I don't have acces to this.
import { Mutation } from './mutation/Mutation'
export default {
Query,
Mutation,
}
then in Mutation.ts
import { customFunctionMutation } from './customFunctionMutation'
export const Mutation = {
customFunction: customFunctionMutation.customFunction,
}
then in customFunctionMutation.ts
export const customFunctionMutation = {
test() {
console.log('test called')
},
async customFunction(parent: any, args: any, ctx: any, info: any) {
console.log('customFunction init')
console.log('this', this)
this.test()
console.log('customFunction end')
},
}
this is undefined and i cannot called the function test() which is in the same object
You separated the method from the object that has the test method when you did this:
import { customFunction } from './customFunction'
So, then when you try to call customFunction() it will have no association with the object it is declared inside of and thus it can't reference this.test() because this will be undefined.
FYI, giving the same name to the export and a property on the export is hopelessly confusing to your clients. Please don't do that.
I would suggest fixing it by making your module be independent of how it was called by changing it to no longer use this:
const moduleObj = {
test() {
console.log('test called')
},
async customFunction(parent: any, args: any, ctx: any, info: any) {
console.log('customFunction init')
console.log('this', this)
moduleObj.test()
console.log('customFunction end')
},
}
export default moduleObj;
Then, you can use:
import { customFunction } from './customFunction'
And, you can then call:
customFunction()
and it will be able to function properly when called.
Probably one of these could work:
import { customFunction } from './customFunction'
export const Mutation = {
customFunction: customFunction.customFunction.bind(customFunction),
}
or
import { customFunction } from './customFunction'
export const Mutation = customFunction
or
import { customFunction } from './customFunction'
export const Mutation = {
customFunction: function functionName(...parameters) { return customFunction.customFunction(...parameters); },
}

Express - REST API - repository instance is lost during routing

please can you help me with my routing problem? I'm trying to make a REST API using typescript and repository pattern in Node.js (express).
I have two generic classes BaseRepository and BaseController which together handle the basic CRUD transactions. Other domain controllers are derived from these ones.
There is my code:
productRouter.ts used to handle routes:
import { Router } from 'express';
import { ProductController } from '../controllers/ProductController';
class ProductRouter {
private _router: Router;
private _controller: ProductController;
constructor() {
this._router = Router();
this._controller = new ProductController;
}
get routes(): Router {
this._router.get('/product', this._controller.getAll);
this._router.post('/product', this._controller.create);
this._router.get('/product/:id', this._controller.getById);
this._router.put('/product/:id', this._controller.update);
this._router.delete('/product/:id', this._controller.delete);
return this._router;
}
}
productController.ts used to init the BaseRepository and its derived from the BaseController.
import { BaseController } from '../common/BaseController';
import { BaseRepository, IRepository } from '../common/BaseRepository';
import { Product, IProduct } from '../models/Product';
export class ProductController extends BaseController<IProduct> {
constructor() {
const productRepository = new BaseRepository<IProduct>(Product);
super(productRepository);
}
}
BaseController.ts
export class BaseController<T> implements IController {
private _repository: IRepository<T>;
constructor(repository: IRepository<T>) {
this._repository = repository;
}
getAll(req: Request, res: Response, next: NextFunction): void {
this._repository.getAll((err, result) => {
if (err)
res.send(err);
res.json(result);
});
}
Every time I navigate to appropriate route I get the following error:
TypeError: Cannot read property '_repository' of undefined
at BaseController.getAll (C:\Users\vrbat\Documents\Projects\router-test\src\api\common\BaseController.ts:19:13)
enter code here
I don't know why, because the _repository property is iniciated in productController.ts.
After some digging I figured out that the problem is in the wrong scoping of _repository property.
The following code will fix it:
productRouter.ts
get routes(): Router {
this._router.get('/product', this._controller.getAll());
this._router.post('/product', this._controller.create());
this._router.get('/product/:id', this._controller.getById());
this._router.put('/product/:id', this._controller.update());
this._router.delete('/product/:id', this._controller.delete());
return this._router;
}
BaseController.ts
export class BaseController<T> implements IController {
private _repository: IRepository<T>;
constructor(repository: IRepository<T>) {
this._repository = repository;
}
getAll() {
const repository = this._repository;
return (req: Request, res: Response, next: NextFunction) => {
repository.getAll((err, result) => {
if (err)
res.send(err);
res.json(result);
});
};
}

typescript + node How to get instanse from methods?

After server rebuild, compiller creates instanse in included api controller here:
NewController.ts
import express = require("express");
import INew = require("../interface/INew");
import NewRepository = require("../repositories/NewRepository");
class NewController {
private _newRepository: INew;
constructor() {
this._newRepository = new NewRepository();
this._newRepository.findById(5);
}
retrieve(req: express.Request, res: express.Response): void {
try {
console.log('-----------retrieve--------------------');
this._newRepository.findById(2);
}
catch (e) {
console.log(e);
}
}
}
Object.seal(NewController);
export = NewController;
constructor works: i see console message:
-------------NewRepository------------------
5 'RESULT'
NewRepository.ts:
import INew = require("../interface/INew");
import bluebird = require("bluebird");
class NewRepository implements INew {
sd: string;
constructor() {
console.log('-------------NewRepository------------------');
}
findById(id: number): void {
setTimeout(function () {
console.log(id, 'RESULT');
}, 3000);
}
}
export = NewRepository;
INew.ts
interface INew {
findById: (id: number) => void;
sd: string;
}
export = INew;
Buut when i use controller's method 'retrieve', visit rout '/new' then i get error [TypeError: Cannot read property '_newRepository' of undefined] instead : 2 'RESULT'
Angular 2 helps me with routing:
.............
getCarsRestful(): Promise<New[]> {
console.log('-------------------------------');
return this.http.get('api/new')
.toPromise()
.then(response => response.json())
.catch(this.handleError);
}
...................
and execute backend:
NewRoutes.ts
import express = require("express");
import NewController = require('../controllers/NewController');
var router = express.Router();
class NewRoutes {
private _newController: NewController;
constructor() {
this._newController = new NewController()
}
get routes() {
var controller = this._newController;
router.get("/new", controller.retrieve);
return router;
}
}
Object.seal(NewRoutes);
export = NewRoutes;
my created instanse '_newRepository' doesn't exist already, why? i get console log:
-----------retrieve--------------------
[TypeError: Cannot read property '_newRepository' of undefined]
Help please, how to make 'singltone' in ts
i don't want to create it in every controller's method, though, that works:
.................
retrieve(req: express.Request, res: express.Response): void {
try {
var _newRepository: INew;
_newRepository = new NewRepository();
_newRepository.findById(2);
.............
Try explicitly set this in router config:
router.get("/new", controller.retrieve.bind(controller));

Resources