API Pipeline Module
Relevant source files
The following files were used as context for generating this wiki page:The API Pipeline module provides a generic implementation of the Chain of Responsibility design pattern. It allows for the creation of sequential processing workflows where a request (or context) is passed through a series of handlers. Each handler in the chain has the opportunity to process the request and can either pass it to the next handler or terminate the chain.
This module is designed to be reusable across different parts of the API for tasks that can be broken down into distinct, ordered steps, such as user authentication, data validation, or multi-stage processing. The PipelineService acts as the entry point to execute a given chain of handlers.
Core Concepts and Architecture
The module is built around three main components: an interface (IHandler), an abstract base class (AbstractHandler), and a service (PipelineService). A client creates a chain by linking handler instances together and then uses the PipelineService to initiate the process.
The following diagram illustrates the general data flow through a pipeline.
This diagram shows how a context object is passed sequentially through a series of handlers, starting from the PipelineService.
Sources: apps/api/src/common/pipeline/pipeline.service.ts:10-12, apps/api/src/common/pipeline/abstract.handler.ts:11-15
Key Components
The pipeline functionality is encapsulated within the PipelineModule, which is a global module making the PipelineService available throughout the application.
Sources: apps/api/src/common/pipeline/pipeline.module.ts:7-12
IHandler<T> Interface
This interface defines the contract for any handler that can be part of a pipeline. It ensures that each handler has a consistent structure for setting the next handler and for processing the context.
| Method | Description |
|---|---|
setNext(handler: IHandler<T>): IHandler<T> | Sets the next handler in the chain and returns it. |
handle(context: T): Promise<T> | Processes the context object. It can modify the context and must either call the next handler or return the final context. |
Sources: apps/api/src/common/pipeline/handler.interface.ts:1-6
AbstractHandler<T> Class
This abstract class provides a base implementation for the IHandler<T> interface. It manages the reference to the next handler in the chain. Concrete handler classes should extend AbstractHandler.
nextHandler: IHandler<T>: A private property to hold the next handler.setNext(handler: IHandler<T>): Implements the interface method to link the next handler.handle(context: T): Provides a default implementation that checks if anextHandlerexists. If so, it callsnextHandler.handle(context). If not, it returns the context, effectively ending the chain. Concrete handlers typically override this method to add their specific logic before optionally callingsuper.handle(context).
Sources: apps/api/src/common/pipeline/abstract.handler.ts:3-17
PipelineService
This service is the main entry point for executing a pipeline. It is an injectable NestJS service.
execute(context: T, chain: IHandler<T>): This is the primary method. It takes an initialcontextobject and the firsthandlerin the chain. It starts the execution by calling thehandlemethod on the first handler.
// apps/api/src/common/pipeline/pipeline.service.ts:8-12
@Injectable()
export class PipelineService {
public async execute<T>(context: T, chain: IHandler<T>): Promise<T> {
return await chain.handle(context);
}
}
Sources: apps/api/src/common/pipeline/pipeline.service.ts:8-12
Component Class Diagram
The diagram below shows the relationship between the core components of the pipeline module and how a concrete handler fits in.
Implementation Example: Login Chain
A practical application of the pipeline module is the user login process, found in LoginChain. This class constructs and executes a specific pipeline for authenticating a user.
Sources: apps/api/src/module/auth/services/login/login.chain.ts
The LoginChain class defines an execute method that assembles a series of handlers and uses the PipelineService to run them. The handlers are:
ValidateUserHandler: Validates the user's existence and status.PasswordHandler: Verifies the provided password.TokenHandler: Generates authentication tokens upon successful validation.
The chain is constructed by calling setNext() on each handler instance to link them in the correct order.
// apps/api/src/module/auth/services/login/login.chain.ts:19-29
public async execute(context: LoginContext): Promise<LoginContext> {
const validateUser = new ValidateUserHandler(this.userRepository);
const password = new PasswordHandler();
const token = new TokenHandler(this.jwtService, this.configService);
validateUser
.setNext(password)
.setNext(token);
return await this.pipeline.execute(context, validateUser);
}
Sources: apps/api/src/module/auth/services/login/login.chain.ts:19-29
Login Sequence Diagram
This diagram shows the sequence of operations during the login process.
This diagram illustrates how the LoginChain initiates the process, which then flows through each handler via the PipelineService.
Sources: apps/api/src/module/auth/services/login/login.chain.ts:19-29, apps/api/src/common/pipeline/pipeline.service.ts:11