I want to reuse all my servise and controllers and I was achieving this with extending class.
I'm calling the class of Controller and service all the way up from a router.
everything looks fine while creating instance of class,
but when I send request to sign in route it says there is no this.servise or this.dto which is i passed to the constructor while creating this instance of object
import express from "express";
const router = express.Router();
import UserController from "../../controllers/user.controller";
import UserService from "../../services/user.service.js";
import {UserDto} from "../../dtos/user.dto.js";
import User from "../../models/User.js";
const userService = new UserService(User);
console.log(userService, UserDto); // successfully logged
const userController = new UserController(userService, UserDto);
console.log(userController); // successfully logged
router.post('/signup', userController.signup);
router.post('/signin', userController.signIn);
router.post('/signout', userController.signOut);
router.get('/all', userController.getAll);
router.route("/:id")
.get(userController.get)
.post(userController.create)
.patch(userController.update)
.delete(userController.delete);
export default router;
export default class UserController extends MainController {
async signIn(req, res) {
try {
const {email, password} = req.body;//await this.dto.login(req.body);
const result = await this.service.signIn(email, password); // TypeError: Cannot read properties of undefined (reading 'service')
return res.status(201).json({message: "successfully signed in", token: result.token, user: this.dto.output(result.user)});
}
catch(err){
sendError(res, err);
}
}
I've reviewed my knowledge of how nodejs modules work, but I can't figure out what the problem is.
Who can explain me this situation, please!?
Thank you in advance!
You forgot to use .bind() when passing the references to the methods to the Express router. So who knows what the value of this will be when Express tries to call them (in JS, this doesn't work like other variable names, it doesn't use lexical scoping, what the value will be depends on the calling code).
Change your code to e.g.
router.post('/signup', userController.signup.bind(userController));
This will ensure that this is set to your userController object when the function gets called (bind() is a method available on all function objects that returns a new function with a fixed this value).
Related
I exported two functions (registerUser and loginUser) as default in my controller, it didn't throw any error at the controller file but when I try getting the routes it throws this error - Property 'registerUser' does not exist on type 'typeof UserController'
My userRoutes Code is as shown below:
userRoutes.ts
here is an image of my userRoutes code
I wanted to export two functions as default, so I used class constructor
My userController code is as shown below:
userController.ts
here is an image of userController code
So apparently, I didn't construct the class in my userRoute file.
wrong - const { registerUser, loginUser } = userController
Correct - const { registerUser, loginUser } = new userController()
I have a nestjs app that has an AuthService which has these parts:
export class AuthService {
constructor(
#InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async updateFromInternally () {
...
}
I have another file which is, crucially, outside of any module, which contains a number of helpful functions relating to Google oauth. For example, this file initiates Google's oauth2 client like so:
export const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_CLIENT_REDIRECT
);
This file also has a listener function which I found in Google's documentation as a way to catch when my use of Google's oauth2 client automatically uses a refresh token to obtain a new access token:
oauth2Client.on('tokens', async (tokens) => {
[****]
})
At [****], I need to query in my database for a particular user and update them. Either of these conceptually work:
I somehow get userRepo into this file right here and use it to query + update
I somehow call updateFromInternally in AuthService from here
But I don't know how to interact with either TypeORM repositories or methods within services from outside of any module in nestjs! Can I do either of these?
The first question is why you're using nestjs?
You can access the internals of nestjs by using the instantiated app;
In your main.ts file you have something like this:
const app = await NestFactory.create(AppModule);
You can access the services or DataSource from the app by using the get method.
import {DataSource} from 'typeorm'
const dataSource = app.get<DataSource>(DataSource)
// or custom service
const someServices = app.get<SomeService>(SomeService)
you just need a way to export app from main.ts and import it in your outside world.
for example:
// main.ts
let app;
async bootstrap() {
app = await NestFactory.create(AppModule);
}
export const getApp = () => app;
bootstrap()
and in your other file
// outside.ts
import {getApp} from './main.ts'
const app = getApp()
I didn't write the whole logic, but I think it would give you an idea of what you need to do.
But in my opinion, it's the worst thing you can do. You're using 'nestjs` and try to respect its philosophy.
Just write a module that handles all the work you want.
I figured out #1:
import {getRepository} from "typeorm";
oauth2Client.on('tokens', async (tokens) => {
const gaRepo = getRepository(Googleauth);
// Can now use gaRepo like you do in a service
})
I have an express.js application that uses the express.Router() to connect my endpoints to controllers.
My goal is to have an object newed up in the controller constructor so I can use it in all controller functions without having to new it up in each one.
The constructor runs correct, and the object is available within the constructor. But whenever I call any actions of the controller, the object is null.
Here is the router
const express = require('express');
const componentController = require('../controllers/component');
const router = express.Router();
// component routes
router.get('/components', componentController.getComponents);
module.exports = router;
And here is my controller.
const LogService = require('../services/logService');
class ComponentController {
constructor() {
this.logger = new LogService('ComponentController');
this.logger.logDebug('test1','test1');
}
async getComponents(req, res) {
const test = new LogService('ComponentController');
test.logDebug('test2','test2');
this.logger.logDebug('test3','test3')
res.json('');
}
}
module.exports = new ComponentController();
I want the LogService to be available in the controller actions. The first two logs work correctly, test1 and test2. But test3 throws an error saying logger is undefined.
Why is this.logger undefined in later functions? How can I fix this issue?
try to refactor getComponents to an arrow function.
Here is why: https://javascript.plainenglish.io/this-binding-in-es6-arrow-function-70d80e216238
You can also do this:
router.get('/components', componentController.getComponents.bind(componentController));
I will show you an example of what i'm trying to do :
server.ts
export class ExpressServer {
public readonly app: express.Application;
public readonly lbApp: ImportedApp;
private server?: Server;
constructor(options: ApplicationConfig = {}) {
this.app = express();
this.lbApp = new ImportedApp(options);
this.app.get('/hello', async function (_req: Request, res: Response) {
//Here i'd like to call a model like User.findById() but can't figure out how to do it..
});
}
}
As you see in the comment i'm trying to access my models method to use them in my route (Like showing users informations on my view) But can't figure out how to do it. I'v already tryed to import the DataSource, the model, the controller but nothing's containing my methods (FindById, Create etc..)
If i find nothing i will have to use something like Axios or Request to request the ressource from the api instead of inside my code like await request('api/users/myusername)
In LoopBack 4, we use Repository design patter for accessing data. In order to find a user instance by its id, you need to obtain an instance of UserRepository via dependency injection. Quoting from https://loopback.io/doc/en/lb4/Repository.html:
Repositories are adding behavior to Models. Models describe the shape of data, Repositories provide behavior like CRUD operations. This is different from LoopBack 3.x where models implement behavior too.
UPDATED SOLUTION
To obtain an instance of a Repository class, you can use the Service Locator design pattern and get the instance from the per-request Context object provided by LoopBack's REST layer.
import {MIDDLEWARE_CONTEXT, RequestContext} from '#loopback/rest';
import {UserRepository} from '../repositories';
function expressHandler(req, res, next) {
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
const userRepo = await ctx.get<UserRepository>('repositories.UserRepository');
const users = await userRepo.find({limit: 10});
// render your view
}
We are discussing how to make this use case easier to implement in GitHub pull request loopback-next#6793, feel free to join the discussion there.
ORIGINAL ANSWER
Instead of writing an Express route for your rendered pages, I recommend you to write a LoopBack 4 Controller instead; and inject Express Response object to allow you to render the HTML view, as explained in https://loopback.io/doc/en/lb4/Accessing-http-request-response.html#inject-http-response
import {Response, RestBindings, oas} from '#loopback/rest';
import {inject} from '#loopback/core';
import {UserRepository} from '../repositories';
export class PingController {
constructor(
#inject(RestBindings.Http.RESPONSE)
private response: Response
#repository(UserRepository)
public userRepository: UserRepository,
) {}
// Hide this endpoint from OpenAPI spec generated for the app
#oas.visibility('undocumented')
#get('/users')
list(): Response {
// Access User data via this.userRepository API
const users = await this.userRepository.find({limit: 10});
// Access the response object via `this.response`
this.response.render('users', {users});
// Return the HTTP response object so that LoopBack framework skips the
// generation of HTTP response
return this.response;
}
}
Having said that, if you already know how to access DataSource instances from your LB4 app in your Express routes, then you can instantiate Repository classes manually from your routes too:
const db = // your datasource
this.app.get('/hello', async function (_req: Request, res: Response) {
const repo = new UserRepository(db);
const users = await this.userRepository.find({limit: 10});
});
To me the solution is not working. Started from the express-composition example, i just need to access lb repositories from a generic express route outside of the lb4 request handler:
constructor(options: ApplicationConfig = {}) {
this.app = express();
this.lbApp = new NoteApplication(options);
this.lbApp.basePath('')
// Expose the front-end assets via Express, not as LB4 route
this.app.use('/api', this.lbApp.requestHandler);
this.app.get('/hello', async (req: Request, res: Response) => {
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
const userRepo = await ctx.get('repositories.UserRepository');
res.send('Hello world!');
});
}
the ctx in the line
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
is always undefined.
My main goal is to have routes not under /api that can still access lb4 repositories.
I'm completely new to node and it's frameworks Koa and express. I've a mongoose model called Drawing and a router module for that.
Problem is with express routers I was able the get the data from database using Drawing.find method but with Koa, control is not even going into Drawing.find. And I'm not able to get the data at all. Please find the following related code and help me understand the things better.
This is my router module
import * as Router from "koa-router";
import Drawing from "../../models/drawing";
function getRoutesForDrawing(): Router {
console.log("Inside getRoutes for drawing");
let route = new Router();
route.get("/drawing", function(context,next) {
console.log("Inside /drawing");
Drawing.find(function(err,drawings) {
console.log("Not gettig executed");
context.body = "Welcome";
});
//context.body = "Welcome";
});
}
export default getRoutesForDrawing();
And the model is
import mongoose = require("mongoose");
export interface IDrawing extends mongoose.Document {
drawingId:Number,
drawingName:String,
updatedOn:Date,
updatedBy:Number
};
export const DrawingSchema = new mongoose.Schema({
drawingId:Number,
drawingName:String,
updatedOn:Date,
updatedBy:Number
});
const Drawing = mongoose.model<IDrawing>('Drawing', DrawingSchema);
export default Drawing;
As you can see in my router module, the control is actually coming for /drawing and it's printing in console "Inside /drawing" but then control isn't coming to Drawing.find. I'm getting difficulty in understanding this.
It's a little bit hard to figure out what's going on because it looks like you have problems all over the place. Let me point out the things that stand out:
getRoutesForDrawing is declared to return a router and doesn't return anything
Koa routes are not like express. In particular they are not callback based. They take either generator functions (Koa 1.x) or async functions (Koa 2.x). You seem to expect that it's wanting a callback function which won't work. Assuming koa 2.x, its router.get('/drawing', async(context) => {...});
Assuming koa 2.x, you need to await the result of the mongoose methods, e.g. context.body = await Drawing.find({})