Course contentsShow
← Node.js
Lesson 712 of 1,9807. Web Framework Architecture and MiddlewareFree lesson

Observer Pattern for Middleware

Decouple cross-cutting concerns by allowing services to register for request events.

In this lesson, you will learn how to decouple your core request-handling logic from secondary "cross-cutting" concerns by using an Observer pattern within your middleware stack.

The Problem: Cluttered Middleware

In a typical Node.js application, middleware often becomes a "catch-all" where you manually trigger telemetry, logging, or audit functions. This makes your request pipeline fragile—if you want to add a new service, like a real-time notification engine, you have to modify your core route logic, which violates the Open/Closed Principle.

The Observer Solution

Instead of hard-coding service calls inside your routes or global middleware, you treat the request lifecycle as an Observable Subject. You create an EventEmitter that exposes specific "hooks" (e.g., requestStarted, requestCompleted, errorOccurred). Other services in your system simply subscribe to these events independently.

By doing this, your middleware doesn't need to know who is listening. It only needs to publish the event:

// Inside your Request Middleware
function eventMiddleware(req, res, next) {
  // Publish the event to any registered observers
  req.app.emit('requestStarted', { 
    path: req.path, 
    userId: req.user?.id 
  });
  
  next();
}

Decoupling Cross-Cutting Concerns

Now, your AuditLogger or TelemetryService can live in their own modules. They simply register themselves:

// Inside a separate AuditLogger service
app.on('requestStarted', (data) => {
  auditRepository.logAccess(data.userId, data.path);
});

Because these services are observers, they operate completely detached from the HTTP request-response loop. If you decide to remove the AuditLogger or add a new UserEngagementTracker, you don’t have to touch a single line of your routing or primary middleware code. This turns your application into a modular ecosystem where features are plugged in rather than bolted on.

Key Takeaway: The Observer pattern allows you to keep your core business logic clean by offloading secondary concerns to independent services that "listen" for lifecycle events instead of being explicitly invoked by the middleware.