Working with Response Locals

1. Understanding res.locals Object

PropertyLifetimeDefault
res.localsSingle request{}
app.localsProcess lifetime{settings, mountpath, ...}

2. Setting Request-Scoped Variables

Example: Attach user

app.use(async (req, res, next) => {
  res.locals.user = await getCurrentUser(req);
  res.locals.requestId = req.id;
  next();
});

3. Sharing Data Between Middleware

Example: Pipeline state

app.use((req, res, next) => { res.locals.startTime = Date.now(); next(); });
app.use((req, res, next) => { res.locals.tenant = req.subdomains[0]; next(); });
app.get("/me", (req, res) => res.json({
  ...res.locals.user,
  tenant: res.locals.tenant,
  ms: Date.now() - res.locals.startTime
}));
PatternWhy
res.localsAvailable in templates & downstream middleware
req.user (Passport)Convention for auth result
AsyncLocalStorageCross-await context (Node 16+)

4. Passing Data to Views

Example: Implicit template context

app.use((req, res, next) => {
  res.locals.csrfToken = req.csrfToken();
  res.locals.flash = req.session.flash;
  next();
});

app.get("/", (req, res) => res.render("home"));  // csrfToken & flash auto-available

5. Using res.locals vs app.locals

Use CaseWhere
Site name, versionapp.locals
Helper functions (formatDate)app.locals
Current user, flashres.locals
Per-request feature flagsres.locals

6. Implementing User Context

Example: User middleware

export async function userContext(req, res, next) {
  try {
    const token = req.cookies.session;
    if (token) {
      const { sub } = jwt.verify(token, env.JWT_SECRET);
      res.locals.user = await db.users.findById(sub);
    }
    next();
  } catch { next(); }
}

7. Creating Flash Messages

Example: One-shot messages

app.use((req, res, next) => {
  res.locals.flash = req.session.flash || [];
  delete req.session.flash;  // consume
  next();
});

// To set
req.session.flash = [{ type: "success", text: "Saved!" }];
res.redirect("/dashboard");
LibraryNotes
connect-flashStandard middleware (requires sessions)
Manual sessionAbove pattern

8. Setting Response Metadata

res.locals.pagination = { page, limit, total };
res.json({
  data: results,
  meta: res.locals.pagination
});

9. Using res.locals for Error Context

Example: Error template context

app.use((err, req, res, next) => {
  res.locals.error = {
    message: err.message,
    status: err.status || 500,
    stack: process.env.NODE_ENV === "development" ? err.stack : undefined
  };
  res.status(res.locals.error.status).render("error");
});

10. Cleaning Up res.locals

QuestionAnswer
Auto-cleared?Yes — new res.locals per request
Memory leak risk?None unless you store on app.locals or external structures
Avoid storingDon't put DB connections, secrets, large buffers
Warning: Never store the full req or res in res.locals — circular references break templates and JSON serialization.