Allow changes to Node middleware.js without prompting to overwrite the file if it is changed

Run in to an issue integrating i18 in to our Node Project. We need to make changes to middleware.js and this results in:

As I am totally useless as explaining this I've had my friend Opus explain it:

Feature Request: Support for res.locals in templateView Middleware

Summary

When customising lib/core/middleware.js to add functionality (such as internationalization), Wappler flags the file as modified and prompts to replace it with the default version. This creates a maintenance burden and risks losing custom functionality during updates.

I'm requesting a small change to the core middleware.js that would allow users to inject custom template variables via standard Express middleware patterns, without needing to modify core Wappler files.


The Problem

Current Behaviour

The templateView function in lib/core/middleware.js renders templates using:

res.render(template, Object.assign({}, app.global.data, app.data, methods), ...)

This bypasses Express's standard res.locals object, which is the conventional way for middleware to inject variables into templates.

Why This Is Limiting

In Express/EJS applications, the standard pattern for middleware to provide template variables is:

app.use((req, res, next) => {
    res.locals.myVariable = 'value';
    next();
});

These variables are then automatically available in all templates. However, because Wappler's templateView creates a fresh object with Object.assign({}, ...) and doesn't include res.locals, any variables set by custom middleware are not available in EJS templates.

This forces users to modify middleware.js directly, which:

  1. Gets flagged as "outdated" by Wappler
  2. Risks being overwritten during updates
  3. Creates merge conflicts when Wappler updates the file
  4. Makes it difficult to maintain custom functionality

Use Case: Internationalization (i18n)

Business Requirement

I needed to add multi-language support to my Wappler application for Portuguese and English markets. This requires:

  • A translation function t('key') available in all EJS templates
  • Language detection from URL parameters, cookies, or browser headers
  • Language metadata (for RTL support, native names, etc.)

Standard Express Solution

The conventional approach would be to create an i18n middleware:

// lib/setup/i18nMiddleware.js
const i18n = require('../i18n');

module.exports = function(app) {
    app.use((req, res, next) => {
        const currentLang = i18n.detectLanguage(req);
        const t = i18n.createTranslator(currentLang);
        
        // Standard Express pattern - inject into res.locals
        res.locals.t = t;
        res.locals.currentLang = currentLang;
        res.locals.supportedLanguages = i18n.getSupportedLanguages();
        
        next();
    });
};

Then in templates:

<h1><%=t('homepage.title')%></h1>
<p>Current language: <%=currentLang%></p>

The Problem

This doesn't work with Wappler because res.locals is not included in the render data. The only solution is to modify middleware.js directly, which triggers the "file modified" warning.


Proposed Solution

The Change

Add res.locals to the Object.assign() calls in the templateView function:

Current code (lines ~143 and ~153):

res.render(template, Object.assign({}, app.global.data, app.data, methods), ...)
res.render(template, Object.assign({}, app.global.data, methods), ...)

Proposed change:

res.render(template, Object.assign({}, res.locals, app.global.data, app.data, methods), ...)
res.render(template, Object.assign({}, res.locals, app.global.data, methods), ...)

Why This Is Safe

  1. Non-breaking: By placing res.locals first in the Object.assign(), Wappler's own data (app.global.data, app.data, methods) takes precedence. Any conflicts are resolved in favour of Wappler's data.

  2. Standard Express pattern: This aligns with how Express and EJS are designed to work together.

  3. Minimal change: Only 2 words added (res.locals,) in 2 locations.

  4. Backward compatible: Existing applications won't be affected since res.locals is typically empty unless explicitly populated by custom middleware.


Benefits for the Wappler Community

1. Internationalization (i18n)

  • Multi-language websites
  • SEO-friendly URL-based language switching
  • RTL language support

2. User Preferences

  • Theme preferences (dark/light mode)
  • Accessibility settings
  • Regional formatting (dates, currencies)

3. Authentication Context

  • User display name in templates
  • Role-based UI elements
  • Permission flags

4. Analytics & Tracking

  • A/B testing variant injection
  • Feature flags
  • Session tracking IDs

5. Site-wide Configuration

  • Environment indicators (dev/staging/prod)
  • Maintenance mode banners
  • Global announcements

Current Workaround

My current workaround requires modifying middleware.js:

// Added to templateView function
const i18n = require('../i18n');

// Inside the async function:
const currentLang = i18n.detectLanguage(req);
const t = i18n.createTranslator(currentLang);

const methods = {
    t: t,
    currentLang: currentLang,
    supportedLanguages: i18n.getSupportedLanguages(),
    // ... rest of methods
};

Issues with this approach:

  • Wappler shows the file as modified and prompts to replace it
  • Risk of losing changes during Wappler updates
  • Difficult to share the solution with other Wappler users
  • Mixes custom code with core Wappler code

Summary

Adding res.locals support to templateView would:

:white_check_mark: Enable standard Express middleware patterns
:white_check_mark: Allow custom functionality without modifying core files
:white_check_mark: Eliminate the "file modified" warnings for this use case
:white_check_mark: Make Wappler more extensible and developer-friendly
:white_check_mark: Require only a minimal, non-breaking change

This small enhancement would significantly improve the developer experience for anyone needing to extend Wappler with custom template variables.


Technical Reference

File: lib/core/middleware.js
Function: templateView
Lines: ~143 and ~153 (the two res.render() calls)

Diff of required change:

- res.render(template, Object.assign({}, app.global.data, app.data, methods), async (err, html) => {
+ res.render(template, Object.assign({}, res.locals, app.global.data, app.data, methods), async (err, html) => {

- res.render(template, Object.assign({}, app.global.data, methods), async (err, html) => {
+ res.render(template, Object.assign({}, res.locals, app.global.data, methods), async (err, html) => {

Please can we either have an 'ignore' and then ignore the file in the update dialogue or have updated files merge the customisations of the Users middleware.js? I really don't want to be forgetting such changes and every time there is an update having to backtrack if I am honest. Files like middleware.js in Node may often require changes to be made so we need some sort of middle ground that allows the User to do this. Or as Opus points out adding res.locals should resolve the issue... More than likely @George @patrick @Teodor will know a lot more about this than me as in regards to which approach to take...

Thank you for any consideration!

:slight_smile:

1 Like