Building a Blog App with Node.js and MongoDB from Scratch

·

18 min read

Building a Blog App with Node.js and MongoDB from Scratch

Introductions

As part of our second-semester project by AltSchool Africa, we were asked to create a simple API for a blog. The blog application will be built in NodeJS with Express.js framework, and the database used will be MongoDB.

Basic Concepts

What is node.js?

Node.js is an open-source and cross-platform runtime environment that runs javascript outside of the browser. It was designed to build scalable network applications.

what is MongoDB?

MongoDB is a free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program. It is a database that is used to store and retrieve data for applications.

What is Express.js?

Express.js is a web application framework for Node.js, designed for building web applications and APIs. It is the de facto standard server framework for Node.js and is designed to be lightweight and flexible.

Prerequisites

The following are required to complete this tutorial:

  • Node.js

  • A MongoDB Atlas account to create our app’s database

  • A text editor, such as Visual Studio Code

  • Gitbash

Setup

The project directory structure is shown below

// Project structure
|--Blog
|  |──config
|  |  |──config.js
|  |──controllers
|  |  |──blogController.js
|  |  |──userController.js
|  |──db
|  |  |──dbConfig.js
|  |──middleware
|  |  |──logging
|  |  |  |──httpLogger.js
|  |  |  |──logger.js
|  |  |──authentication.js
|  |  |──rateLimit.js
|  |  |──validation.js
|  |──model
|  |  |──post.js
|  |  |──user.js
|  |──routes
|  |  |──blogRoute.js
|  |  |──userRoute.js
|  |──utils
|  |  |──helper.js
|──.env
|──index.js
|──package.json
|──package-lock.json

Let's get started with the Node.js code.

Open the terminal and make a folder called blog and enter into the blog folder.

mkdir blog && cd blog

Then, in the terminal, type npm init -y to create a package.json file.

After that, you must install some packages before proceeding. These packages will be used throughout the project. Install the following packages:

  • Express: a web application framework for Node.js, designed for building web applications and APIs

  • Mongoose: an object modeling tool for MongoDB that allows you to define models for the documents in your MongoDB collections. It provides a simple, flexible, and powerful way to work with MongoDB from your Node.js applications.

  • dotenv: a library that allows developers to store configuration variables in a separate file, called a ".env" file, in their project.

  • bcrypt: a JavaScript library that provides functions for hashing passwords using the bcrypt algorithm.

  • winston: a logging library for Node.js. It provides a simple and flexible way to log messages.

  • morgan: a middleware library for Node.js that helps with HTTP request logging

  • express-rate-limit: a middleware for the Express.js web framework that limits the number of requests that a user or IP address can make to an API or website in a specified period.

  • helmet: a JavaScript library that provides security-related middleware functions for Express, it helps protect web applications from various types of HTTP attacks.

  • http-status-codes: JavaScript library that provides constants for HTTP status codes.

  • joi: a JavaScript library for validating and manipulating data. It can be used to validate user input.

  • jsonwebtoken: a standardized, open-source method of securely transmitting information between parties as a JSON object. It is often used in authentication and authorization processes, allowing users to securely access protected resources.

  • passport: a middleware that simplifies the process of authentication in Node.js applications. It supports multiple authentication strategies

  • passport-jwt: enables the use of JSON Web Tokens (JWT) for authentication.

  • Moment: A JavaScript date library for parsing, validating, manipulating, and formatting dates.

in the blog folder using the terminal.

npm i express mongoose dotenv bcrypt dotenv express-rate-limit helmet http-status-codes joi jsonwebtoken morgan passport passport-jwt winston moment

When the packages are installed, make a file called .env touch .env. After that, launch MongoDB and copy the link to our project.

EXPIRE_TIME = 1h
JWT_SECRET = somethingrandom
MONGODB_URI = mongodb+srv://username:password@cluster0.ie4et0o.mongodb.net/database-name?retryWrites=true&w=majority

Let's make our main file and call it index.js. This is where all routes and middleware will be assembled.

Configuration Setup

In the blog folder, make another directory called config, navigate into this directory and create a file called Config.js using this command mkdir config && cd config && touch Config.js to hold all our configurations.

// Config.js
require('dotenv').config();

module.exports = {
    MONGODB_URI: process.env.MONGODB_URI,
    JWT_SECRET: process.env.JWT_SECRET,
    EXPIRE_TIME: process.env.EXPIRE_TIME,
}

Initial Route Setup

Let's start with the first route, which checks to see if everything is in order. The Node.js Express package allows you to create routes, which is how the majority of the Internet works. Most back-end languages, such as Node.js and Java, provide the ability to create these routes that interact with databases. The first route does not interact with the database and simply returns a text when accessed via a GET request.

In the blog folder, create an index.js file. First, import the Express and dotenv packages. Next, we create an express instance and use it to create a port variable that will run on the port you will be given, or port 3000 if no ports are available.

const express = require('express')

//App Config
const app = express()
const port = process.env.PORT || 3000
//DB Config
//Middleware
//API Endpoints

//Listener
app.listen(port, () => console.log(`Listening on localhost: ${port}`))

Access to the Database and the Network

You must create a database user in MongoDB and grant network access. and use dotenv to connect it to the initial routes because we've saved our link in the .env file

//dbConfig.js
const mongoose = require('mongoose');
const CONFIG = require('../config/config');

function connectToDataBase() {
    mongoose.connect(CONFIG.MONGODB_URL)

    mongoose.connection.on("connected", () => {
        console.log('Database connected successfully')
    })

    mongoose.connection.on("error", (err) => {
        console.log('An error occurred while trying to connect to database', error)
    })
};
module.exports = connectToDataBase

Database Schema, Middlewares, Controllers and Routes

Database Schema

MongoDB, the database we're using, stores data in JSON format rather than the regular table structure found in traditional databases like MySQL. You create the schema file that MongoDB requires. It describes how fields in MongoDB are stored.

First, make a folder called models. Create two files inside the folder and name them user.js and blog.js

  • user.js

//User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const Schema = mongoose.Schema;

const userSchema = new Schema({
    firstname: {
        type: String,
        required: true,
        lowercase: true,
        trim: true
    },
    lastname: {
        type: String,
        required: true,
        lowercase: true,
        trim: true
    },
    password: {
        type: String,
        required: true,
        trim: true
    },
    email: {
        type: String,
        unique: true,
        required: true,
        lowercase: true
    }},
    {timestamps: true}
);

// Hashing User password to DB
userSchema.pre('save', async function hashPassword (next) {
    const hash = await bcrypt.hash(this.password, 10);

    this.password = hash;
    next()
});

userSchema.methods.isValidPassword = async function(enteredPassword){
    return await bcrypt.compare(enteredPassword, this.password);
}


const userModel = mongoose.model('user', userSchema);
module.exports = userModel
  • We need to encrypt passwords while storing them in the database, for Hashing (encrypting) passwords we will use the bcrypt package for it. Encrypting passwords while registering new users we have to use mongoose middleware (hook) like pre(save). Every time the User schema is called, first it will execute pre-hook and later do its work. So to encrypt the password we have to use `bcrypt.hash()` method inside pre(save) hook.

    The password inside `req.body` was changed to the hashed password in the hook.

  • We stored Hashed(encrypted) password in our database but we have a normal password now to match with the hash. this problem can be solved using `bcrypt.compare()` method of the bcrypt package.

    post.js

    
      //post.js
      const mongoose = require('mongoose');
      const Schema = mongoose.Schema;
    
      const blogSchema = new Schema({
          title: {
              type: String,
              required: true,
              unique: true
          },
          description: {
              type: String,
          },
          author: {
              type: mongoose.Schema.Types.ObjectId,
              ref: 'user',
              required: true,
          },
          state: {
              type: String,
              enum: ["draft", "published"],
              default: "draft"
          },
          read_count: {
              type:Number,
              default: 0
          },
          likes: Number,
          reading_time: String,
          tags: [String],
          body: {
              type: String,
              required: true
          }},
          {timestamps: true}
      )
    
      const blogModel = mongoose.model('blogs', blogSchema);
      module.exports = blogModel
    

So in the post schema, each post is supposed to have,

1. title — title of the post

2. description — description of the post

3. tags — each post should have tags ( tags provide a useful way to group related posts together )

4. author — each post have the ID of the author

5. state — each post has a default draft state upon creation( The author can then decide to publish )

6. read_count — each post contains a read_count field that signifies how many times the post has been viewed when published (default is 0)

  1. reading_time — each post has a reading_time field that determines how long it takes to read the post

8. body — each post has a body

9. timestamps — each post has timestamps, a time of the creation

Middlewares

logging middleware

In the logging folder, create two files logger.js and httpLogger.js. Then we’ll be able to declare our logger using Winston:

//logger.js
const winston = require('winston')
const options = {
    file: {
        level: 'info',
        filename: './log/app.log',
        handleExceptions: true,
        json: true,
        maxsize: 5242880,
        maxFiles: 5,
        colorize: true
},
    console: {
        level: 'debug',
        handleExceptions: true,
        json: false,
        colorize: true
    }
}

const logger = winston.createLogger({
    levels: winston.config.npm.levels,
    transports: [
        new winston.transports.File(options.file),
        new winston.transports.Console(options.console)
    ],
    exitOnError: false
})

module.exports = logger

Let’s take a look at what we’re setting up here. We’re passing a config object into the createLogger method that we’re calling.

The config object has a few fields. format defines how we would like our log messages to be formatted. There are a number of options for this, which can be found in more detail here.

Finally, the transports field declares the storage type for the log messages. In our case, we’re just outputting these logs to the console and file app.log. The transport has a level declared.

//httpLogger.js
const logger = require('./logger')
const morgan = require('morgan')
const { json } = require('express')

const format = json({
    method: ':method',
    url: ':url',
    contentLength: 'res[content-length]',
    responseTime: ':response-time'
})

const httpLogger = morgan(format, {
    stream: {
        write: (message) => {
            const {
                method,
                url,
                status,
                contentLength,
                responseTime
            } = JSON.parse(message)

            logger.info('HTTP Access Log', {
                timestamp: new Date().toString(),
                method,
                url,
                status: Number(status),
                contentLength,
                responseTime: Number(responseTime)
            })
        }
    }
})

module.exports = httpLogger

Here we’re passing some arguments into morgan. The first argument is format which we specified, the second is options. Our format argument is simply a string of predefined tokens, as per the Morgan docs.

The options argument is an object containing a single field: stream. This indicates the output stream for our logs. In our case, we pass an object with a callback function which simply calls the http method on the logger instance that we set up earlier. By doing this, our Morgan HTTP log will be passed to the Winston logger, where additional formatting such as timestamp will be added.

Now we can use our logger as a middleware.

Rate-limiting Middleware

//rateLimit.js
const rateLimit = require('express-rate-limit')

const limiter = rateLimit({
    windowMs: 20 * 60 * 60, // 20 minutes
    max: 50, //Limit each IP to 50 request per window (per 20 minutes)
    standardHeaders: true, //Return rate limit info in the RateLimit-* headers
    legacyHeaders: false // Disable the X-RateLimit-* headers 
})

module.exports = limiter

we import the express-rate-limit in our code just under the express import. Then we can configure the time box in milliseconds and the maximum number of requests per IP address (max).

You can read about them in more detail, in the official docs.

Validation Middleware

const Joi = require('joi');
const {StatusCodes} = require('http-status-codes')

const addBlogSchema = Joi.object({
    title: Joi.string()
        .max(255)
        .trim()
        .required(),
    description: Joi.string()
        .min(10)
        .trim(),
    author: Joi.string(),
    state: Joi.string(),
    tags: Joi.array()
        .items(Joi.string()),
    body: Joi.string()
        .required()         
});

const updateBlogSchema = Joi.object({
    title: Joi.string()
        .max(255)
        .trim(),
    description: Joi.string()
        .min(10)
        .trim(),
    author: Joi.string(),
    state: Joi.string(),
    tags: Joi.array()
        .items(Joi.string()),
    body: Joi.string()       
});


const addUserSchema = Joi.object({
    firstname: Joi.string()
        .max(255)
        .trim()
        .required(),
    lastname: Joi.string()
        .max(255)
        .required()
        .trim(),
    password: Joi.string()
        .min(7)
        .trim()
        .required(),
    email: Joi.string()
        .email()
        .required()         
});


async function addBlogValidationMW (req, res, next) {
    const blogPayLoad = req.body

    try {
        await addBlogSchema.validateAsync(blogPayLoad)
        next()
    } catch (error) {
        next({
            message: error.details[0].message,
            status: StatusCodes.BAD_REQUEST
        })
    }
};

async function updateBlogValidationMW (req, res, next) {
    const blogPayLoad = req.body

    try {
        await updateBlogSchema.validateAsync(blogPayLoad)
        next()
    } catch (error) {
        next({
            message: error.details[0].message,
            status: StatusCodes.BAD_REQUEST
        })
    }
};

async function addUserValidationMW (req, res, next) {
    const blogPayLoad = req.body

    try {
        await addUserSchema.validateAsync(blogPayLoad)
        next()
    } catch (error) {
        next({
            message: error.details[0].message,
            status: StatusCodes.BAD_REQUEST
        })
    }
};

module.exports = {
    addBlogValidationMW,
    updateBlogValidationMW,
    addUserValidationMW
}

We create the schema that Joi will validate against. Since a schema is designed to resemble the object we expect as an input.

Authentication Middleware

we are going to be creating a middleware to authenticate a user and passport package will be used for authentication.

The Passport JWT authentication strategy is created and configured. fromAuthHeaderAsBearerToken() creates a new extractor that looks for the JWT in the authorization header with the scheme bearer. This middleware authenticates the user's request. Using passport.authenticate() and specifying the jwt strategy, the request is authenticated by checking for the standard Authorization header and verifying the verification token, if any. If unable to authenticate request, an error message is returned.

//authentication.js
const passport = require('passport');
const userModel = require('../model/user');
const Jwtstrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const CONFIG = require('../config/config')

passport.use(
    new Jwtstrategy(
        {
            secretOrKey: CONFIG.JWT_SECRET,
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
        },
        function (payload, done) {
            userModel.findById(payload.id, function(error,user) {
                if(error) {
                    return done(null, user)
                }
                if (user) {
                    return done(null, user)
                } else {
                    return done(null, false)
                }
            })
        }
    )
)

Helper functions

const CONFIG = require("../config/config");
const jwt = require('jsonwebtoken');
const userModel = require('../model/user')
const {StatusCodes} = require('http-status-codes')

// Helper function for calculating reading time of blog post
const readingTime = (post) => {
    // get number of words in blogpost
    const wordCount = post.split(' ').length
    // get the number of words per minute
    // assuming an average person reads 200 words per minute
    const countPerMinute = wordCount / 200
    const readingTime = Math.ceil(countPerMinute)
    return ` ${readingTime} Minute Read Time`  
}

//Helper function to sign token
const jwtSignToken = (user) => {
    return jwt.sign(user, CONFIG.JWT_SECRET, { expiresIn: CONFIG.EXPIRE_TIME })
}

// Helper function to validate user input email and password 
const validateUser = async(email, password) => {
    let user = await userModel.findOne({
        email: email
    }).select('+password')

    if (!user){
        return false
    }
    const verifyPassword = await user.isValidPassword(password, user.password)
    if(!verifyPassword){
        return false
    }
    return user
}


module.exports = {
    readingTime,
    jwtSignToken,
    validateUser
}

Controllers

  • userController.js

    
      const userModel = require('../model/user')
      const {StatusCodes} = require('http-status-codes')
      const {validateUser, jwtSignToken} = require('../utils/helper')
    
      const signup = async (req, res, next) => {
          const {
              firstname, lastname,
              password, email
          } = req.body
    
         try {
          let userExist = await userModel.findOne({email: email})
          if (userExist) {
              return res.status(StatusCodes.BAD_REQUEST).json({
                  status: false,
                  msg: "This user already exist"
              })
          }
    
          const user = await userModel.create({
              firstname,
              lastname,
              password,
              email
          })
          res.status(StatusCodes.ACCEPTED).json({
              status: true,
              msg: "User created successfully",
              user
          })
         } catch (error) {
             next(error)
         }
      }
    
      const login = async(req, res, next) => {
          const {email, password} = req.body
    
          try {
              const user = validateUser(email, password)
              if (!user){
                  return res.status(StatusCodes.UNAUTHORIZED).json("Email or Password does not exist")
              }
              const body = {_id: user._id, email: user.email}
              const token = jwtSignToken(body)
              res.status(StatusCodes.OK).json({
                  status: true,
                  msg: "Login successful",
                  token
              })
          } catch (error) {
              next(error)
          }
      }
    
      module.exports = {
          signup,
          login
      }
    

user.js controller file would handle creating a new user and logging in a user.

  • In the signup function we start by checking if a user has been signed up before by connecting to our database model UserModel.findOne and checking if the passed email has been sent before. If that is so, we will send an error. We finally send the data passed from the client req.body to our database through the model using the UserModel.create function and return a success message with our res parameter.

  • In the login function. We start by validating the user input email and password using the helper function we created. This function checks if user trying to login in is in our database records through the email using our model UserModel.findOne and also validate the user password against the one stored in the database. If the user isn't found, we return an error. After that, we use the user information as a payload to sign a jwt token in order to use it for user authorization.

After successfully signing our jwt token, we return a success message and the token to the client.

  • blogController.js

const blog = require("../model/post");
const moment = require('moment')
const {readingTime} = require("../utils/helper");

const createPost = async(req, res, next) => {
    const {title, 
            description, 
            tags, 
            body} = req.body
    try {
        const newPost = await blog.create({
            title,
            description: description || title,
            author: req.user._id,
            tags,
            body,
            reading_time: readingTime(body)
        })
        return res.status(201).json({
            status: true,
            newPost
        })
    } catch (error) {
        next(error)
    }
}


const getAllPost = async(req, res, next) => {
    console.log(req.user)
    try {
        const {query} = req;
        const {
            author,
            tags,
            state,
            read_count = 'asc',
            reading_time = 'asc',
            order_by = 'timestamp',
            per_page = 20,
            page =  parseInt(req.query.page)-1 || 0,
            timestamp

        } = query;

        const findQuery = {}

        if (timestamp) {
            findQuery.timestamp = {
                $gt: moment(timestamp).startOf('day').toDate(),
                $lt: moment(timestamp).endOf('day').toDate()
            }
        };

        if (author){
            findQuery.author = author
        }
        if(state) {
            findQuery.state = {$eq: "published"}
        }
        if(tags){
            findQuery.tags = {$in: tags}
        }

        const sortQuery = {};
        const sortAttr = order_by.split(',')

        for(const attr of sortAttr){
            if (read_count === 'asc' && reading_time === 'asc'){
                sortQuery[attr] = 1
            }

            if (read_count === 'desc' && reading_time === 'desc'){
                sortQuery[attr] = -1
            }
        }

        const posts = await blog.find(findQuery)
            .sort(sortQuery)
            .skip(page)
            .limit(per_page)
            .populate('author')


        return res.status(200).json({
            status : true,
            page: page+1,
            posts
        })    

    } catch (error) {
        next(error)
    }
}


const getUserPosts = async (req, res, next) => {
    try {
        const id = req.user._id
        const page = parseInt(req.query.page) || 1;
        const limit = parseInt(req.query.limit) || 10;
        const skip = (page - 1) * limit;
        const state = req.query.state || 'published'

        const posts = await blog.find({author: id, state: state})
            .skip(skip)
            .limit(limit)
            .populate('author')

        return res.status(200).json({
            status: true,
            page: page,
            posts
        })
    } catch (error) {
        next(error)
    }
}

const getPostbyId = async (req, res, next) => {
    const id = req.params.id
    try {
        const posts = await blog.findById({_id: id})
            .where({state: 'published'})
            .populate('author')

        if(!posts){
            return res.status(404).json({
                status: false,
                message: `Blog Not found`
            })
        }
        posts.read_count += 1;
        await posts.save()
        res.status(200).json({
            status: true,
            posts
        })
    } catch (error) {
        next(error)
    }

}


const updatePost = async(req, res, next) => {
    let body = req.body
    const {id} = req.params;

    try {
        const post = await blog.findByIdAndUpdate(
            id,
            {$set: body},
            {new: true}
        )
        post.updatedAt = new Date()

        return res.status(200).json({
            status: true,
            post: post
        });

    } catch (error) {
        next(error)
    }
}

const updateState = async(req, res, next) => {
    const {id} = req.params;
    const state = req.body.state

    try {
        const post = await blog.findById(id)
        if (post.state === 'published') {
            return res.status(400).send('Post has already been published')
        }
        post.state = state;
        post.updatedAt = new Date()
        await post.save()
        return res.status(200).json({
            status: true,
            post
        })
    } catch (error) {
        next(error)
    }
}


const deletePost = async(req, res, next) => {
    const {id} = req.params
    try {
        const post = await blog.findByIdAndDelete(id)
        if(!post) {
            return res.status(404).json({
                status: false,
                msg: "Post with id not found"
            })
        }
        return res.status(200).json({
            status: true,
            msg: null
        })
    } catch (error) {
        next(error)
    }
}

module.exports = {
    createPost,
    getAllPost,
    getPostbyId,
    getUserPosts,
    updatePost,
    updateState,
    deletePost
}
  • In the getAllPost function, we return all published post for users to read, Note that users that are not logged in will be allowed to access this. The following queries: page = 0, perpage = 20, tag, author, title, created_at, sortby = "created_at", sort = "asc" can be specified from the client to return specific information needed from the published post

  • In the getPostbyId function, we will return only a specific published blog. In order to know which blog to return, we will accept a query parameter from the client which is an id of the blog we want to return

  • In the createBlog function, we create a blog by passing the required fields and informations, when a blog is created with our blog model, it will be saved as a draft so it wont be returned with published blogs.

  • The getUserPosts function allows us get the blogs of a specific user/author. We use population to get the blogs of an author, only the author's id was needed. We can get both the author's published blogs and drafts.

  • The deleteBlog function deletes a blog post with a specific id passed from the client as a query parameter.

  • The updatePost and updateState update users post and state (to published) respectively.

Routes

Routes are endpoints in which requests will be made to our application, let's start creating them.

To get started, create the folder routes in the root directory and add the following files blog.routes.js and user.routes.js . Add the following lines of code to them.

  • userRoutes.js

    ```javascript

    const express = require('express'); const userRoute = express.Router(); const userController = require('../controller/userController'); const {addUserValidationMW} = require('../middleware/validation');

    userRoute.post('/signup', addUserValidationMW, userController.signup); userRoute.post('/login', userController.login)

module.exports = userRoute
```

we import and make use of the middleware we created earlier. We protected and validated the necessary routes.

  • blogRoute.js

    
      const express = require('express');
      const blogController = require("../controller/blogController");
      const {addBlogValidationMW,
          updateBlogValidationMW,
      } = require('../middleware/validation');
      const passport = require('passport')
    
      const postRoute = express.Router()
    
      postRoute.get('/', blogController.getAllPost);
      postRoute.get('/:id', blogController.getPostbyId);
    
      postRoute.get('/user/:id', passport.authenticate("jwt", { session: false }), blogController.getUserPosts)
    
      postRoute.post('/create', addBlogValidationMW, passport.authenticate("jwt", { session: false }), blogController.createPost);
    
      postRoute.put('/:id', updateBlogValidationMW, passport.authenticate("jwt", { session: false }), blogController.updatePost);
    
      postRoute.patch('/state/:id', passport.authenticate("jwt", { session: false }), blogController.updateState)
    
      postRoute.delete('/:id', passport.authenticate("jwt", { session: false }), blogController.deletePost);
    
      module.exports = postRoute
    

we import and make use of the middleware we created earlier. We protected and validated the necessary routes.

Updating DB Config

Since we have created a logging middleware, we would be updating our /dbConfig.js file

//dbConfig.js
const mongoose = require('mongoose');
const CONFIG = require('../config/config');
const logger = require('../middleware/logging/logger')


function connectToDataBase() {
    mongoose.connect(CONFIG.MONGODB_URL)

    mongoose.connection.on("connected", () => {
        logger.info('Database connected successfully')
    })

    mongoose.connection.on("error", (err) => {
        logger.info('An error occurred while trying to connect to database')
        logger.error(error)
    })
};
module.exports = connectToDataBase

Adding all to our entry file

app.js

const express = require('express');
const bodyParser = require('body-parser');
const logger = require('./middleware/logging/logger');
const limiter = require('./middleware/rate.limit')
const helmet = require('helmet')
const userRoute = require("./routes/userRoute");
const blogRoute = require("./routes/blogRoute")
const CONFIG = require('./config/config');
const connectToDB = require("./db/dbConfig");
const cors = require('cors')


connectToDB()
require("./middleware/authorization")

const app = express()
//security
app.use(helmet())

app.use(cors())
app.use(limiter)

app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())


app.use('/api', userRoute)
app.use('/api/blog', blogRoute)


app.get('/', (req, res) => {
    res.send("Welcome to Eddy Blog")
});

// Error handler middleware
app.use((error, req, res, next) => {
    console.error(error)
    const errorStatus = error.status || 500
    res.status(errorStatus).send(error.message)
    next()
})

app.listen(CONFIG.PORT, () => {
    logger.info(`Server is listening on port ${CONFIG.PORT}`)
});

The app.js is our base file, we import the necessary packages installed and the modules we created, we also use the middlewares created by us and the ones provided by express. Helmet is a security middleware used to secure our API.

Conclusion

I hope you found the article useful, you can check out the full project repository on GITHUB

Did you find this article valuable?

Support Eddy's Space by becoming a sponsor. Any amount is appreciated!