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

Dependency Injection in Middleware

Provide services like database connections to route handlers without global state variables.

In this lesson, you will learn how to gracefully pass services—like database connections—into your route handlers using middleware instead of relying on risky global variables.

The Problem with Globals

When building an application, you often need to access a database client or a logger inside your route handlers. A common beginner mistake is to import a database connection from a global file:

// Risky global approach
const db = require('./db-connection'); 

app.get('/users', (req, res) => {
  db.query('SELECT * FROM users'); // Implicit dependency on global state
});

This approach makes your code hard to test and fragile. If you need to switch databases for a test environment or mock a service, you are stuck with whatever that global module exports.

Injecting via Middleware

Dependency Injection (DI) allows us to "inject" these services into the request object (req) as it passes through the middleware chain. By attaching our database instance to req at the start of the lifecycle, every subsequent handler gets access to the exact service it needs without knowing where it came from.

// The Middleware Injector
const injectDb = (dbInstance) => (req, res, next) => {
  req.db = dbInstance; // Attach the dependency to the request
  next();
};

// Usage
app.use(injectDb(myDatabasePool));

app.get('/users', (req, res) => {
  // We use the service injected into the request
  req.db.query('SELECT * FROM users')
    .then(data => res.json(data));
});

Why This Matters

By attaching services to the req object, we treat our dependencies as part of the "request context." This decouples your route logic from the specific implementation of your services. If you want to use a different database mock during testing, you simply change the instance you pass to the injectDb middleware. Your route handlers remain identical, unaware that the underlying service has changed. This pattern keeps your architecture clean, modular, and much easier to debug as your server scales.

Key Takeaway: Dependency Injection via middleware transforms your route handlers into modular functions that rely on context passed through the request object, rather than hard-coded global dependencies.