CodingNic

Server-Rendered Views

Passing Data to Views

Server-Rendered Views 10 min read

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

html
<!-- 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>
javascript
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 });
});
bash
curl "http://localhost:4107/users/1?viewer=1"
text
<!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

bash
curl -w " [%{http_code}]" http://localhost:4107/users/99
text
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

  1. Create an object keyed by ID (like users above) with at least three entries, each with several fields, and render one of them as a detail page using a route parameter.
  2. Add a “not found” check before rendering, returning a 404 for an ID that doesn’t exist, and confirm it with curl.
  3. Add a query parameter that toggles a conditional section of the template, similar to isOwnProfile above.
  4. 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.