'NestJs redirect without response usage

Is it possible to make a redirect from a Nest controller without the usage of the @Response object?

For now I know that we can only do this via direct @Response object injection into the route handler.



Solution 1:[1]

You can write a RedirectInterceptor:

@Injectable()
export class RedirectInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
    const response = context.switchToHttp().getResponse();
    response.redirect('redirect-target');
    return stream$;
  }
}

Then use it in your controller like this:

@Get('user')
@UseInterceptors(RedirectInterceptor)
getUser() {
  // will be redirected.
}

It is important not to return anything from your controller, otherwise you will get the following error: Can't set headers after they are sent.

If needed the RedirectInterceptor can be dynamic as well:

@Injectable()
export class RedirectInterceptor implements NestInterceptor {
  constructor(private readonly target: string) {}
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
    const response = context.switchToHttp().getResponse();
    response.redirect(this.target);
                      ^^^^^^^^^^^
    return stream$;
  }
}

and then in the controller:

@UseInterceptors(new RedirectInterceptor('redirect-target'))

Solution 2:[2]

(a bit of a different implementation to another answer here...)

I created a RedirectError which can be thrown more dynamically than a decorator

import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
import { Response } from 'express';

export class RedirectError extends Error {
  constructor(public readonly status: number, public readonly url: string) {
    super();
  }
}

@Catch(RedirectError)
export class RedirectFilter implements ExceptionFilter {
  public catch(exception: RedirectError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    return response.redirect(exception.status, exception.url);
  }
}

and then in main.ts set it:

  app.useGlobalFilters(new RedirectFilter());

and finally to use it:

throw new RedirectError(302, '/some-target');

Solution 3:[3]

I've done it more complex, but I think it is good enough.

  1. Create a class such as util/RedirectException like this:

The code like this:

import { HttpException, HttpStatus } from '@nestjs/common';

export class RedirectException extends HttpException {
  constructor(message?: string | object) {
    super(message, HttpStatus.CONTINUE);
  }
}
  1. Create a RedirectFilter by: nest g f RedirectFilter

Write the code like this:

import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common';
import { RedirectException } from './util/redirect-exception';

@Catch()
export class RedirectFilter implements ExceptionFilter {
 catch(exception: any, host: ArgumentsHost) {
  const res = host.switchToHttp().getResponse(),
  req = host.switchToHttp().getRequest();
  try {
    if (exception instanceof RedirectException) {
      Object.keys(exception.message).forEach(k => {
        req.session[k] = exception.message[k];
      });
      req.session.save(() => {
        res.redirect(exception.message.url);
      });
      return;
    }

    if (exception instanceof HttpException) {
      return res.status(exception.status).json(exception.message)
    }
    res.status(500).json({status: 500, message: 'Internal Server error'})
   } catch (e) {
    return res.status(500)
      .json({
        status: HttpStatus.INTERNAL_SERVER_ERROR,
        message: e.message
      });
   }
  }
}

This class help you handle all the response when an exception is throw. And yes, this include the Redirect exception. Now we can throw the exception with exactly params, and it work!

  1. Use the filter in main.ts: app.useGlobalFilters(new RedirectFilter());

  2. And in controller, if you want to redirect to an url, just do this any time you want

Code:

 throw new RedirectException({
    url: 'the url you want to redirect',
    field1: 'The first field you want to pass to session'
    field2: 'The second field you want to pass to session'
 });
  1. Don't forget setup express-session if you want to pass data by session when redirect: https://www.npmjs.com/package/express-session.

If you don't want to use this, just replace the code inside if (exception instanceof RedirectException) {} to: res.redirect(exception.message.url);. It don't check and setup the session anymore.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Kim Kern
Solution 2 Alon Burg
Solution 3