Ajantha Bandara
Ajantha Bandara

Reputation: 1531

How to access request body in an Angular Server Side Rendering application?

I'm implementing an application to generate pdf using angular server side rendering. In there I want to use request body inside the angular components, this is the code sample I trying

server.ts file

  server.post('/api/v1/pdf', (req, res) => {
    // res.status(404).send('data requests are not yet supported');
    res.render(indexHtml, { req, providers: [
        { provide: APP_BASE_HREF, useValue: req.baseUrl },
        { provide: PDFRequestService, useValue: req}
      ]
    });
  });

service to access request data pdf-request.service.ts

@Injectable()
export class PDFRequestService {
  constructor(@Inject(REQUEST) private request: Request) {}

  get requestBody(): {workflowId: string} {
    return this.request.body;
  }
}

and import it to the app.server.module.ts

@NgModule({
  imports: [
    AppModule,
    ServerModule,
  ],
  providers: [
    // Add server-only providers here.
    PDFRequestService
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

But I don't know how to access this PDFRequestService inside the angular application, when I import the service to app.module.ts file throwing an error saying null Injecttor for REQUEST, but it's in the express engine.

Does anyone knows how to access the request data inside angular application before rendering in server side?

Upvotes: 2

Views: 2539

Answers (2)

Radowan Mahmud Redoy
Radowan Mahmud Redoy

Reputation: 271

You can simply pass the request from the server using providers.

  server.get('*', async (req, res) => {
    var data = 'any data';
    res.render(indexHtml, {
      req,
      providers: [
        {provide: APP_BASE_HREF, useValue: req.baseUrl},
        {provide: 'REQUEST', useValue: req},
        {provide: 'RESPONSE', useValue: res},
        {provide: 'data', useValue: data},
      ],
    });
  });

Not only the request and response but you can also send custom data to angular components using the same process.

And in the component you can access these datas by using Inject()

  constructor(
    @Optional() @Inject(REQUEST) private request: Request<any>,
    @Optional() @Inject(RESPONSE) private response: Response,
    @Optional() @Inject('data') private data: any,
    ) {
  }

This mechanism typically passes the data to angular components from the express server.

Angular 17 >

For Angular ssr from Angular 17 and onwards, the above mechanism won't let you access the request body from Angular.

You can use express injection tokens to access the request. Create a file as express.token.ts

include the following lines

import { InjectionToken } from '@angular/core';
import { Request, Response } from 'express';
export const REQUEST = new InjectionToken<Request>('REQUEST');
export const RESPONSE = new InjectionToken<Response>('RESPONSE');

and in the server.ts file, you can as usual pass the request using providers. The code shall look like

 server.get('*', async (req, res, next) => {
    const {protocol, originalUrl, baseUrl, headers} = req;
    commonEngine
      .render({
        bootstrap,
        documentFilePath: indexHtml,
        url: `${protocol}://${headers.host}${originalUrl}`,
        publicPath: distFolder,
        providers: [
          {provide: APP_BASE_HREF, useValue: baseUrl},
          {provide: REQUEST, useValue: req},
          {provide: RESPONSE, useValue: res},
        ],
      })
      .then((html) => res.send(html))
      .catch((err) => next(err));
  });

While using the request in the angular component you can inject the request as before just remember to import it from express.token.ts

import {REQUEST} from "express.token";
import {Request} from "express";
....

And in constructor

@Optional() @Inject(REQUEST) private request: Request,

This should let you access requests from the angular component portion as well.

Upvotes: 2

Ajantha Bandara
Ajantha Bandara

Reputation: 1531

Finally found a way to parse the request body to angular application. In the route controller put the a provider like below(I was using body-parser to take in request body as json)

server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [
      { provide: APP_BASE_HREF, useValue: req.baseUrl },
        { provide: 'body', useValue: req.body}] 
    });
  });

and inside AppModule we cam access it as below

export class AppModule {
  constructor(
    @Optional() @Inject('body') private body: any
  ) {
    console.log(`body`, body);
}

That all. But there is another problem, when we use angular ssr rendering process happening two times, one is the server and other one in the client, so we will loose the data in client side. To solve that we can use below modules

  • ServerTransferStateModule
  • BrowserTransferStateModule
  • TransferState

Upvotes: 1

Related Questions