Node.js web application development enables the development of scalable and effective platforms. You will learn how to put up a basic application with a dashboard interface and user authentication in this tutorial. The database will be MongoDB, the web framework will be Express, and the templating will be done with EJS. For simpler deployment, we will also use Docker to containerize our application.
![](http://fulltrusteuropeanhosting.hostforlife.eu/image.axd?picture=2016%2f8%2fhflnet51.jpg)
Before you begin, ensure you have the following installed on your system:
- Node.js and npm
- Docker
- Docker Compose
- A text editor or IDE of your choice
Step 1. Setting Up Your Project
Initialize a New Node.js Project
- Open your terminal.
- Create a new directory for your project and navigate into it:
mkdir nodejs-app
cd nodejs-app
Initialize a new Node.js project:
npm init -y
Install Dependencies
Install Express, EJS, Mongoose, and other necessary packages:
npm install express ejs mongoose bcryptjs express-session passport passport-local
Step 2. Create the Application Structure
Create the necessary directories and files for your application:
nodejs-app/
│
├── app.js
├── Dockerfile
├── docker-compose.yml
├── package.json
├── routes/
│ ├── index.js
│ ├── user.js
├── config/
│ ├── passportConfig.js
├── views/
│ ├── login.ejs
│ ├── register.ejs
│ ├── dashboard.ejs
│ ├── layout.ejs
│ └── partials/
│ └── messages.ejs
├── models/
│ ├── User.js
└── public/
├── css/
├── style.css
Here is a shell script (create-nodejs-app.sh) to automatically create the directory structure for your Node.js application:
#!/bin/bash
# Define the folder structure
mkdir -p nodejs-app/{routes,config,views,models,public/css}
# Create the necessary files
touch nodejs-app/app.js
touch nodejs-app/Dockerfile
touch nodejs-app/docker-compose.yml
touch nodejs-app/package.json
touch nodejs-app/routes/index.js
touch nodejs-app/routes/user.js
touch nodejs-app/config/passportConfig.js
touch nodejs-app/views/login.ejs
touch nodejs-app/views/register.ejs
touch nodejs-app/views/dashboard.ejs
touch nodejs-app/views/layout.ejs
touch nodejs-app/views/partials/messages.ejs
touch nodejs-app/models/User.js
touch nodejs-app/public/css/style.css
# Confirmation message
echo "Node.js app folder structure created successfully!"
Step 3. Developing the Backend
Set Up Express and Middleware
In app.js, Set up your Express application and middleware to handle requests.
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const flash = require('connect-flash');
const passport = require('passport');
const app = express();
const port = 3000;
// Passport Config
require('./config/passportConfig')(passport);
// DB Config
mongoose.connect('mongodb://mongo:27017/nodejs-app', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
// Middleware
app.use(express.urlencoded({ extended: false }));
app.use(express.static('public'));
// EJS
app.set('view engine', 'ejs');
// Express Session
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Connect Flash
app.use(flash());
// Global Variables
app.use((req, res, next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
// Routes
app.use('/', require('./routes/index'));
app.use('/users', require('./routes/user'));
// Start Server
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
Configure Passport for user authentication
Database Models
Use Mongoose to define models in models/User.js.
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
User Authentication
Implement registration and login functionality in routes/user.js.
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const passport = require('passport');
const User = require('../models/User');
// Register Page
router.get('/register', (req, res) => res.render('register'));
// Register Handle
router.post('/register', (req, res) => {
const { name, email, password, password2 } = req.body;
let errors = [];
// Validation
if (!name || !email || !password || !password2) {
errors.push({ msg: 'Please fill in all fields' });
}
if (password !== password2) {
errors.push({ msg: 'Passwords do not match' });
}
if (errors.length > 0) {
res.render('register', { errors, name, email, password, password2 });
} else {
User.findOne({ email: email })
.then(user => {
if (user) {
errors.push({ msg: 'Email already exists' });
res.render('register', { errors, name, email, password, password2 });
} else {
const newUser = new User({ name, email, password });
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save()
.then(user => {
req.flash('success_msg', 'You are now registered and can log in');
res.redirect('/users/login');
})
.catch(err => console.log(err));
});
});
}
});
}
});
// Login Page
router.get('/login', (req, res) => res.render('login'));
// Login Handle
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/users/login',
failureFlash: true
})(req, res, next);
});
// Logout Handle
router.get('/logout', (req, res) => {
req.logout(() => {
req.flash('success_msg', 'You are logged out');
res.redirect('/users/login');
});
});
module.exports = router;
Utilize Passport for handling authentication. config/passportConfig.js
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// Load User model
const User = require('../models/User');
module.exports = function (passport) {
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({ email: email })
.then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
})
.catch(err => console.log(err));
}));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
};
routes/user.js
const express = require('express');
const router = express.Router();
const { ensureAuthenticated } = require('../config/auth');
// Welcome Page
router.get('/', (req, res) => res.render('welcome'));
// Dashboard Page
router.get('/dashboard', ensureAuthenticated, (req, res) =>
res.render('dashboard', {
user: req.user
})
);
module.exports = router;
config/auth.js
module.exports = {
ensureAuthenticated: function (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
req.flash('error_msg', 'Please log in to view this resource');
res.redirect('/users/login');
}
};
Step 4. Creating the Frontend
EJS Templates
Create views for registration, login, and the dashboard in the views/ directory.
views/register.ejs
<%- include('layout') %>
<form action="/users/register" method="POST">
<div class="form-group">
<input type="text" name="name" class="form-control" placeholder="Name">
</div>
<div class="form-group">
<input type="email" name="email" class="form-control" placeholder="Email">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="Password">
</div>
<div class="form-group">
<input type="password" name="password2" class="form-control" placeholder="Confirm Password">
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
views/login.ejs
<%- include('layout') %>
<form action="/users/login" method="POST">
<div class="form-group">
<input type="email" name="email" class="form-control" placeholder="Email">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="Password">
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
views/dashboard.ejs
<%- include('layout') %>
<h1>Welcome, <%= user.name %>!</h1>
<a href="/users/logout" class="btn btn-danger">Logout</a>
JavaScript
views/layout.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Node.js App</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/users/login">Login</a>
<a href="/users/register">Register</a>
</nav>
<div class="container">
<%- include('partials/messages') %>
</div>
</body>
</html>
Create the partials Directory and messages.ejs
Inside your views directory, create a new folder called partials.
Inside this partials folder, create a file called messages.ejs.
Here is an example of the content for messages.ejs:
<% if (typeof success_msg != 'undefined') { %>
<div class="alert alert-success">
<%= success_msg %>
</div>
<% } %>
<% if (typeof error_msg != 'undefined') { %>
<div class="alert alert-danger">
<%= error_msg %>
</div>
<% } %>
<% if (typeof errors != 'undefined' && errors.length > 0) { %>
<div class="alert alert-danger">
<ul>
<% errors.forEach(function(error) { %>
<li><%= error.msg %></li>
<% }) %>
</ul>
</div>
<% } %>
Static Files
Style your application using CSS in the public/css/style.css file.
/* Reset some default browser styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
line-height: 1.6;
margin: 0;
padding: 0;
}
/* Container */
.container {
max-width: 1170px;
margin: 0 auto;
padding: 20px;
}
/* Navigation */
nav {
background: #333;
color: #fff;
padding: 15px;
text-align: center;
}
nav a {
color: #fff;
text-decoration: none;
margin: 0 10px;
}
nav a:hover {
text-decoration: underline;
}
/* Form Styling */
form {
background: #fff;
padding: 20px;
margin-top: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 15px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
background: #333;
color: #fff;
border: 0;
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;
}
button:hover {
background: #555;
}
/* Messages */
.alert {
padding: 10px;
background-color: #f4f4f4;
color: #333;
margin-bottom: 20px;
border: 1px solid #ccc;
}
.alert-success {
background-color: #dff0d8;
color: #3c763d;
}
.alert-error {
background-color: #f2dede;
color: #a94442;
}
/* Dashboard */
h1 {
font-size: 24px;
margin-bottom: 20px;
}
.btn-danger {
background-color: #e74c3c;
}
.btn-danger:hover {
background-color: #c0392b;
}
Step 5. Dockerizing the Application
Dockerfile
Create a Dockerfile to specify the build process for your application.
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Docker Compose
Define services, including your Node.js app and MongoDB, in docker-compose.yml.
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
depends_on:
- mongo
mongo:
image: mongo
ports:
- "27017:27017"
Step 6. Running Your Application
Build and Run with Docker Compose
docker-compose up --build
Access the App: Go to http://localhost:3000 in your browser.
![](/image.axd?picture=2025%2f2%2fapp.png)
Access the App: Go to http://localhost:3000 in your browser.
HostForLIFE.eu Node.js Hosting
HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes. We have customers from around the globe, spread across every continent. We serve the hosting needs of the business and professional, government and nonprofit, entertainment and personal use market segments.
![](http://iis75europeanhosting.hostforlife.eu/image.axd?picture=2015%2f1%2frecommended+windows+hosting.png)