Hello and welcome to this article where we will learn how to create a full-stack web application using NodeJS and also deploy it on Heroku. Let us get started.

So before we start coding, let us define what our application will do

We will create an application (QuickLinks) that will let us save our favorite websites and launch them from a single place. The website will be guarded with basic authentication and support — Login and Sign Up process.

Once we are done, the website will look as follows -

QuickLinks

The code can be found here -

Setting up the project

You can either clone the GitHub repository linked above or you can follow along.

Let us set up the application -

npm init -y

Let us add all the dependencies -

npm i bcrypt-nodejs express mongoose connect-mongo dotenv express-session hbs passport passport-local valid-url npm i nodemon --save-dev

The above dependencies are as follows -

express = Express server

Express server bcrypt-nodejs = It is used to hash the plain-text password.

It is used to hash the plain-text password. mongoose = Mongoose package that makes it easy to deal with mongoDB

Mongoose package that makes it easy to deal with mongoDB connect-mongo = Used to store the session related information in mongoDB

Used to store the session related information in mongoDB dotenv = Parse environment variables.

Parse environment variables. express-session = Session handling in express.

Session handling in express. hbs = Handlebars library for server side templating.

Handlebars library for server side templating. passport = Library to handle authentication.

Library to handle authentication. passport-local = Provides basic authentication strategy.

Provides basic authentication strategy. valid-url = Checks if the input is of URI type.

Templates

Let us design the templates now. We will make use of handlebars templating for designing the web pages. Under the parent directory, create a folder called templates and inside that create 2 sub folders called — partials and views. Partials contains the re-usable components that can be used in different web pages, Views contains all the web pages corresponding to our 3 views — Login page, Sign Up page and dashboard.

The partials are as follows -

header.hbs

footer.hbs

The templates are as follows -

login.hbs

signup.hbs

dashboard.hbs

Few observation on the templates -

We make use of bootstrap to render the UI.

For the links, we are making use of bootstrap’s card layout. In the card, there are fields for — link, name, description and an optional image field. Additionally, we also provide 2 buttons inside each card — delete link and open link.

Besides the card, we also provide 3 buttons on the profile — Add link, Logout and Delete profile link.

Static assets

Let us create the css and javascript files now. The static assets are inside the public directory (check GitHub repo link).

styles.css

And now, let us create the javascript file that will handle all the ajax requests and modal handling. Login and SignUp pages are basically modals. So to display them, we need to open the modals when the page is loaded.

login.js

signup.js

dashboard.js

Explanation-

signup.js and login.js contains logic to open the modal on page load.

dashboard.js contains code for — sending AJAX request to backend when link is deleted ( deleteLink )and a new link is to be added (submitForm). We have not defined the routes yet. Along with the AJAX calls, it also contains a function to convert the image into a Base64 string.

Backend

Let us start with defining the User model and the Links model. Models are basically representation of documents that you want to persist in MongoDB. We define all the fields and types of attributes in models. User model will be used to store user related information and the links model will store all links related info.

User.js

Links.js

Links documents will be embedded in the user documents as sub-documents/array of docs. This is done through the use of a virtual field. We also define 2 methods — generateHash and validPassword which are used during the signup and login process. We will see later how passportjs uses these 2 methods to hash the user provided password during sign up process and then validate the password during login process. We make use of bcrypt library here to do the hashing and validation.

Now, let us define the passport.js configuration for sign up and log in. Check the GitHub link for the location of the file relative to the project directory.

passport.js

There are few things going on here -

serializeUser = This method is used by passport.JS to store the client identifier (in this case -> email) in the session table.

= This method is used by passport.JS to store the client identifier (in this case -> email) in the session table. deserializeUser = It is used by passport to load the actual user based on the serialized content from the database. The deserialized user information is bound to the request object. A new parameter called user is attached to it which contains the user info. If the session is not active, then the user field is empty/null.

= It is used by passport to load the actual user based on the serialized content from the database. The deserialized user information is bound to the request object. A new parameter called is attached to it which contains the user info. If the session is not active, then the user field is empty/null. local-signup = This is the sign up strategy. We will see how it is invoked later on. In this strategy, we basically map the incoming payload to username and password field, and then using these 2 fields we query our database to check if an user already exists. If they do, we raise an error — User already exists, otherwise we provision a new user and pass that on to the request.

= This is the sign up strategy. We will see how it is invoked later on. In this strategy, we basically map the incoming payload to username and password field, and then using these 2 fields we query our database to check if an user already exists. If they do, we raise an error — User already exists, otherwise we provision a new user and pass that on to the request. local-login = This is the login strategy. This works similar to the signup strategy. Here as well, we look for the user in the database. If not present, we raise an error — No user exists. If the user exists and password doesn’t match — we raise another error. The password check is done using the validPassword method defined earlier. If user exists and password is valid, we pass on the user object to the request.

Now, with the passport middle ware in place, let us define the application routes.

router.js

isLoggedIn = This method is used to check if the current request is authenticated. It is used as a middleware.

= This method is used to check if the current request is authenticated. It is used as a middleware. /dashboard = This endpoint is used to load the user information + links information to be displayed in the dashboard.

= This endpoint is used to load the user information + links information to be displayed in the dashboard. /signup = It has 2 types of routes. If the request is of type GET, then the signup form is presented. If the request is of type POST, then the local-signup strategy is invoked and the user is logged in after successful sign up.

= It has 2 types of routes. If the request is of type GET, then the signup form is presented. If the request is of type POST, then the local-signup strategy is invoked and the user is logged in after successful sign up. /login = Similar to the above route, this also has a GET and POST endpoint. If the request is of type POST, then the user is authenticated using the local-login strategy. If the login process fails, then the login page is reloaded with the error details.

= Similar to the above route, this also has a GET and POST endpoint. If the request is of type POST, then the user is authenticated using the local-login strategy. If the login process fails, then the login page is reloaded with the error details. /logout = As the name suggests, this endpoint is used to log out. The user session information is deleted from the database and user is logged out.

= As the name suggests, this endpoint is used to log out. The user session information is deleted from the database and user is logged out. /add = This endpoint is used to accept link payload from the UI through AJAX request. It validates the link information and if correct,writes into DB. Since, it is a AJAX request, the response type is JSON and not a page render/redirect.

= This endpoint is used to accept link payload from the UI through AJAX request. It validates the link information and if correct,writes into DB. Since, it is a AJAX request, the response type is JSON and not a page render/redirect. /deleteLink = This is used to delete a given link in UI.

= This is used to delete a given link in UI. /deleteUserProfile = This endpoint deletes all the user related information and redirects to the sign up page.

= This endpoint deletes all the user related information and redirects to the sign up page. * = For every other request, the user is redirected to the dashboard page, if they are logged in. Otherwise, the sign up page, if the user is not logged in.

Now, we have defined the routes and authentication configuration. What is left now, is the server config. Let us do that.

app.js

The app.js file contains all the config needed to start up the server.

staticPath = Defines the relative path of static folder (css, js).

= Defines the relative path of static folder (css, js). viewsPath and partialsPath defines the path of the views and partials templates.

and defines the path of the views and partials templates. We set up the mongoose connection using the mongoose.connect method.

method. We define the session config using express-session . We also define the storage type with that of mongoDB . The session is persisted for 15 mins.

. We also define the storage type with that of . The session is persisted for 15 mins. And finally we start the application at the port available.

Before we start our application, we need to define a .env file which contains the 2 fields -

DBURL=mondoDB_Connection_URL

SECRET=appSecret_for_passport.js_Config

If you want to run the app locally, you need to keep a mongodb local instance running. If you don’t have a local mongo installation, you can make use of Mongo Atlas to create a free cluster. Replace the DBURL value with the connection string.

Now with that you can run the application by going to the root of the app -

node src/app.js

This will start the app at localhost:3000. Visit the URL and follow through the sign up process, login and dashboard.

Deployment

Now, that we have a perfectly functioning local web application. Let us, deploy the app. We will use Heroku for this

Create an account if you don’t have, at Heroku. They have a free tier.

Now install the Heroku CLI — https://devcenter.heroku.com/articles/heroku-cli

Navigate to the project directory and apply the following commands.

Login to Heroku -

heroku login

Create a new web app in Heroku with name quickLinks

heroku create quickLinks

Set the config values

heroku config:set DBURL=mongodb+srv:....

heroku config:set SECRET=appsecret...

Push the app to heroku. Heroku uses Git to deploy.

git push heroku master

With that, you will notice that the app is deployed and you will be provided with the app link.

I have deployed my copy which can be accessed here —

https://quick-links-app.herokuapp.com

You can access the above link and try out the application.

With that we are at the end of our article. We have seen how easy it is to perform server side rendering, authentication, routing and using database with NodeJs. Not to forget, deploying an actual app on Heroku. I hope this article will be useful for you.

Regards,

Boudhayan Dev