I defined a class variable in a constructor which holds a reference to an imported class instance.
However, when I try to access the variable in my class methods I keep getting undefined for that variable.
Exported class
import models from '../../database/models'
const {User} = models;
class UserRepository {
constructor(){
this.model = User;
}
async create({firstname, lastname, email, isadmin, password}){
return await this.model.create({firstname, lastname, email, isadmin, password})
}
}
export default new UserRepository();
Class using the exported class
import repository from './repository';
import { sendSuccessMessage } from '../../utils'
class UserController {
constructor() {
this.model = repository;
}
async createUser({ body }, res, next) {
try {
const result = await this.model.create(body);
sendSuccessMessage(res, 200, result);
} catch( e ){
next(e)
}
}
}
export default new UserController();
However, using the variable repository directory in createUser as specified below:
const result = await repository.create(body);
works as expected.
Current call chain
// routes.js file
import controller from './controller'
export default [
{
path: '/signup',
method: 'post',
handlers: [controller.createUser] // createUser from class UserController
}
]
// utitlity.js file
export const routesLoader = (routes, router) => {
for (const route of routes) {
const { path, handlers, method } = route;
(router)[method](path, handlers);
}
};
// app.js file
import express from 'express';
import routes from './routes.js';
import { routesLoader } from './utils';
const router = express.Router();
routesLoader(routes, router);
// server.js file
import http from 'http';
import app from './app';
const { PORT = 3000 } = process.env;
const server = http.createServer(app);
server.listen(PORT, () => {
console.log(`server listening on localhost: ${PORT}`);
});
Error
TypeError: Cannot read property 'create' of undefined
at _callee$ (C:\Users\MYPC\Downloads\moch-shop-api-master\components\user/controller.js:16:39)
at tryCatch (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:45:40)
at Generator.invoke [as _invoke] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:271:22)
at Generator.prototype.<computed> [as next] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\regenerator-runtime\runtime.js:97:21)
at asyncGeneratorStep (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:3:24)
at _next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:25:9)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:32:7
at new Promise (<anonymous>)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\#babel\runtime\helpers\asyncToGenerator.js:21:12
at createUser (C:\Users\MYPC\Downloads\moch-shop-api-master\components\user\controller.js:70:28)
at Layer.handle [as handle_request] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\express\lib\router\index.js:275:10)
at cors (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:188:7)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:224:17
at originCallback (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:214:15)
at C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:219:13
at optionsCallback (C:\Users\MYPC\Downloads\moch-shop-api-master\node_modules\cors\lib\index.js:199:9)
Technologies used
#babel/node: "^7.8.4"
terminal: git bash
Os: windows
How is UserController.createUser called? Sounds like its this is not set up correctly. Let's say we initialized the class like this:
const userController = new UserController();
Then, one source for missing this can be if you only pass an exact reference to the method
setImmediate(userController.createUser);
instead of a way that retains this:
setImmediate(() => {
userController.createUser()
});
In other words, you want to invocate the method using dot notation and involve the class instance.
Your method create is an instance method. It needs to be static for this to work
First, you need to import UserRepository properly before its utilization.
import UserRepository from 'path to UserRepository class';
then
Use self in your function as follows:
async createUser({ body }, res, next) {
const self = this;
try {
const result = await self.model.create(body);
sendSuccessMessage(res, 200, result);
} catch( e ){
next(e)
}
}
I think better to use self as compared to this in your createUser() function.
Related
I'm using Express into a TypeScript project and I have the following situation
This is my route file
...
import findAllUsersFactory from "src/factory/FindAllUsers";
routes.get("/users", findAllUsersFactory().handle);
...
This is the factory where I do a sequence of injections
const findAllUsersFactory = () => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController;
};
This is my Controller
class FindAllUsersController {
constructor(private findUserBusiness: FindAllUsersBusiness) { }
async handle(request: Request, response: Response) {
const allUsers = await this.findUserBusiness.execute();
return response.status(200).send({ allUsers });
}
}
And finally my Business
class FindAllUsersBusiness {
constructor(private usersRepository: IUsersRepository) {}
async execute() {
return this.usersRepository.findAll();
}
}
The problem is that I'm getting an error "Cannot read property 'execute' of undefined" because the findUserBusiness into handle function is undefined. And what I can't understand is that if I change my route to
routes.get("/users", (request, response) => {
findAllUsersFactory().handle(request, response);
});
it works
I've tried to log the functions, but I can say why findUserBusiness is undefined since it came from the constructor, and since the handle functions came from an instance of FindAllUsersController it should have it "defined"
You need to make some adjustments in order to adapt your factory to the way router.get expects its parameters.
const findAllUsersFactory = (req, res) => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController.handle(req, res)
};
Then in your router you need to do the following:
routes.get("/users", findAllUsersFactory);
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;
}
}
I am trying to understand why "this" can be undefined in classes sometimes, and how to fix it:
import { UserDatabase } from './user.database';
export class UserService {
private userDatabase = new UserDatabase();
async login (request: Request, response: Response) {
let {email, password} = request.body;
const user = await this.userDatabase.login(email, password);
...
Error log:
(node:3254) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'userDatabase' of undefined
The usual problem with the this.
You could bind the method to the class context:
export class UserService {
constructor () {
this.login = this.login.bind(this);
}
...
}
Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
I'm new in typescript. In my node-express app, I want to call public function. But this is always undefined, so when I call public function, it throw always error. My code is given below:
app.ts
import * as express from 'express';
import User from './user/ctrl';
class App {
public express: express.Application;
constructor() {
this.express = express();
this.routes();
}
private routes():void {
let router = express.Router();
router.get('/', User.index);
this.express.use('/', router);
}
}
export default new App().express;
./user/ctrl.ts
class User {
public demo:string;
constructor() {
this.demo = "this is text";
}
public infox() {
console.log("demoo test : ", this.demo);
}
public index(req:any, res:any) {
console.log(this) // output: undefined
this.infox(); // throw an error.
}
}
const user = new User();
export default user;
Server run at port 3000.
Any suggestion??
When you passed a reference the User.index function, the this inside it will change based on how it is called. Or when strict mode is on this will be undefined.
Change router.get('/', User.index); to router.get('/', (req, res) => User.index(req, res));. Notice that User.index is wrapped inside an arrow function which captures the correct this when User.index is called.
See red flags for this in TypeScript
Im just learn to use sequelize for my node.js project. For summary my project is ExpressJS+Typescript with Sequelize as ORM and Webpack as module bundler.
Below is my project structure.
src
-router
-server
--config
config.json
--controllers
index.ts
User.ts
--migrations
--models
index.js
user.js
--seeders
App.ts
index.ts
(sorry can not post picture yet, new user to stackoverflow)
I have build some simple router '/user' and expect it should call the user controller and call sequelize method findAll() from my models module, but the result is its error says Cannot read property 'list' of undefined. Below is my code:
models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(module.filename);
const env = process.env.NODE_ENV || 'development';
const config = require(`${__dirname}/../config/config.json`)[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
sequelize = new Sequelize(
config.database, config.username, config.password, config
);
}
fs
.readdirSync(__dirname)
.filter(file =>
(file.indexOf('.') !== 0) &&
(file !== basename) &&
(file.slice(-3) === '.js'))
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
export default db;
models/user.js
export default function(sequelize, DataTypes) {
var user = sequelize.define('user', {
username: DataTypes.STRING,
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
phone: DataTypes.STRING,
wallet: DataTypes.DECIMAL
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
user.hasMany(models.top_up);
}
}
});
return user;
};
controllers/User.ts
let user = require('../models').user;
export default {
list(req, res) {
return user
.findAll()
.then(topUp => res.status(200).send(topUp))
.catch(error => res.status(400).send(error));
}
};
controllers/Index.ts
import users from './User'
export default {
users
}
router/router.ts
import * as express from 'express';
const userController = require('../server/controllers').users;
// Init express router
let router = express.Router();
// Setting API URL
router.get('/', (req, res, next) => {
res.json({
message: 'Hello World!'
});
});
router.get('/about',(req, res, next) => {
res.send('<p>This is about about</p>');
});
router.get('/user', userController.list());
export default router
Fyi, all of my project configuration for start express server, typescript compile and webpack bundle is fine already, and the other route for '/' and '/about' is work fine. I know there is something I'm missing, im still new to sequelize, thanks for help.
TL;DR: The server/controllers/index.ts does not export a binding named users, itexports a binding named default.
You are importing the controllers/Index.ts module using the require function. In a CommonJS environment, this imports the entire module.exports object. As currently transpiled by TypeScript, every named export of the required module is exposed as a property of the import.
An export default clause implies an export named default. As per the ES Module specification, there is a shorthand for importing the default export of a module. That shorthand is
import userController from '../server/controllers';
On the other hand, the syntax
import userController = require('../server/controllers');
or
const userController = require('../server/controllers'); // (or let or var)
imports an object with a property corresponding to each export. In this case it has the shape
{ default }
So if you use require, you need to write
import userController = require('../server/controllers').default;
or
import userController from '../server/controllers';
All ES Module style exports are named, including the default which is named default.
To illustrate this, consider the following, more verbose but semantically identical form
import {default as userController} from '../server/controllers';
If you would prefer to stick with CommonJS style exports, eschewing ES Modules when working in NodeJS, the idiomatic way to export a single object as the entire module (the object returned by require)
You may write
// ../server/controllers/index.ts
export = {
list(req, res) {
return user
.findAll()
.then(topUp => res.status(200).send(topUp))
.catch(error => res.status(400).send(error));
}
};
Personally, I would stick with what you have and write
import userController from '../server/controllers';