The proxy request gets delayed unless I utilize the http-proxy-middleware

Here is the code for a provider:

@Injectable()
export class GameServerProxyService {

    private httpProxy: httpProxy;
    
    constructor(@Inject(GameServerDetailsService) private gameServiceDetailsService: GameServerDetailsService) {
        this.httpProxy = httpProxy.createProxyServer({});

        this.httpProxy.on("proxyReq", (err,req,res)=>{
            console.log("proxyReq");
        });

        this.httpProxy.on("proxyRes", (err,req,res)=>{
            console.log("proxyRes");
        });
        
        this.httpProxy.on("error", (err,req,res)=>{
            console.error(err);
        });
    }

    proxyRequest(request: Request, response: Response){

        console.log("proxyRequest");

        this.httpProxy.web(request, response, {
            target: "http://localhost:3100/",
            changeOrigin: true,
        }, (error) => {
            console.log(error);
        });
    }
}

Below is a controller that uses the above provider to proxy calls:

@Controller()
export class SsrApiGatewayController {

    constructor(
        @Inject(GameServerProxyService) private gameServerProxyService: GameServerProxyService
    ) { }
    
    @All("game-server/*")
    async proxyGameServerRequest(@Req() request: Request, @Res() response: Response) {
        console.log("Proxying Request..!")
        this.gameServerProxyService.proxyRequest(request, response);
    }
}

The issue I am facing is that when I send a request to the route handled by the controller, my request seems to stall. The logs show:

Proxy Request..!
proxyReq

This indicates that the proxied request never reaches the response stage. Upon canceling the stalled request, I receive an error from the server being proxied to (http://localhost:3100/):

[Nest] 6220  - 01/27/2022, 7:41:35 PM   ERROR [ExceptionsHandler] request aborted
BadRequestError: request aborted
    at IncomingMessage.onAborted (C:\******\node_modules\raw-body\index.js:231:10)
    at IncomingMessage.emit (events.js:314:20)
    at IncomingMessage.EventEmitter.emit (domain.js:483:12)
    at abortIncoming (_http_server.js:533:9)
    at socketOnClose (_http_server.js:526:3)
    at Socket.emit (events.js:326:22)
    at Socket.EventEmitter.emit (domain.js:483:12)
    at TCP.<anonymous> (net.js:675:12)

The request is being proxied forward but there is no response coming back.

Interestingly, using http-proxy-middleware instead of http-proxy solves the problem. This middleware configuration works smoothly:


async function bootstrap() {
    const app = await NestFactory.create(SsrApiGatewayModule);

    app.useGlobalPipes(
        new ValidationPipe({
            transform: true,
            whitelist: true,
        }),
    );

    app.use("/game-server/*", 
       createProxyMiddleware({
           target: "http://localhost:3100",
           changeOrigin: true,
       })
    );

    var configService = app.get(ConfigService);
    var commonConfig = configService.get<CommonConfig>(CommonKey);
    await app.listen(commonConfig.port);
}

Despite this workaround, I want to leverage my custom provider because it allows for a more intricate router setup and access to other injected providers within NestJS ecosystem.

The reason behind the request stalling despite various attempts including implementing a NestJS middleware with http-proxy-middleware wrapped inside remains unclear.

Answer №1

After some investigation, it was revealed that the issue stemmed from the BodyParser middleware consuming the request body data stream. As a result, when the proxy code attempted to forward both the request and its body, it encountered difficulties as the data stream had already been consumed. Consequently, the proxied server continued to wait indefinitely for the missing request data.

To address this issue, I developed a custom middleware that encapsulates both the body parser and the proxy middleware functions. The decision on which middleware to employ is based on the request URL - if the URL begins with /game-server/ or ends with /game-server, the proxy is used; otherwise, the body parser is utilized.

For reference, below is the code:

Bootstrapping:

const app = await NestFactory.create(SsrApiGatewayModule, {
    //We will manually invoke body-parser in the API Gateway Proxy Middleware. 
    bodyParser: false,
});

Root module

@Module({
    //...
})
export class SsrApiGatewayModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
        consumer
            .apply(ApiGatewayProxyMiddleware)
            .forRoutes({ path: `*`, method: RequestMethod.ALL });
    }
}

Middleware:

@Injectable()
export class ApiGatewayProxyMiddleware implements NestMiddleware {

    private jsonBodyParser: RequestHandler;
    private httpProxyMiddleware: RequestHandler;
    private startWith: string;
    private endWidth: string;

    //You can inject stuff here.
    constructor() {
        this.jsonBodyParser = bodyParser.json();

        //You can inject the config module to configure target, etc
        this.httpProxyMiddleware = createProxyMiddleware({
            target: "http://localhost:3000",
            changeOrigin: true,
            pathRewrite: {'^/game-server' : ''},
        })

        this.startWith = `/game-server/`;
        this.endWidth = `/game-server`;
    }

    use(req: Request, res: Response, next: NextFunction) {

        let pathMatch = req.path.startsWith(this.startWith);
        if(!pathMatch) pathMatch = req.path.endsWith(this.endWidth);

        if(pathMatch) {
            console.log("Proxying request for path: " + req.path)
            this.httpProxyMiddleware(req,res,next);
        }
        else {
            //Only run body parser if we plan to handle the request here instead of proxying it https://stackoverflow.com/questions/70875743
            console.log("Parsing request body for path: " + req.path);
            this.jsonBodyParser(req, res, next);
        }
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring methods for testing React components with TypeScript's props

I am currently tackling a react-typescript project and I am looking to conduct testing on props passed to a react component using react-testing library. Here, we have the SharedDashboardUiLatestValueWidget.tsx component: export interface SharedDashboardU ...

RTK update mutation: updating data efficiently without the need to refresh the page

I am facing an issue with my mui rating component in a post-rating scenario. Although the rating updates successfully in the data, the page does not refresh after a click event, and hence, the rating remains enabled. To address this, I have implemented a d ...

Show pictures stored in S3

I currently have an Amazon AWS S3 Bucket where I store images. Each object in the bucket has its own link, but when I try to open it in a browser, the image downloads instead of displaying directly on the site. This makes it challenging to view the images ...

When using Framer Motion for page transitions alongside React Router DOM v6, the layout components, particularly the Sidebar, experience rerenders when changing pages

After implementing page transitions in my React app using Framer Motion and React-Router-DOM, I noticed that all layout components such as the sidebar and navbar were unexpectedly rerendering upon page change. Here's a snippet of my router and layout ...

What is the significance of default parameters in a JavaScript IIFE within a TypeScript module?

If I were to create a basic TypeScript module called test, it would appear as follows: module test { export class MyTest { name = "hello"; } } The resulting JavaScript generates an IIFE structured like this: var test; (function (test) { ...

What is the rationale behind placing the CSS outside of the React function components, near the imports?

Recently, I encountered an issue with loading CSS inside a React function component using Material UI. Even though I managed to resolve it, I am still intrigued by the underlying reason. Initially, I had something like this setup where I placed both makeSt ...

Creating web components with lit-element, leveraging rollup, postcss, and the tailwind framework for packaging

I have been attempting to package a functional web component that was developed using the lit-element/lit-html with the tailwind framework utilizing the postcss plugin from the rollup packager. Upon conducting a rollup, I discovered the compiled js and ht ...

The data type 'StaticImageData' cannot be converted to type 'string'

I've hit a roadblock with interfaces while working on my NextJS and TypeScript project. I thought I had everything figured out, but I'm encountering an issue with the src prop in my Header component. The error messages I keep receiving are: Typ ...

Retrieving the return type of generic functions stored in a map

In this unique scenario, I have a dictionary with polymorphic functions that accept the same argument but return different results: const dict = { one: { foo<A>(a: A) { return [a] as const } }, two: { foo<A>(a: A) { ...

Having trouble sending an email using nodejs and mailgun

Before accusing me of asking a duplicate question, I want to clarify that I have already searched for solutions and none of them worked for me. For example, I tried the solution provided in this link: Example of the domain name for mailgun before nodejs? ...

Angular 5 - Reverting back to the previous state

In my Angular web application, I encounter a scenario where I need to navigate back to the previous state. Let's say I am currently on a page at http://localhost/someURL. On this particular page, users have the ability to search for items and see the ...

Wrapper functions that are nested are returning a Promise that resolves to another Promise of type T

I have a function called doesPromiseyThings that wraps a thunk and returns its value inside a Promise. I want to create another wrapper that not only handles the creation of thunks, but also ensures the returned type is a Promise-wrapped version of the ori ...

The type 'RefObject<HTMLDivElement>' cannot be matched with type 'RefObject<HTMLInputElement>' in this context

Encountered an error message: Issue with assigning types: Type '{ placeholder: string | undefined; autoComplete: string | undefined; "data-testid": string | undefined; onChange?: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement&g ...

Transferring Files from Bower to Library Directory in ASP.Net Core Web Application

Exploring ASP.Net Core + NPM for the first time, I have been trying out different online tutorials. However, most of them don't seem to work completely as expected, including the current one that I am working on. I'm facing an issue where Bower ...

TypeScript and Angular: Error Encountered when Trying to Combine Two Arrays

I'm attempting to combine two arrays of the same type that are nested within a "parent" array. The end goal is to flatten the structure. Below is the code I have been using: ngOnInit() { this.Logs.getAllLogs() .subscribe(logs => { ...

How to use attributes in Angular 2 when initializing a class constructor

Is there a way to transfer attributes from a parent component to the constructor of their child components in Angular 2? The process is halfway solved, with attributes being successfully passed to the view. /client/app.ts import {Component, View, bootst ...

Leveraging the typeof Operator within a Class

How can we utilize typeof in order to specify the type of a class property? Take a look at both examples below, where example A works but example B does not. A) Works outside class const data: {age:number, name:string} = {age:10, name:'John'}; c ...

Regular pattern with Kubernetes cluster endpoint utilizing either IP address or fully qualified domain name

In my Angular/typescript project, I am working on building a regex for a cluster endpoint that includes an IP address or hostname (FQDN) in a URL format. For instance: Example 1 - 10.210.163.246/k8s/clusters/c-m-vftt4j5q Example 2 - fg380g9-32-vip3-ocs.s ...

Together, we have a shared Yarn JS directory for both the client and server components,

The scenario: both the client and server share a folder named shared when we make changes to the shared folder in our development process, we need the corresponding references to update in both the client and server the server seems to w ...

The Kubernetes cluster unexpectedly closes down following a period of processing

My GCP cluster is hosting a NodeJS server. The server functions flawlessly when run locally, but mysteriously stops without any error messages when I attempt to send a post request to a specific route. This post request is supposed to trigger the sending o ...