Labs ICT
Pro Login

Error Handling

Gracefully handling missing data and errors.

Checking with typeof

Before accessing properties, check the type of your data. This prevents errors when data might not be what you expect.


<% if (typeof user !== 'undefined' && user) { %>
  <p>Hello, <%= user.name %></p>
<% } else { %>
  <p>Please log in.</p>
<% } %>
    

The typeof check ensures the variable exists before you try to use it.

Try it Yourself →

Using && Guards

The && operator provides a shorter way to guard against undefined values. It only proceeds if the first part is truthy.


<p>Name: <%= user && user.name || 'Guest' %></p>
<p>Email: <%= user && user.email || 'No email' %></p>
    

This pattern is clean and readable for simple fallback values.


<% if (user && user.posts && user.posts.length) { %>
  <h3>Your Posts</h3>
  <% user.posts.forEach(post => { %>
    <article><%= post.title %></article>
  <% }); %>
<% } %>
    

Optional Chaining Pattern

While EJS doesn't support the ?. operator directly, you can achieve similar safety by chaining checks or using a helper function.


<% const city = user && user.address && user.address.city; %>
<p>City: <%= city || 'Unknown' %></p>
    

For cleaner templates, create a helper function in your middleware.


app.locals.get = (obj, path, defaultVal) => {
  const keys = path.split('.');
  let result = obj;
  for (const key of keys) {
    result = result && result[key];
    if (result === undefined) return defaultVal;
  }
  return result || defaultVal;
});
    

Now you can safely access nested properties.


<p>City: <%= get(user, 'address.city', 'Unknown') %></p>
<p>Phone: <%= get(user, 'contacts.phone', 'N/A') %></p>
    
Try it Yourself →

Try/Catch in Templates

For complex logic that might throw errors, wrap it in a try/catch block. This prevents template rendering from failing completely.


<% try { %>
  <% const data = JSON.parse(jsonString); %>
  <p>Parsed value: <%= data.value %></p>
<% } catch (e) { %>
  <p>Unable to display data.</p>
<% } %>
    

Keep try/catch blocks small and focused. Move complex logic to your route handlers when possible.

Fallback Values with ||

The || operator is the simplest way to provide default values. If the left side is falsy, the right side is used.


<p>Name: <%= user.name || 'Anonymous' %></p>
<p>Role: <%= user.role || 'User' %></p>
<p>Posts: <%= user.postCount || 0 %></p>
    

Remember that 0, '', and null are all falsy. Use explicit checks when you need to distinguish between them.


<p>Items: <%= typeof cart.count === 'number' ? cart.count : 0 %></p>
    
Try it Yourself →

Error Boundaries

Wrap sections of your template in error boundaries to gracefully handle failures in specific parts of the page.


<%- include('partials/header') %>

<% try { %>
  <%- include('partials/sidebar', { items: menuItems }) %>
<% } catch (e) { %>
  <p class="error">Sidebar unavailable</p>
<% } %>

<main>
  <%= content %>
</main>

<%- include('partials/footer') %>
    

This approach ensures that a failure in one component doesn't break the entire page.