Node.js Express API with TypeScript 3 Update a record - node.js

I have setup Node.js Express API with TypeScript 3 and it is working fine.
I got an issue when I try to update the record.
RecordsRouter.ts
import { Router } from 'express';
import {RecordComponent} from '../../components';
const router: Router = Router();
router.get('/', RecordComponent.findAll);
router.get('/:id', RecordComponent.findOne);
router.post('/', RecordComponent.create);
router.delete('/:id', RecordComponent.remove);
router.put('/:id', RecordComponent.update);
export default router;
My RecordComponent.ts
export async function update(req: Request, res: Response, next: NextFunction): Promise <void> {
try {
const record: IRecord = await RecordService.put(req.body)
res.status(201).json(record);
} catch (error) {
next(new HttpError(error.message.status, error.message));
}
}
and my IRepository.ts
export interface IRepository<T> {
findAll(): Promise<T[]>;
findOne(code: string): Promise<T>;
insert(T: any): Promise<T>;
remove(id: string): Promise<T>;
put:(T: any)=>Promise<T>;
}
Service.ts
async put(data: IRecord): Promise {
try {
const validate: Joi.ValidationResult = RecordValidation.updateRecord(data);
if(validate.error) {
throw new Error(validate.error.message);
}
return await RecordModel.findOneAndUpdate(data);
} catch (error) {
throw new Error(error.message);
}
},
Did I did all correctly or something is missing because I am getting the error

That means you didn't implement all of the interface members in RecordService. Either implement them or mark them as optional in the IRepository interface by adding a question mark before the colon:
export interface IRepository<T> {
findAll()?: Promise<T[]>;
findOne(code: string)?: Promise<T>;
insert(T: any)?: Promise<T>;
remove(id: string)?: Promise<T>;
put(id: string)?: Promise<T>;
}

So, You should implement RecordService in next way:
const RecordService:IRepository<IRecord>={
// ...some code for findAll, findOne ....
remove:(id: string)=>Promise.resolve(),
put:(id: string)=>Promise.resolve(),
}
UPDATE
Your RecordModel.findOneAndUpdate(data) should receive 0 arguments:
RecordModel.findOneAndUpdate() or RecordModel.findOneAndUpdate(data, other, another)
Please get familiar with mongoose docs

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);
});
});
}
}

Assign route dynamically Node/Express

I need dynamically assign a new route but it for some reason refuses to work.
When I send a request in the Postman it just keeps waiting for a response
The whole picture of what I am doing is the following:
I've got a controller with a decorator on one of its methods
#Controller()
export class Test {
#RESTful({
endpoint: '/product/test',
method: 'post',
})
async testMe() {
return {
type: 'hi'
}
}
}
export function RESTful({ endpoint, method, version }: { endpoint: string, version?: string, method: HTTPMethodTypes }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
const originalMethod = descriptor.value
Reflect.defineMetadata(propertyKey, {
endpoint,
method,
propertyKey,
version
}, target)
return originalMethod
}
}
export function Controller() {
return function (constructor: any) {
const methods = Object.getOwnPropertyNames(constructor.prototype)
Container.set(constructor)
for (let action of methods) {
const route: RESTfulRoute = Reflect.getMetadata(action, constructor.prototype)
if (route) {
const version: string = route.version ? `/${route.version}` : '/v1'
Container.get(Express).injectRoute((instance: Application) => {
instance[route.method](`/api${version}${route.endpoint}`, async () => {
return await Reflect.getOwnPropertyDescriptor(constructor, route.propertyKey)
// return await constructor.prototype[route.propertyKey](req, res)
})
})
}
}
}
}
Is it possible to dynamically set the route in the way?
I mainly use GraphQL but sometimes I need RESTful API too. So, I want to solve this by that decorator
In order for the response to finish, there must be a res.end() or res.json(...) or similar. But I cannot see that anywhere in your code.

Angular2 - Handling API Response

Good afternoon! I'm new in Angular 2, so I'm sorry in advance if my question is generic. I cannot figure out how to handle an API response.
My NodeJS Server API function is (Checked and works fine):
router.get('/appointment/:iatreio/:time', function(req, res, next) {
var paramIatreio = req.params.iatreio;
var paramTime = req.params.time;
db.appointments.findOne({iatreio: paramIatreio, time: req.params.time}, function(err, resultFound) {
if (err) { res.send(err); }
if (resultFound) {
res.json(true); // 1st Question: For best practice, res.json(true) or res.send(true)?
} else {
res.json(false);
}
});
});
My Angular2 Service:
import { Injectable } from '#angular/core';
import { Headers , Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class AppointmentService {
constructor(private http: Http) { }
isBooked(iatreio: string, time: string): Observable<boolean> {
return this.http
.get('http://localhost:3000/appointment/'+iatreio+'/'+time)
.map(); //2nd Question: What inside map()?
}
} // end of Service
Component Function
isBooked(selectedIatreio: string, selectedTime: string): boolean {
this.appointmentService
.isBooked(selectedIatreio, selectedTime)
.subscribe(() => {}); //3rd Question: What inside subscribe()?
}
My final goal is the "isBooked(...)" function of my Component to be called and to return true or false. I have seen the code in the examples in the Angular2 site, but I'm a little confused on my case.
Can Service function return directly a true or false value or it has to be an Observable?? Map() function is necessary??
Generally, my thinking is right?? Or my goal can be accomplished more easily??
Thank you a lot for your time!!
map is used to convert the response into the model which you look for
isBooked(iatreio: string, time: string): Observable<boolean> {
return this.http
.get('http://localhost:3000/appointment/'+iatreio+'/'+time)
.map((response)=><boolean>response.json());
}
subscribe will return the data emitted by the service
isBooked(selectedIatreio: string, selectedTime: string): boolean {
this.appointmentService
.isBooked(selectedIatreio, selectedTime)
.subscribe((data) => {
//your operation
console.log(data);
});
}

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