Skip to content

Node JS Routing

Aakash Goplani edited this page Feb 23, 2020 · 11 revisions

Topics covered


Adding unique id to path

  • In your Model class, you should have a property named id. This means every time to create an object you will always have this id property.

  • In the view, you can access this property as:

    <% for (let product of prods) { %>
       ...
       <a href="/products/<%=product.id %>" class="btn">Details</a>
       ...
    <% } %>

Extracting data from unique id

  • First step is to register the route to handle such request.

    router.get('/products/:productId', <controller_method_reference>);
    • We can specify dynamic routes parameters by adding colon : e.g. /products/4

    • Be very careful with placement of routes. Placement matters a lot. Lets say you have 3 routes starting with /product

      router.get('/products', <controller_method_reference>);
      router.get('/products/:productId', <controller_method_reference>);
      router.get('/products/delete', <controller_method_reference>);
    • Here, /products/delete will not be reachable
    • Whenever we get request for /delete, our previous route /products/:productId will consider delete as dynamic parameter and it will get executed instead of /delete
    • So you should place dynamic routes with same pattern in the end, as your request gets parsed from top-to-bottom. So correct placement will be:
      router.get('/products', <controller_method_reference>);
      router.get('/products/delete', <controller_method_reference>);
      router.get('/products/:productId', <controller_method_reference>);
  • Second step is to register corresponding controller middleware:

    exports.getProduct = (req, res, next) => {
       const productId = req.params.productId;
       console.log('productId: ', productId);
       res.redirect('/');
    };
    • You can access dynamic parameters from req object. req object has property params that holds all data.
    • Name productId is the name you set in step 1.
    • Alternatively, you can write a function in Model to return a specific product matching given id:
      static findProductById(id, cb) {
         getProductsFromFile(products => {
            const product = products.find(p => p.id === id);
            if (product) {
                cb(product);
            } else {
                cb([]);
            }
         });
      }
    • Then in controller, we can call that method:
    exports.getProduct = (req, res, next) => {
       const productId = req.params.productId;
       Product.findProductById(productId, product => {
         console.log('productId: ', product);
       });
       res.redirect('/');
    };
  • Last step is to assign this controller to router:

    router.get('/products/:productId', controller.getProduct);

Sending data via POST request

  • We send the data as the part of request body when working with POST request. We can take help of hidden variables and pass the required data:

    <form action="/cart" method="POST">
       <button class="btn">Add to Cart</button>
       <input type="hidden" name="productId" value="<%=product.id %>">
    </form>
  • To retrieve this data, we can use req.body.<param_name> in controller:

    exports.postCart = (req, res, next) => {
       Product.findProductById('req.body.productId', (product) => {
          res.render('shop/cart', { 
             prods: product, 
             title: 'Cart', 
             path: '/cart'
          });
       });
    };
  • Note: To make use of req.body, you should have bodyParser enabled at root level:

    /* in app.js file */
    const express = require('express');
    const app = express();
    const bodyParser = require('body-parser');
    ...
    app.use(bodyParser.urlencoded({extended: false}));
    ...
    app.use('/admin', adminRoutes);
    app.use(shoppingRoutes);
    • Here, bodyParser should always come before using any sub-routes, else req.body will always be undefined.

Pass data in includes(- ..)

There are two scenarios in which data is passed through includes():

  1. Inherit Data Passing: This is default. Data gets passed from Controller to HTML file to Includes files. The variable within includes file has access to data from Controller file.

    • Includes File
      <!DOCTYPE html>
      <html lang="en">
      <head>
         <title><%=title %></title>
    • HTML file
         <%- include('../includes/head') %>
      </head>
    • Controller
      exports.getIndex = (req, res, next) => {
         res.render('shop/index', { 
            title: 'Index',
         });
      };
  2. Manual Data Passing: When you need to pass data which is not inherit by controller but by temporary process like loop etc. then in that case you manually pass data as object as second argument to includes() method.

    • Includes File
      <form action="/cart" method="POST">
         <input type="hidden" name="productId" value="<%=product.id %>">
      </form>
    • HTML file
      <% for (let product of prods) { %>
         <article class="card product-item">
            <div class="card__actions">
               <a href="/products/<%=product.id %>" class="btn">Details</a>
               <%- include('../includes/add-to-cart', {product: product}) %>
            </div>
         </article>
      <% } %>

Fetching Query Params

  • You can fetch query params from URL https://abc.pqr/path?edit=true using:
    req.query.<quaery_param_name> i.e. req.query.edit
  • You can then pass this value to view or use within controller for logic processing:
    exports.getEditProduct = (req, res, next) => {
       const editMode = req.query.edit;
       res.render('admin/edit-product', { 
             title: 'Edit Product',
             editing: editMode
       });
    }

Official Express Routing Documentation

Clone this wiki locally