20

I'm trying:

    @Post('login')
    async login(@Body() body: AuthDto, @Res() res: Response) {
        const loginResponse = await this.authService.login(body);
        console.log('loginResponse', loginResponse)
        res.headers.set('x-access-token', loginResponse.access_token)
        return loginResponse
    }

but no dice. I get an error:

TypeError: Cannot read property 'set' of undefined
Shamoon
  • 41,293
  • 91
  • 306
  • 570

4 Answers4

26

Not the most elegant way: return res.set({ 'x-access-token': loginResponse.access_token }).json(loginResponse);

I'd separate this logic into an interceptor, checking if the response is valid for path /login, if so return the correct header (using some value from loginResponse)

import { Controller, Get, Response } from '@nestjs/common';
import { Response as Res } from 'express';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(@Response() res: Res): Res {
    return res.set({ 'x-access-token': 1 }).json({ hello: 'world' });
  }

  @Get()
  getHelloAlt(@Response() res: Res): Res {
    return res.set({ 'x-access-token': 1 }).json({ hello: 'world' });
  }
}

This is my working version, notice the Express Response and not Nest.js.

EDIT: The type imported from Nest.js/common is a decorator function, instead either use no type, or import Response from Express.js.

Daniel Santos
  • 14,328
  • 21
  • 91
  • 174
Isolated
  • 2,167
  • 14
  • 14
  • 3
    Same: `Property 'set' does not exist on type 'Response'` – Shamoon May 09 '20 at 14:42
  • Is that error at compile-time or run-time? If compile-time, where are you importing Response from? Native, or Express? Be aware there's a global Response type also. I've updated my original answer for you, let me know how that works. – Isolated May 09 '20 at 14:44
  • It's at compile time. I'm importing from `nest.js` – Shamoon May 09 '20 at 14:48
  • `async login(@Req() req: Request, @Response() res) {` - that did it – Shamoon May 09 '20 at 14:50
  • If you want to keep type-safety, use the Express.js Response interface, it won't affect run-time though as the Express Response is used internally. – Isolated May 09 '20 at 14:53
  • Why won't the nest type work? And if you update your answer to reflect my comment, I can accept – Shamoon May 09 '20 at 14:55
  • Because the type you're importing isn't a type, it's a decorator so it doesn't have a collection of properties. Depending on your code editor (I use VS Code), you can view where the import is declared, this looks like: `export declare const Response: () => ParameterDecorator;` A little confusing I must admit. I'll edit your comment into the answer though! – Isolated May 09 '20 at 15:36
19

To specify a custom response header, you can either use a @Header() decorator or a library-specific response object (and call res.header() directly).

Import Header from the @nestjs/common package.

@Post()
@Header('Cache-Control', 'none')
create() {
  return 'This action adds a new cat';
}
Ismail
  • 1,188
  • 13
  • 31
  • This is a super-simple way to fix CORS errors when debugging your UI locally. Just add `Access-Control-Allow-Origin: http://localhost:4200` (or similar) to your endpoints. – piccy Oct 08 '22 at 15:36
9

I use an interceptor. As I want to add a field to the headers on the response indicating my service, I also set this as a global interceptor, but you can use it by route as well.

response-add-access-token-to-header.interceptor.ts

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';

import { Response as ExpressResponse } from 'express';

@Injectable()
export class ResponseAddAccessTokenToHeaderInterceptor implements NestInterceptor {
    intercept(context:ExecutionContext, next:CallHandler): Observable<any> {

        const ResponseObj:ExpressResponse = context.switchToHttp().getResponse();
        ResponseObj.setHeader('x-access-token', 'Your Data' );
        return next.handle();
    }
}

To add it globally, adjust main.ts:

async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    app.useGlobalInterceptors(new ResponseAddAccessTokenToHeaderInterceptor());
    await app.listen(8080);
}
bootstrap();
Steven Scott
  • 10,234
  • 9
  • 69
  • 117
0

Nest is able to work with different HTTP frameworks (Express by default). Thus, to set a header value that is not known in advance, use API of your chosen HTTP framework. If you are using Express, the code will look like this:

@Post('login')
async login(@Body() body: AuthDto, @Res() res: Response) {
    const loginResponse = await this.authService.login(body);
    res.header('x-access-token', loginResponse.access_token).json(loginResponse);
}

However, in the given case, you lose compatibility with Nest features that depend on Nest standard response handling, such as Interceptors and @HttpCode() / @Header() decorators. To fix this, you can set the passthrough option to true:

@Post('login')
async login(@Body() body: AuthDto, @Res({ passthrough: true }) res: Response) {
    const loginResponse = await this.authService.login(body);
    res.header('x-access-token', loginResponse.access_token);
    return loginResponse;
}
ns16
  • 1,322
  • 2
  • 17
  • 26