How to pool postgresql connections in nodejs with facade design pattern? - node.js

Hello i am writing simple web application using design similar to facade design pattern. Application is written in Typescript using nodejs, expressjs, node-postres and inversify. Let say i have this simple example
Router.ts
router.get('/test', testController.test);
TestController.ts
import { Request, Response } from 'express';
import { ITestUC } from '../usecase/TestUC';
import { di } from '../core/Di';
import { TYPES } from '../core/Types';
class TestController {
public async test(req: Request, res: Response, next: Function) {
const uc = di.get<ITestUC>(TYPES.ITestUC);
await uc.run();
res.send({ data:1 });
}
}
export const testController = new TestController();
TestUC.ts
import "reflect-metadata";
import { injectable, interfaces } from "inversify";
import { di } from "../core/Di";
import { TYPES } from "../core/Types";
import { ITestManager1 } from "../library/Test/TestManager1";
import { ITestManager2 } from "../library/Test/TestManager2";
import { PoolClient } from "pg";
import { PostgresClient, IPostgresClient } from "../core/PostgresClient";
import { IPostgresPool } from "../core/PostgresPool";
function db(transaction: boolean) {
return (target: any, property: string, descriptor: TypedPropertyDescriptor<() => void>) => {
const fn = descriptor.value;
if(!fn) return;
descriptor.value = async function (){
let poolClient: PoolClient,
postgresClient: PostgresClient = new PostgresClient();
try {
poolClient = await di.get<IPostgresPool>(TYPES.IPostgresPool).pool.connect();
postgresClient.set(poolClient);
di.rebind<IPostgresClient>(TYPES.IPostgresClient).toDynamicValue((context: interfaces.Context) => { return postgresClient });
if (transaction) postgresClient.begin();
await fn.apply(this);
if (transaction) postgresClient.commit();
} catch (e) {
if (transaction) postgresClient.rollback();
throw e;
} finally {
postgresClient.get().release();
}
}
}
}
#injectable()
export class TestUC implements ITestUC {
#db(true)
public async run(): Promise<void> {
const manager1 = await di.get<ITestManager1>(TYPES.ITestManager1);
manager1.test1('m1');
const manager2 = await di.get<ITestManager2>(TYPES.ITestManager2);
manager2.test1('m2');
}
}
export interface ITestUC {
run(): Promise<void>
}
TestManager1.ts
import { injectable, inject} from "inversify";
import "reflect-metadata";
import { TYPES } from "../../core/Types";
import { ITestSql1 } from "./TestSql1";
#injectable()
export class TestManager1 implements ITestManager1 {
#inject(TYPES.ITestSql1) private sql: ITestSql1;
public async test1(value: string) {
await this.sql.test1(value);
}
}
export interface ITestManager1 {
test1(value: string)
}
TestSql1.ts
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { IPostgresClient } from "../../core/PostgresClient";
import { TYPES } from "../../core/Types";
#injectable()
export class TestSql1 implements ITestSql1{
#inject(TYPES.IPostgresClient) db: IPostgresClient;
public async test1(value: string) {
const query = {
name: 'insert-test',
text: `
INSERT INTO pr.test (
process,
operation,
key
) VALUES (
$1,
$2,
$3
)`,
values: [
this.db.get()['processID'],
1,
value
]
};
await this.db.get().query(query);
}
}
export interface ITestSql1 {
test1(value: string)
}
PostgresClient.ts
import { PoolClient } from "pg";
export class PostgresClient implements IPostgresClient {
private client: PoolClient;
get(): PoolClient {
return this.client;
}
set(client: PoolClient) {
this.client = client;
}
async begin() {
await this.client.query('BEGIN');
}
async commit() {
await this.client.query('COMMIT');
}
async rollback() {
await this.client.query('ROLLBACK');
}
}
export interface IPostgresClient {
get(): PoolClient;
set(client: PoolClient);
commit();
rollback();
begin();
}
TestManager2.ts and TestSql2.ts are basically same as TestManager1.ts and TestSql1.ts
My problem is that every request seems to use only one same postgresql connection from pool (Tested with JMeter) and serialize all api request.
Pool doesn't even create other connections to postgresql. It looks like other requests waits for previous request end or postgresql connection release.
How to instantiate one connection (transaction) for every request using node-postgres pool and at the same time don't block other requests?
Is this code blocking? Or i misunderstood somthing in documentation? Or simply this design isn't suitable for nodejs? I really don't now and stuck for week.

Related

How to Extract a Value from microservice returned Observable in NestJS?

I am trying to implement NestJS Microservice to Validate my Bearer Token:
Whenever I get TCP response from Microservice which decides whether token is valid or not, it return Observable object, I am unable to extract the response from that Observable, please help me. Not getting proper way to extract the value from Observable in Nestjs, already tried lastValueFrom from RXJS
Here is the Controller to Make a call to Microservice(consumer/caller of microservice):
app.controller.ts
import { Body, Controller, Get, Post } from '#nestjs/common';
import { AppService } from './app.service';
import { PayloadDTO} from './payload.dto';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): string {
return this.appService.getHello();
}
#Post()
createUserData(#Body() payload: PayloadDTO) {
const token = header.authorization.replace('Bearer ', '')
const isValidToken = this.appService.validateToken(token);
}
if(isValidToken) {
this.appService.CreateRecord(Payload : PayloadDTO)
} else {
//Throw New UnathorizedException
}
}
app.service.ts
import { Inject, Injectable } from '#nestjs/common';
import { ClientProxy } from '#nestjs/microservices';
import { PayloadDTO } from './payload.dto';
#Injectable()
export class AppService {
constructor(
#Inject('COMMUNICATION') private readonly commClient: ClientProxy
) {}
// service method to call and validate token
validateToken(token: String) {
//calling microservice by sending token
const checkToken: Observable = this.commClient.send({cmd:'validate_token'},token);
//Unable to extract value from Observable here, it is returning observable object
return extractedResponse;
}
// service method to create record
createRecord(payload: PayloadDTO) {
//code related to creating new Record
}
}
app.controller.ts - Microservice Project
import { Controller, Get } from '#nestjs/common';
import { MessagePattern } from '#nestjs/microservices';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#MessagePattern({ cmd: 'validate_token' })
validateToken() {
return this.appService.checkToken();
}
}
Finally I got the answer on my own:
In Microservice Consumer/Caller app.controller.ts I forgot to use await before calling this.appService.validateToke(token)
#Post()
async createUserData(#Body() payload: PayloadDTO) {
const token = header.authorization.replace('Bearer ', '')
const isValidToken = await this.appService.validateToken(token);
}
To convert/extract value from Observable:
In app.service.ts (consumer/caller of microservice) :
use async before validateToken
use await lastValueFrom(checkToken)
return resp
// service method to call and validate token
async validateToken(token: String) {
//calling microservice by sending token
const checkToken = this.commClient.send({cmd:'validate_token'},token);
const resp = await lastValueFrom(checkToken);
reutn resp;
}

How to inject repository into nestjs-rate-limiter guard

I've tried to inject a repository into the guard which extends from RateLimiterGuard nestjs-rate-limiter but I got an error when call super.canActivate(ctx). It said that this.reflector.get is not a function. Is there any mistake that I have?
Here is my code:
import { RateLimiterGuard, RateLimiterOptions } from 'nestjs-rate-limiter';
import type { Request } from 'express';
import config from 'config';
import { ExecutionContext, Injectable } from '#nestjs/common';
import { MockAccountRepository } from '../modules/mock/mock-accounts/accounts-mock.repository';
import { Reflector } from '#nestjs/core';
import { UserIdentifierType } from 'src/modules/users/user.types';
const ipHeader = config.get<string>('server.ipHeader');
#Injectable()
export class ForwardedIpAddressRateLimiterGuard extends RateLimiterGuard {
constructor(
reflector: Reflector,
options: RateLimiterOptions,
private readonly mockAccRepo: MockAccountRepository,
) {
super(options, reflector);
}
protected getIpFromRequest(request: Request): string {
return request.get(ipHeader) || request.ip;
}
async canActivate(ctx: ExecutionContext): Promise<boolean> {
const req = ctx.switchToHttp().getRequest();
const phoneNumber = req.body?.phoneNumber || req.params?.phoneNumber;
// If mock phone number, always allow
if (
await this.mockAccRepo.findOne({
identifier: phoneNumber,
identifierType: UserIdentifierType.PHONE_NUMBER,
})
) {
return true;
}
// Otherwise, apply rate limiting
return super.canActivate(ctx);
}
}

Where is the beest place to put prisma middleware on NestJs?

My prisma.service.ts looks like this:
#Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
According to Prisma docs I am supposed to put them outside the context of the request handler. That would be the app that I create on main.ts. Putting a middleware there before the app itself is defined looks wierd to me and doesn't work. I'd prefer to put it on the prisma.service.ts file itself
Not sure whether this is the "best" place to register them, but we do it in the constructor of the service along with the logging configuration and it works:
import { INestApplication, Injectable, Logger, OnModuleInit } from "#nestjs/common";
import { Prisma, PrismaClient } from "#prisma/client";
import { ConcurrencyErrorMiddleware } from "./concurrency-error.middleware";
#Injectable()
export class PrismaService extends PrismaClient<Prisma.PrismaClientOptions, "query"> implements OnModuleInit {
private readonly logger = new Logger(PrismaService.name);
constructor() {
super({ log: [{ emit: "event", level: "query" }] });
this.logger.log(`Prisma v${Prisma.prismaVersion.client}`);
this.$on("query", (e) => this.logger.debug(`${e.query} ${e.params}`));
this.$use(ConcurrencyErrorMiddleware());
}
async onModuleInit(): Promise<void> {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication): Promise<void> {
this.$on("beforeExit", async () => {
await app.close();
});
}
}
// An example of such a middleware.
import { Prisma } from "#prisma/client";
export function ConcurrencyErrorMiddleware<T extends Prisma.BatchPayload = Prisma.BatchPayload>(): Prisma.Middleware {
return async (params: Prisma.MiddlewareParams, next: (params: Prisma.MiddlewareParams) => Promise<T>): Promise<T> => {
const result = await next(params);
if (
(params.action === "updateMany" || params.action === "deleteMany") &&
params.args.where.version &&
result.count === 0
) {
throw new ConcurrencyError();
}
return result;
};
}

Is there a way to insert current user into fields in base entity before inserting data

How to to insert current logged in user to createdBy & lastChangedBy fields after creating/updating entity?
In my BaseEntity i've tried
#BeforeInsert()
async insertUser(#GetAuthUserPayload() userPayload: User) {
const user = await this.usersService.findOne({
where: { username: userPayload.username },
});
this.createdBy = user;
this.lastChangedBy = user;
}
But i've found out decorators work only in controllers(in entity they return undefined). Is there any other way than updating DTO in controller or using session?
Since i am using #nestjsx/crud i haven't found any other method than updating DTO. I've managed to solve this issue by creating BaseService:
import { TypeOrmCrudService } from '#nestjsx/crud-typeorm';
import { InjectRepository } from '#nestjs/typeorm';
import { Inject, Injectable, Scope, Type } from '#nestjs/common';
import { CrudRequest, Override } from '#nestjsx/crud';
import { DeepPartial } from 'typeorm';
import { REQUEST } from '#nestjs/core';
import { User } from '../users/entities/user.entity';
export interface IBaseService<T> {}
type Constructor<I> = new (...args: any[]) => I;
export function BaseService<T>(entity: Constructor<T>): Type<IBaseService<T>> {
#Injectable({
scope: Scope.REQUEST,
})
class BaseServiceHost extends TypeOrmCrudService<T> implements IBaseService<T> {
constructor(#InjectRepository(entity) repo, #Inject(REQUEST) readonly request: any) {
super(repo);
}
#Override()
createOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.createOne(req, this.addCreatedByToDTO(dto));
}
#Override()
replaceOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.replaceOne(req, this.addLastChangedByToDTO(dto));
}
#Override()
updateOne(req: CrudRequest, dto: DeepPartial<T>): Promise<T> {
return super.updateOne(req, this.addLastChangedByToDTO(dto));
}
private addCreatedByToDTO(dto: DeepPartial<T>): DeepPartial<T> {
const userUUID: Partial<User> = this.request.user.userUUID;
return { ...dto, createdBy: userUUID };
}
private addLastChangedByToDTO(dto: DeepPartial<T>): DeepPartial<T> {
const userUUID: Partial<User> = this.request.user.userUUID;
return { ...dto, lastChangedBy: userUUID };
}
}
return BaseServiceHost;
}
Later on i just extend my service like:
#Injectable()
export class ExampleService extends BaseService(ExampleEntity) {}

Apply an interface in Typescript

I am creating a project with Node JS and Typescript, I have a class and an interface where I type all the data. What I need to know is how to apply it to the classroom.
This is my interface:
export interface IController {
url: string,
api: string
}
This is my class where I want to apply it:
import { Request, Response } from 'express';
import { constUtils } from '../utils/const.utils';
import { IController } from '../utils/model.utils';
import { utilsController } from '../utils/index.utils';
class IndexController {
public async index(req: Request, res: Response): Promise<IController> {
try {
let api = req.query.api;
let constUt = new constUtils();
let url = constUt.conf.API_MOCS[`${api}`].url;
await utilsController.utilsCsvConverter(api, url);
} catch (error) {
console.log(error);
}
}
}
export const indexController = new IndexController();
This assumes that the local variables url and api should be returned in a Promise resolving to an object specified the by IController interface:
import { Request, Response } from 'express';
import { constUtils } from '../utils/const.utils';
import { IController } from '../utils/model.utils';
import { utilsController } from '../utils/index.utils';
class IndexController {
public async index(req: Request, res: Response): Promise<IController> {
try {
let api = req.query.api;
let constUt = new constUtils();
let url = constUt.conf.API_MOCS[`${api}`].url;
await utilsController.utilsCsvConverter(api, url);
return {url, api};
} catch (error) {
console.log(error);
}
}
}
export const indexController = new IndexController();
If you want use or apply interface to the class you should have to implement it within the applied class. Below code might give you clear idea.
class IndexController implements csvModel {
url: string;
api: string;
public async index(req: Request, res: Response): Promise<IController> {
try {
this.api = req.query.api;
let constUt = new constUtils();
this.url = constUt.conf.API_MOCS[`${api}`].url;
} catch (error) {
console.log(error);
}
}
}
export const indexController = new IndexController();

Resources