Asked 1 month ago by OrbitalRover973
How can I dynamically render EJS templates in Express using URL parameters?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by OrbitalRover973
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm building a simple Express server and want to render different EJS templates based solely on the URL, without creating explicit routes for each file. For example, in my index.ejs, I'd like to have links to page2.ejs, page3.ejs, etc., and have Express render the corresponding template dynamically. Here’s a simplified version of my server:
JAVASCRIPTimport express from 'express'; const app = express(); app.set('views', './public'); const PORT = 3002; app.get('/', (req, res) => { res.render('index.ejs', { title: 'Page Title'}); }); app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });
I don't want to have to add separate routes like this:
JAVASCRIPTapp.get('/page2.ejs', (req, res) => { res.render('page2.ejs'); }); app.get('/page3.ejs', (req, res) => { res.render('page3.ejs'); }); app.get('/page4.ejs', (req, res) => { res.render('page4.ejs'); });
I'd prefer a more dynamic solution where Express can render the appropriate EJS file based on the URL requested. Any suggestions or best practices for implementing this?
Comments 0
•Answers 4
•Views 44
Answer by TychoAI Agent • 1 month ago
You can create a generic route using a URL parameter that corresponds to the template name. For example, if you navigate to "/page2", Express will extract "page2" from the URL and try to render "page2" (which by default will append the view engine extension if you set it, or you can include the extension in your call). One way to do this is:
JAVASCRIPTimport express from 'express'; const app = express(); // Set where your .ejs templates are located app.set('views', './public'); // (Optional) Set the view engine if you want to omit the extension in res.render() // app.set('view engine', 'ejs'); const PORT = 3002; // Home page route app.get('/', (req, res) => { res.render('index.ejs', { title: 'Page Title' }); }); // Generic dynamic route for other pages app.get('/:page', (req, res) => { const page = req.params.page; // Optionally: validate "page" against a whitelist to ensure security. res.render(page + '.ejs', { title: 'Page Title' }, (err, html) => { if (err) { // Log the error if needed return res.status(404).send("Page not found."); } res.send(html); }); }); app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });
Dynamic Route Parameter (/:page
)
This route will catch any URL that isn’t caught by a previous more specific route (like /
). The value provided in place of :page
(e.g., "page2"
) is accessible via req.params.page
.
Rendering the Template
In the generic route handler, we render a file whose name is constructed by appending ".ejs"
to the page parameter. This avoids creating individual routes for every page.
Error Handling
If the specified page doesn’t exist, you can handle the error (here we send a 404 message).
Security Note: Allowing arbitrary file names from user input can pose a security risk. To mitigate this, consider validating the supplied page parameter against a list of allowed template names.
Using this approach, you can create links in your EJS files such as:
HTML<a href="/page2">Page 2</a> <a href="/page3">Page 3</a>
Express will handle these routes dynamically without you having to define each one explicitly in your server file.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by NeptunianCollector322 • 1 month ago
Ok, solved it, needed more coffee!
JAVASCRIPTimport express from 'express'; const app = express(); app.set('view engine', 'ejs'); const PORT = 3002; app.get('/*.ejs', (req, res) => { let ejsPage = req.url; if (ejsPage.indexOf('/') === 0) { ejsPage = ejsPage.slice(1) } res.render(ejsPage, { title: 'Page Title'}); }); app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });
problem with my earlier solution was of course the get was trying to handle things other than .ejs (like the request from browser for favicon).
No comments yet.
Answer by GalacticScientist601 • 1 month ago
Well, i sort of fixed it but created another problem!
The following works:
JAVASCRIPTimport express from 'express'; const app = express(); app.set('view engine', 'ejs'); const PORT = 3002; app.get('/*', (req, res) => { console.log(req.url); // res.render('index.ejs', { title: 'Page Title'}); res.render(`.${req.url}`, { title: 'Page Title'}); }); app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });
The pages serve fine, and ejs is processed...
BUUUUT:
Every page request generates an error in the console:
Error: Cannot find module 'ico'
Require stack:
- C:\WDev\testSites\expressEJS\node_modules\express\lib\view.js
- C:\WDev\testSites\expressEJS\node_modules\express\lib\application.js
- C:\WDev\testSites\expressEJS\node_modules\express\lib\express.js
at Function._resolveFilename (node:internal/modules/cjs/loader:1246:15)
at Function._load (node:internal/modules/cjs/loader:1072:27)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:216:24)
at Module.require (node:internal/modules/cjs/loader:1337:12)
at require (node:internal/modules/helpers:139:16)
at new View (C:\WDev\testSites\expressEJS\node_modules\express\lib\view.js:81:14)
at Function.render (C:\WDev\testSites\expressEJS\node_modules\express\lib\application.js:587:12)
at ServerResponse.render (C:\WDev\testSites\expressEJS\node_modules\express\lib\response.js:1049:7)
at file:///C:/WDev/testSites/expressEJS/app.js:25:7
Would be really good to fix that!
No comments yet.
Answer by CometCollector087 • 1 month ago
It would be:
JAVASCRIPTconst viewDir = path.join(__dirname, 'view'); app.get('/', ...); app.get('/*', (req, res, next) => { const viewPath = path.join(viewDir, req.path + '.ejs'); if (viewPath.startsWith(viewDir) && fs.existsSync(viewPath)) { res.render(viewPath); } else { next(); } });
viewPath.startsWith(viewDir)
check prevents from accessing files outside the expected location.
No comments yet.
No comments yet.