diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts
index 20833a4e4..6dea53ce2 100644
--- a/packages/nestjs/src/index.ts
+++ b/packages/nestjs/src/index.ts
@@ -2,3 +2,4 @@ export * from './lib/automapper.module';
export * from './lib/interfaces';
export * from './lib/di';
export * from './lib/abstracts';
+export * from './lib/interceptors';
diff --git a/packages/nestjs/src/lib/interceptors/index.ts b/packages/nestjs/src/lib/interceptors/index.ts
index e69de29bb..e852578c9 100644
--- a/packages/nestjs/src/lib/interceptors/index.ts
+++ b/packages/nestjs/src/lib/interceptors/index.ts
@@ -0,0 +1 @@
+export * from './map.interceptor';
diff --git a/packages/nestjs/src/lib/interceptors/map.interceptor.ts b/packages/nestjs/src/lib/interceptors/map.interceptor.ts
index e69de29bb..b934077ef 100644
--- a/packages/nestjs/src/lib/interceptors/map.interceptor.ts
+++ b/packages/nestjs/src/lib/interceptors/map.interceptor.ts
@@ -0,0 +1,71 @@
+import type { MapOptions, Mapper } from '@automapper/types';
+import type {
+ CallHandler,
+ ExecutionContext,
+ NestInterceptor,
+} from '@nestjs/common';
+import { mixin, Optional } from '@nestjs/common';
+import type { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { InjectMapper } from '../di';
+import { memoize } from './memoize.util';
+
+export const MapInterceptor: (
+ to: unknown,
+ from: unknown,
+ options?: { isArray?: boolean; mapperName?: string } & MapOptions
+) => NestInterceptor = memoize(createMapInterceptor);
+
+function createMapInterceptor(
+ to: unknown,
+ from: unknown,
+ options?: { isArray?: boolean; mapperName?: string }
+): new (...args) => NestInterceptor {
+ const { isArray = false, mapperName, ...mapOptions } = options || {};
+
+ class MixinMapInterceptor implements NestInterceptor {
+ constructor(
+ @Optional() @InjectMapper(mapperName) private readonly mapper: Mapper
+ ) {}
+
+ async intercept(
+ context: ExecutionContext,
+ next: CallHandler
+ ): Promise> {
+ if (!this.mapper || !to || !from) {
+ return next.handle();
+ }
+
+ try {
+ return next.handle().pipe(
+ map((response) => {
+ if (isArray) {
+ if (!Array.isArray(response)) return response;
+ return this.mapper.mapArray(
+ response,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ to as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ from as any,
+ mapOptions
+ );
+ }
+
+ return this.mapper.map(
+ response,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ to as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ from as any,
+ mapOptions
+ );
+ })
+ );
+ } catch {
+ return next.handle();
+ }
+ }
+ }
+
+ return mixin(MixinMapInterceptor);
+}
diff --git a/packages/nestjs/src/lib/interceptors/memoize.util.ts b/packages/nestjs/src/lib/interceptors/memoize.util.ts
index e69de29bb..52042be85 100644
--- a/packages/nestjs/src/lib/interceptors/memoize.util.ts
+++ b/packages/nestjs/src/lib/interceptors/memoize.util.ts
@@ -0,0 +1,21 @@
+const defaultKey = 'default';
+
+// eslint-disable-next-line @typescript-eslint/ban-types
+export function memoize(fn: Function) {
+ const cache = {};
+ return (...args) => {
+ const n =
+ args.reduce(
+ (key, arg) =>
+ key.concat('|', typeof arg === 'string' ? arg : arg.toString()),
+ ''
+ ) || defaultKey;
+ if (n in cache) {
+ return cache[n];
+ }
+
+ const result = n === defaultKey ? fn() : fn(...args);
+ cache[n] = result;
+ return result;
+ };
+}