Create a Basic Framework with Express (Part 1)

·

3 min read

We will be using Docker, Node 18, MySQL 8, and Redis. Following Setting Up Node App using Docker post express, nodemon, dotenv packages should be installed.

In this application, we will not be writing a rest app, it will be a basic full stack framework. Packages will be installed once we reach the point where we need to use them.

Create Project Structure

node_modules
public
controllers
database
helpers
middlewares
routes
storage
views

Setup Logging

  1. Install Winston

    npm install winston
    
  2. To be able to write daily logs instead of a single log, install Winston Daily Rotate File

    npm install winston-daily-rotate-file
    
  3. Create a logger.js helper in helpers directory

    • We want to have it log into daily log files of max size of 1GB and will be kept for 10 days.

      const winston = require('winston')
      const { format } = winston
      const { combine, timestamp, printf, splat } = format
      require('winston-daily-rotate-file')
      
      const dailyTransport = new winston.transports.DailyRotateFile({
        // file name format
        filename: 'application-%DATE%.log',
        datePattern: 'YYYY-MM-DD',
        zippedArchive: true,
        // limit each log to 1GB
        maxSize: '1g',
        // format the log
        format: combine(
            timestamp(),
            splat(),
            printf((info) => {
                return `[${info.timestamp}] ${info.level}: ${info.message}`
            })
        ),
        level: 'info',
        // save logs into storage/logs
        dirname: 'storage/logs',
        // save logs for 10 days
        maxFiles: '10d',
      })
      
      const logger = winston.createLogger({
        transports: [dailyTransport],
      })
      
      module.exports = logger
      
  4. Create a errorHandler.middleware.js helper in middlewares directory

    • Log the error and send a response to the user

      // import logger helpers
      const loggerx = require('../helpers/logger')
      
      const errorHandler = {
       // log the error
       logger: (error, request, response, next) => {
           loggerx.error(`${error.message} %o`, error)
           next(error) // calling next middleware
       },
      
       responder: (error, request, response, next) => {
           const status = error.statusCode || 500
           // send status and error message
           return response.status(status).send(error.message)
       }
      }
      module.exports = errorHandler;
      
  5. Add the error handler middleware to index.js and test it out!

     ...
     const express = require('express')
     const app = express()
    
     // import the logger helper so we can test the logging
     const logger = require('../helpers/logger')
     // import the error handler
     const errorHandler = require('./middlewares/errorHandler.middleware.js')
    
     ...
    
     // Cause an error
     app.get('/error', async (req, res, next) => {
         try {
             let error = new Error('This is a major issue!!')
             error.statusCode = 500
             throw error
         } catch (error) {
             return next(error)
         }
     })
    
     // Just logging
     app.get('/log', async (req, res, next) => {
         logger.info('hello from /log')
         res.send('hello')
     })
    
     // log the error
     app.use(errorHandler.logger)
     // send error to user
     app.use(errorHandler.responder)
    
     ...
    
    • Go to localhost:3000/error and localhost:3000/log, the log file can be found in storage/logs (example below)
      [2023-03-20T00:41:29.652Z] error: This is a major issue!! Error: This is a major issue!!
        at /app/index.js:28:25
        at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
        at next (/app/node_modules/express/lib/router/route.js:144:13)
        at Route.dispatch (/app/node_modules/express/lib/router/route.js:114:3)
        at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
        at /app/node_modules/express/lib/router/index.js:284:15
        at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)
        at next (/app/node_modules/express/lib/router/index.js:280:10)
        at expressInit (/app/node_modules/express/lib/middleware/init.js:40:5)
        at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) {
      [stack]: 'Error: This is a major issue!!\n' +
        '    at /app/index.js:28:25\n' +
        '    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n' +
        '    at next (/app/node_modules/express/lib/router/route.js:144:13)\n' +
        '    at Route.dispatch (/app/node_modules/express/lib/router/route.js:114:3)\n' +
        '    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n' +
        '    at /app/node_modules/express/lib/router/index.js:284:15\n' +
        '    at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n' +
        '    at next (/app/node_modules/express/lib/router/index.js:280:10)\n' +
        '    at expressInit (/app/node_modules/express/lib/middleware/init.js:40:5)\n' +
        '    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)',
      [message]: 'This is a major issue!!',
      statusCode: 500
      }
      [2023-03-20T00:41:59.271Z] info: hello from /log