Interceptors in NestJS add cross-cutting logic by running code before and after the main controller handles a request. When a request arrives, the interceptor first runs its 'before' code, then calls next.handle() to let the controller process the request. After the controller returns a response, the interceptor runs its 'after' code and finally returns the response to the client. This wrapping behavior allows interceptors to add shared features like logging or timing around many requests without changing each controller. The execution table shows the step-by-step flow: logging 'Before', calling the controller, receiving the response, logging 'After', and sending the response. Variables like the response result change from undefined to resolved data as the interceptor awaits the controller. Key points include that interceptors must call next.handle() to continue processing, and they can run code both before and after the controller. This pattern helps keep code clean and reusable.