Typescript error want to map dto to entity but there is error -> not a function - node.js

Hi I want to make signUp logic with typescript + express + nodeJs + typeORM
If client send request , request.body will be dto.I want to make Dto map to Entity
But my compiler said function is not a function.
const newUser = createUserDto.createUser();
^
TypeError: createUserDto.createUser is not a function
How can I fix it..to map dto to entity?
this is my code
[Controller]
import { Request, Response } from "express";
import { CreateUserDto } from "../service/dto/CreateUserDto";
import userService from "../service/UserService";
export class UserController{
async createUser(request: Request, response: Response) {
const createUserRequest: CreateUserDto = request.body;
userService.createUser(createUserRequest);
response.status(201).json();
}
}
[Service]
import userRepository from "../repository/UserRepository";
import { CreateUserDto } from "../service/dto/CreateUserDto";
export class UserService{
async createUser(createUserDto : CreateUserDto){
const newUser = createUserDto.createUser();
await userRepository.createUser(newUser);
}
}
let userService = new UserService();
export default userService;
[DTO]
import { User } from "../../entity/User"
export class CreateUserDto{
private userId: string;
private password: string;
private email: string;
createUser() {
return User.from(
this.userId,
this.password,
this.email,
);
}
}
I am beginner at typescript so please help me

Related

Unable to access the this context inside the callback in NestJs

import {ApiTags} from "#nestjs/swagger";
import {Body, Controller, Get, Param, Post, Res,Req} from "#nestjs/common";
import {ReceiptService} from "../service/ReceiptService";
import {ReceiptDto} from "../common/domain/ReceiptDto";
import {Response,Request} from 'express';
import {HttpService} from "#nestjs/axios";
#ApiTags('Receipt')
#Controller({version:'1',path:'Receipt'})
export class ReceiptController{
constructor(private readonly receiptService:ReceiptService,private readonly httpService:HttpService) {
}
#Post()
generateReport(#Body() receiptDto:ReceiptDto){
return this.receiptService.getReceiptData(receiptDto.OrderId);
}
#Get(':OrderId')
async displayReceipt(#Param('OrderId') orderId:number,#Res() res:Response,#Req() req:Request){
const data=await this.receiptService.getReceiptData(orderId);
res.render('OrderReceipt',{orderData:data},function (err,html) {
res.send(html).on('finish',async function (){
const result=await this.httpService.get(`http://localhost:51971/pdfgenerator?url=localhost:4200/v1/Receipt/${orderId}`).bind(this);
console.log(result);
});
});
}
}
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'get' of undefined Error has been thrown on this.httpService.get() line. I don't know how to solve this problem. Thanks and regards.
Change the function in the res.render callback to an arrow function to preserve the this context of the class.
#ApiTags('Receipt')
#Controller({version:'1',path:'Receipt'})
export class ReceiptController{
constructor(private readonly receiptService:ReceiptService,private readonly httpService:HttpService) {
}
#Post()
generateReport(#Body() receiptDto:ReceiptDto){
return this.receiptService.getReceiptData(receiptDto.OrderId);
}
#Get(':OrderId')
async displayReceipt(#Param('OrderId') orderId:number,#Res() res:Response,#Req() req:Request){
const data=await this.receiptService.getReceiptData(orderId);
res.render('OrderReceipt',{orderData:data}, (err,html) => {
res.send(html).on('finish',async () => {
const result=await this.httpService.get(`http://localhost:51971/pdfgenerator?url=localhost:4200/v1/Receipt/${orderId}`).bind(this);
console.log(result);
});
});
}
}

What is the best way for approaching dynamic Auth Guards?

I'm trying to package my own AuthGuard for use in other projects and need to pass it a string before use.
Because I saw the Passport auth guard use a function that wrapped around a new class I've done the same...
export const AnchorAuthGuard = (rpc?: string): Type<CanActivate> => {
class AuthGuard implements CanActivate {
rpc = rpc || "https://eos.greymass.com";
async canActivate(context: ExecutionContext): Promise<boolean> {
const [req] = context.getArgs();
const { body } = req as { body: ProofPayload };
const proof = IdentityProof.from(body.proof);
const client = new APIClient({
provider: new AxiosAPIProvider(this.rpc),
});
const accountName = proof.signer.actor;
const account = await client.v1.chain.get_account(accountName);
const auth = account.getPermission(proof.signer.permission).required_auth;
const valid = proof.verify(auth, account.head_block_time);
if (valid) {
req.anchor = {
account: {
actor: proof.signer.actor.toString(),
permission: proof.signer.permission.toString(),
},
object: account.toJSON(),
};
return true;
} else {
return false;
}
}
}
return AuthGuard;
};
However, now that I've packaged this up and extending the Guard with extends for some more functionality in a projhect I'm consuming the library in I'm having trouble figuring out how to enter the rpc parameter via configService from the ConfigModule and now feel like I'm not using the best practices here and that there's a better way from the start.
Any ideas?
I am not sure if I understood you correctly but to modify AuthGuard you must extend AuthGuard class and write over canActivate method.
#Injectable()
export class LoginGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector, private config: ConfigService) {
super();
}
canActivate(context: ExecutionContext) {
return super.canActivate(context); // this is necessary due to possibly returning `boolean | Promise<boolean> | Observable<boolean>
}
}

TypeScript in Node with Express. Cannot set a controller's class property with a constructor

I have a Node app with Express implementing TypeScript using Babel.
I tried creating a UserController with a private property _user: User and set it in the class constructor. The problem is, I keep getting "message": "Cannot read property '_user' of undefined" in the output.
This is my class:
export class UserController extends CrudController {
private _user: User;
constructor() {
super();
const user = new User();
user.id = 1;
user.email = 'johnappleseed#apple.com';
user.firstName = 'John';
user.lastName = 'Appleseed';
this._user = user; // Setting the global class property
}
get(req: Request, res: Response) {
res.json({ user: this._user });
}
}
The extended class CrudController is just an abstract class, but still, here's the code:
export abstract class CrudController {
public abstract get(req: Request, res: Response): void;
}
This is my routes file:
const userController = new UserController();
router.route('/user')
.get(userController.get);
What could be the issue? Thanks!
Since your passing userController.get to the router, you will loose the this-context of the class, once the router is invoked.
You can solve this by binding the method to userController and thus setting this to the controller (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind for more information):
const userController = new UserController();
router.route('/user')
.get(userController.get.bind(userController);

Struggling to save an entity in OneToMany/ManyToOne relation using NestJS/TypeORM

Bookstore exercise. A Book can be assigned a single Genre
Trying to assign a new book with the genreId supplied in the DTO.
BookRepository
#EntityRepository(Book)
export class BookRepository extends Repository<Book> {
constructor(
private genreService: GenresService,
) {
super();
}
async createBook(createBookDto: CreateBookDto): Promise<Book> {
const genre = await this.genreService.getOne(createBookDto.genreId);
const newBook = this.create({
...createBookDto,
genre,
});
return newBook.save();
}
}
GenreService
#Injectable()
export class GenresService {
constructor(
#InjectRepository(GenreRepository) private readonly genreRepository: GenreRepository,
) {}
async getOne(id: number): Promise<Genre> {
return this.getById(id);
}
private async getById(id: number): Promise<Genre> {
const found = await this.genreRepository.findOne(id);
if (!found) {
throw new NotFoundException(`Genre with id ${id} not found.`);
}
return found;
}
}
BookRepository and GenreRepository are imported together by the BookstoreModule, like so:
imports: [
TypeOrmModule.forFeature([
GenreRepository,
BookRepository,
AuthorRepository,
]),
// ...etc
},
NestJS spits out the following error:
[ExceptionsHandler] this.genreService.getOne is not a function +1045981ms
TypeError: this.genreService.getOne is not a function
at BookRepository.createBook (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/book.repository.js:21:47)
at BooksService.createBook (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/books.service.js:29:36)
at BooksController.create (/Users/artur/Code/Sandbox/books-nest/dist/bookstore/books/books.controller.js:31:33)
Tried to inject GenreRepository into theBookRepository`
constructor(
#InjectRepository(GenreRepository) private genreRepository: GenreRepository,
){
super();
}
and .findOne() from there, the error was:
No metadata for "2" was found. +26884ms
EntityMetadataNotFound: No metadata for "2" was found.
(2 being the id for genreId)
Not sure if my approach to saving is correct. Maybe the idea of finding the genre from within BookRepository is wrong and it should be solved in a different way. If so, how?
To me the createBook method should be lying within the BookService, BookService needs to inject GenreService and then you should call the genreService.getOne(createBookDto.genreId) from within the BookService.
It makes more sense to let the services handle the business logic / orchestration of the data flow imo.
I'd suggest to give it a try try with the following code:
BookService
#Injectable()
export class BookService {
constructor(
#InjectRepository(BookRepository) private readonly bookRepository: BookRepository,
private genreService: GenreService // <= here you go, you inject the genreService into the bookService to take advantage of its methods
) {}
async getOne(id: number): Promise<Genre> {
return this.getById(id);
}
async createBook(createBookDto: CreateBookDto): Promise<Book> {
const genre = await this.genreService.getOne(createBookDto.genreId);
const newBook = this.create({
...createBookDto,
genre,
});
return newBook.save();
}
private async getById(id: number): Promise<Genre> {
const found = await this.bookRepository.findOne(id);
if (!found) {
throw new NotFoundException(`Book with id ${id} not found.`);
}
return found;
}
}
BookRepository
#EntityRepository(Book)
export class BookRepository extends Repository<Book> {}
GenresService
#Injectable()
export class GenresService {
constructor(
#InjectRepository(GenreRepository) private readonly genreRepository: GenreRepository,
) {}
async getOne(id: number): Promise<Genre> {
return this.getById(id);
}
private async getById(id: number): Promise<Genre> {
const found = await this.genreRepository.findOne(id);
if (!found) {
throw new NotFoundException(`Genre with id ${id} not found.`);
}
return found;
}
}
GenreRepository
#EntityRepository(Genre)
export class GenreRepository extends Repository<Genre> {}
Edit DTO comment
CreateBookDto
import { Exclude, Expose } from 'class-transformer';
import { IsNumber, IsString, IsDate } from 'class-validator';
#Exclude()
export class CreateBookDto {
#Expose()
#IsNumber()
genreId: number;
#Expose()
#IsString()
title: string;
#Expose()
#IsString()
author: string;
#Expose()
#IsDate()
date_of_publication: Date;
// rest of informations....
}
Note that we're using class-transformer along with class-validator in order to instantiate and validate a proper CreateBookDto. CreateBookDto can be instantiated at the controller level if you're using validation pipe
Let me know if it helps :)

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