Passing Data to Views
Objectives
By the end of this lesson, you should be able to:
- Pass an object (not just flat values) into a view and access its properties
- Combine route parameters and query parameters with data passed to a view
- Handle a “not found” case before attempting to render
💡 Why this matters: A real page’s data usually comes from a database record, an object with several fields, not a handful of separate flat values. This lesson combines everything from Module 5 (route params, query params) with rendering, exactly the shape of a real profile or detail page.
⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.
Passing an Object to a View
<!-- views/user-profile.ejs -->
<!DOCTYPE html>
<html>
<head><title><%= user.name %>'s Profile</title></head>
<body>
<h1><%= user.name %></h1>
<p>Email: <%= user.email %></p>
<p>Member since: <%= user.joined %></p>
<% if (isOwnProfile) { %>
<a href="/settings">Edit your profile</a>
<% } %>
</body>
</html>
const users = {
1: { name: 'Erin', email: 'erin@example.com', joined: '2024' },
2: { name: 'Jordan', email: 'jordan@example.com', joined: '2025' }
};
app.get('/users/:id', (req, res) => {
const user = users[req.params.id];
if (!user) {
return res.status(404).send('User not found');
}
const isOwnProfile = req.query.viewer === req.params.id;
res.render('user-profile', { user, isOwnProfile });
});
curl "http://localhost:4107/users/1?viewer=1"
<!DOCTYPE html>
<html>
<head><title>Erin's Profile</title></head>
<body>
<h1>Erin</h1>
<p>Email: erin@example.com</p>
<p>Member since: 2024</p>
<a href="/settings">Edit your profile</a>
</body>
</html>
user here is a whole object, <%= user.name %>, <%= user.email %> access its properties inside the template exactly like normal JavaScript property access, nothing EJS-specific about it. isOwnProfile, computed from comparing the route parameter (req.params.id, Module 5 Lesson 4) against a query parameter (req.query.viewer, Module 5 Lesson 5), demonstrates that a view’s data can be computed, not just passed straight through from a database or a fixed object.
Handling “Not Found” Before Rendering
curl -w " [%{http_code}]" http://localhost:4107/users/99
User not found [404]
Looking up users[req.params.id] before rendering, and returning early with res.status(404).send(...) if nothing is found, avoids ever calling res.render() with missing data, which would either throw an error inside the template (trying to read .name off undefined) or silently render broken HTML. Checking for missing data before rendering, not inside the template, is the safer pattern.
Try It
- Create an object keyed by ID (like
usersabove) with at least three entries, each with several fields, and render one of them as a detail page using a route parameter. - Add a “not found” check before rendering, returning a 404 for an ID that doesn’t exist, and confirm it with
curl. - Add a query parameter that toggles a conditional section of the template, similar to
isOwnProfileabove. - Explain, in your own words, why checking for missing data before calling
res.render()is safer than letting the template itself handle a missing value.
Recap
- A view can receive whole objects, not just flat values, template code accesses their properties normally.
- Route parameters and query parameters (Module 5) can be combined and computed into new values before being passed to a view.
- Check for missing or invalid data before rendering, returning an appropriate status code, rather than letting the template encounter
undefined.
Next lesson: partials and layouts, reusing common HTML across multiple pages.