Post

Episode 8: Using Cookies for Cart & Auth in Handlebars Views

Allow Handlebars views to all pages

Episode 8: Using Cookies for Cart & Auth in Handlebars Views
1
npm install cookie-parser

In app.js:

1
2
const cookieParser = require('cookie-parser');
app.use(cookieParser());

2. Create Middleware to Pass Auth Info to Views

Create middleware/viewContext.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const jwt = require('jsonwebtoken');
const User = require('../models/User');

module.exports = async function viewContext(req, res, next) {
  const token = req.cookies.token;

  if (token) {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      const user = await User.findById(decoded.id).populate('cart.product');
      res.locals.user = {
        id: user._id,
        email: user.email,
        role: user.role,
        cartCount: user.cart.reduce((sum, item) => sum + item.quantity, 0)
      };
    } catch (err) {
      res.locals.user = null;
    }
  } else {
    res.locals.user = null;
  }

  next();
};

Register in app.js:

1
2
const viewContext = require('./middleware/viewContext');
app.use(viewContext);

This makes user available in all Handlebars templates.

3. Update main.hbs Layout to Show Auth Info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
  <a class="navbar-brand" href="/">MyShop</a>
  <div class="collapse navbar-collapse">
    <ul class="navbar-nav ms-auto">
      
        <li class="nav-item">
          <a class="nav-link" href="/cart">Cart ()</a>
        </li>
        <li class="nav-item">
          <span class="nav-link">Hello, </span>
        </li>
        <li class="nav-item">
          <form action="/api/auth/logout" method="POST">
            <button class="btn btn-link nav-link" type="submit">Logout</button>
          </form>
        </li>
      
        <li class="nav-item">
          <a class="nav-link" href="/login">Login</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="/register">Register</a>
        </li>
      
    </ul>
  </div>
</nav>

4. Render Cart Page in Views

In routes/pages.js:

1
2
3
4
5
6
7
8
9
10
11
router.get('/cart', async (req, res) => {
  if (!res.locals.user) {
    return res.redirect('/login');
  }

  const user = await User.findById(res.locals.user.id).populate('cart.product');
  res.render('cart', {
    title: 'My Cart',
    cart: user.cart
  });
});

In views/cart.hbs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<h2>Your Shopping Cart</h2>


  <ul class="list-group mb-3">
    
      <li class="list-group-item d-flex justify-content-between">
        <div>
          <strong></strong> ()
        </div>
        <div>$</div>
      </li>
    
  </ul>
  <form action="/api/orders/checkout" method="POST">
    <button class="btn btn-success">Checkout</button>
  </form>

  <p>Your cart is empty.</p>

5. Enable Logout Button

In routes/auth.js:

1
2
3
4
router.post('/logout', (req, res) => {
  res.clearCookie('token');
  res.redirect('/');
});

Notes:

  • res.locals.user is accessible in any view thanks to middleware.
  • You can later show admin panels with using a Handlebars helper.
  • Don’t forget CSRF protection if doing full POST forms in production.
This post is licensed under CC BY 4.0 by the author.