Course contentsShow
← Node.js
Lesson 1143 of 1,98010. Real-time Communication and WebsocketsFree lesson

Building Custom Socket Middleware

You will write reusable middleware to inject common cross-cutting concerns like logging and auth into socket handlers.

In this lesson, you will learn how to build reusable middleware to handle cross-cutting concerns like logging and authentication for your socket event handlers.

Why Use Middleware?

As your real-time application grows, you’ll find yourself repeating the same logic inside every socket event: checking if a user is authenticated, logging the incoming payload for debugging, or validating the data structure. If you write this inside every handler, your codebase becomes brittle and difficult to maintain.

Custom middleware allows you to "wrap" your event handlers, intercepting the request before it reaches your core logic. This keeps your business logic clean and separates "what the application does" from "how the application protects or monitors itself."

Implementing a Middleware Pattern

In a Node.js socket environment (such as Socket.io or a raw ws implementation), you can achieve this by creating a higher-order function that wraps your handler.

// Middleware: Authentication check
const authenticate = (handler) => (socket, data) => {
  if (!socket.user) {
    return socket.emit('error', 'Unauthorized');
  }
  return handler(socket, data);
};

// Middleware: Logging traffic
const logger = (handler) => (socket, data) => {
  console.log(`Event received from ${socket.id}:`, data);
  return handler(socket, data);
};

// Applying the middleware
socket.on('update-profile', logger(authenticate((socket, data) => {
  // Now our handler is protected and logged automatically
  updateDatabase(socket.user.id, data);
})));

How It Works

The logger and authenticate functions accept an existing handler and return a new function. When the socket triggers the event, it calls this returned function instead. The middleware performs its check first—if the user isn't authenticated or the logging finishes, it invokes the original handler.

By stacking these functions (e.g., logger(authenticate(myHandler))), you create a pipeline that ensures every event follows your organization's standards for safety and visibility without polluting your primary logic. This architectural approach makes it trivial to add or remove global requirements across your entire socket infrastructure.

Key Takeaway: Custom socket middleware acts as a protective wrapper that decouples cross-cutting concerns like auth and logging from your core event logic, resulting in a cleaner and more scalable codebase.