Creating a blogging app with Node.js, MongoDB, and ExpressJS

Josh Hargett
3 min readSep 28, 2023

GitHub Repository

I recently revisited a previous blogging app project I created as part of

’s Complete Web Development Bootcamp on Udemy. I had found an article on how to use Multer to upload images to a MongoDB as binary data, then render the image correctly by converting it when needed. I also wanted to continue working with authentication, so I added basic authentication to the application as well.

This is a personal blog app using Node.js and Express. This article will guide you through my process of building it from start to finish, explaining each piece of code along the way.

Project Initialization

I started by initializing a new Node project:

npm init

Then installed the required packages:

npm install express mongoose passport ejs express-session multer dotenv

This provides:

  • dotenv — To load environment variables
  • express — Web framework
  • mongoose — MongoDB ODM
  • passport — Authentication
  • ejs — Templating
  • multer — File uploads

App Structure

The main entry point is app.js which creates an Express app:

// app.js

const express = require("express");
const app = express();

// ...

app.listen(3000, async () => {
console.log("Server started on port 3000");
});

The app is structured using the MVC pattern:

  • Models — Mongoose schemas
  • Views — EJS templates
  • Controllers — Route handlers in app.js

Environment Variables

I used dotenv to load environment variables from a .env file:

// app.js

require('dotenv').config();

mongoose.connect(process.env.CONNECTION_MONGO, {
useNewUrlParser: true,
useUnifiedTopology: true
});

This allows me to store the MongoDB connection string securely.

Database and Models

My Post and User models use Mongoose schemas:

const postSchema = new mongoose.Schema({
title: String,
content: String
});

const userSchema = new mongoose.Schema({
username: String,
password: String
});

I connect to the MongoDB database specified in my .env:

mongoose.connect(process.env.CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true
});

And compile Mongoose models:

const Post = mongoose.model('Post', postSchema);
const User = mongoose.model('User', userSchema);

Now I can perform CRUD operations on the collections!

Routes and Views

The main routes are:

// app.js 

app.get('/', (req, res) => {
// ...
});

app.get('/register', (req, res) => {
// ...
});

EJS templates are rendered from each route handler:

app.get('/', (req, res) => {
res.render('home');
});

Authentication

Authentication is implemented with Passport.js:

const LocalStrategy = require('passport-local').Strategy;

passport.use(
new localStrategy(async (username, password, done) => {
const user = await User.findOne({ username });
if (!User) {
return done(null, false);
}
if (password !== user.password) {
return done(null, false);
}
return done(null, user);
})
);

This verifies credentials against the User model.

Register and login routes:

app.post('/register', (req, res) => {
// Create user
});

app.post('/login',
passport.authenticate('local'),
(req, res) => {
// Login user
}
);

Sessions are maintained by serializing the user ID.

File Uploads

For image uploads, I used the Multer middleware. Multer handles parsing multipart/form-data from file upload forms and extracts the files. This saves having to manually handle parsing raw HTTP requests.

// app.js

const multer = require('multer');

const upload = multer({
storage: multer.memoryStorage()
});

I configured it to store uploads in memory rather than on disk for simplicity.

To handle a file upload, I add the upload.single() middleware before the route handler:

app.post('/profile', 
upload.single('image'),
(req, res) => {
// req.file contains the uploaded file
}
);

When the form is submitted, Multer will:

  • Parse the multipart form data
  • Extract the file from the field named ‘image’
  • Make the file available on req.file for the route handler to process

This allows easy handling of file uploads within an Express application.

Conclusion

Adding functionality to this blog really helped reinforce my Node.js and MongoDB skills. Some key learnings:

  • The Express framework for routing, middleware, and view templating
  • MongoDB and Mongoose for data modeling and querying
  • Passport.js for robust user authentication
  • Using Multer for seamless file upload handling
  • Environment variables with dotenv to keep secrets secure
  • MVC architecture for separation of concerns

There’s still many features I could continue adding like API support, comments, search, and more advanced authentication.

But completing the core blogging functionality served as fantastic practice for learning real-world Node development. I have a much stronger grasp now on architecting and building full-stack Node.js web apps.

--

--

Josh Hargett

Experienced System Engineer. Passionate about Cloud Computing. Talking about #virtualization #Azure #DevOps #Cloud #Certifications