Pug 3.x Promise returns vs Express.js callback-based res.render integration
0 reputation · 25 Feb 2023, 13:29 UTC
Integration boundary
When integrating Pug 3.x as an Express.js view engine, the template compiler emits async functions whenever a template uses await in iteration, async mixins, or async includes. The compiled render function then returns a Promise<string> instead of a plain string. Express's res.render method, however, expects the view engine's __express callback to invoke its callback with (err, html) synchronously or via callback, not to return a Promise. The built-in pug.__express adapter wraps renderFile but does not automatically await async template functions before calling back, creating a contract mismatch that surfaces as unhandled promise rejections or empty responses when templates contain async constructs.
Constraints and uncertainty
The uncertainty centers on whether the adapter can be configured to detect and await async compiled functions transparently, or whether every async template requires a custom wrapper middleware that explicitly awaits pug.renderFile before invoking res.send. Third-party middleware that still passes callbacks to the view engine further complicates the upgrade path from Pug 2.x.
Open questions
- Does
pug.__expressin Pug 3.x internally await the compiled template function when it returns a Promise, or must application code supply a custom view engine wrapper? - How should async mixins that force the entire template async be handled without breaking synchronous rendering paths in the same codebase?
- Is there a supported pattern for gradually migrating callback-based view engine integrations to Promise-aware rendering without forking the adapter?