You are on page 1of 12

NodeJS + MongoDB - Simple API for Authentication, Registration and User Management

Tutorial built with Node.js and MongoDB

Other versions available:

ASP.NET: ASP.NET Core 3.1, ASP.NET Core 2.2


Node: Node.js + MySQL
In this tutorial we'll go through an example Node.js + MongoDB API that supports
user registration, login with JWT authentication and user management. For an
extended example that includes email verification, role based authorization and
forgot password functionality see Node + Mongo - Boilerplate API with Email Sign
Up, Verification, Authentication & Forgot Password.

After building a few front end tutorials and examples on user authentication with
Angular and React I thought I'd put together a simple custom backend api that can
be easily 'plugged in' to the front ends, or be easily hooked up with your own
custom client application.

The api is written in JavaScript for NodeJS and requires MongoDB to be running.
Mongoose is used to connect to MongoDB, define the database schema and read/write
data. Express is used as the web server.

The project is available on GitHub at https://github.com/cornflourblue/node-mongo-


registration-login-api.

Update History:

02 Jul 2020 - Updated to express-jwt version 6.0.0 to fix security vulnerability


27 Apr 2020 - Updated Mongoose model and added instructions for testing with
Postman and deploying to AWS
14 Jun 2018 - Built with Node.js and MongoDB

Node + Mongo Tutorial Contents


Running the example API locally
Testing the API with Postman
Running with a React app
Deploy to AWS with React
Running with an Angular app
Deploy to AWS with Angular
Running a Vue.js app
Deploy to AWS with Vue
Deploying to Heroku
Node API Project Structure

Running the NodeJS + MongoDB API Locally


Install NodeJS and NPM from https://nodejs.org/en/download/.
Install MongoDB Community Server from https://www.mongodb.com/download-center.
Run MongoDB, instructions are available on the install page for each OS at
https://docs.mongodb.com/manual/administration/install-community/
Download or clone the project source code from
https://github.com/cornflourblue/node-mongo-registration-login-api
Install all required npm packages by running npm install from the command line in
the project root folder (where the package.json is located).
Start the api by running npm start from the command line in the project root
folder, you should see the message Server listening on port 4000. Follow the
instructions below to test with Postman or hook up with one of the example single
page applications available (React, Angular or Vue).
Testing the Node Auth API with Postman
Postman is a great tool for testing APIs, you can download it at
https://www.getpostman.com/.

Below are instructions on how to use Postman to register a new user with the api,
authenticate a user to get a JWT token, and then make an authenticated request with
the JWT token to retrieve a list of users from the api.

How to register a new user with Postman


To register a new user with the api follow these steps:

Open a new request tab by clicking the plus (+) button at the end of the tabs.
Change the http request method to "POST" with the dropdown selector on the left of
the URL input field.
In the URL field enter the address to the register route of your local API -
http://localhost:4000/users/register.
Select the "Body" tab below the URL field, change the body type radio button to
"raw", and change the format dropdown selector to "JSON (application/json)".
Enter a JSON object containing the required user properties in the "Body" textarea,
e.g:
{
"firstName": "Jason",
"lastName": "Watmore",
"username": "jason",
"password": "my-super-secret-password"
}
Click the "Send" button, you should receive a "200 OK" response with an empty JSON
object in the response body.
Here's a screenshot of Postman after the request is sent and the new user has been
registered:

How to authenticate a user with Postman


To authenticate a user with the api and get a JWT token follow these steps:

Open a new request tab by clicking the plus (+) button at the end of the tabs.
Change the http request method to "POST" with the dropdown selector on the left of
the URL input field.
In the URL field enter the address to the authenticate route of your local API -
http://localhost:4000/users/authenticate.
Select the "Body" tab below the URL field, change the body type radio button to
"raw", and change the format dropdown selector to "JSON (application/json)".
Enter a JSON object containing the username and password in the "Body" textarea:
{
"username": "jason",
"password": "my-super-secret-password"
}
Click the "Send" button, you should receive a "200 OK" response with the user
details including a JWT token in the response body, make a copy of the token value
because we'll be using it in the next step to make an authenticated request.
Here's a screenshot of Postman after the request is sent and the user has been
authenticated:

How to make an authenticated request to retrieve all users


To make an authenticated request using the JWT token from the previous step, follow
these steps:
Open a new request tab by clicking the plus (+) button at the end of the tabs.
Change the http request method to "GET" with the dropdown selector on the left of
the URL input field.
In the URL field enter the address to the users route of your local API -
http://localhost:4000/users.
Select the "Authorization" tab below the URL field, change the type to "Bearer
Token" in the type dropdown selector, and paste the JWT token from the previous
authenticate step into the "Token" field.
Click the "Send" button, you should receive a "200 OK" response containing a JSON
array with all the user records in the system.
Here's a screenshot of Postman after making an authenticated request to get all
users:

How to update a user with Postman


To update a user with the api follow these steps:

Open a new request tab by clicking the plus (+) button at the end of the tabs.
Change the http request method to "PUT" with the dropdown selector on the left of
the URL input field.
In the URL field enter the address to the /users/{id} route with the id of the user
you registered above, e.g - http://localhost:4000/users/1.
Select the "Authorization" tab below the URL field, change the type to "Bearer
Token" in the type dropdown selector, and paste the JWT token from the previous
authenticate step into the "Token" field.
Select the "Body" tab below the URL field, change the body type radio button to
"raw", and change the format dropdown selector to "JSON (application/json)".
Enter a JSON object in the "Body" textarea containing the properties you want to
update, for example to update the first and last names:
{
"firstName": "Foo",
"lastName": "Bar"
}
Click the "Send" button, you should receive a "200 OK" response with an empty JSON
object in the response body.
Here's a screenshot of Postman after the request is sent and the user has been
updated:

Running a React client application with the NodeJS API


For full details about the example React application see the post React Hooks +
Redux - User Registration and Login Tutorial & Example. But to get up and running
quickly just follow the below steps.

Download or clone the React tutorial code from


https://github.com/cornflourblue/react-hooks-redux-registration-login-example
Install all required npm packages by running npm install from the command line in
the project root folder (where the package.json is located).
Remove or comment out the 2 lines below the comment // setup fake backend located
in the /src/index.jsx file.
Start the application by running npm start from the command line in the project
root folder, this will launch a browser displaying the application and it should be
hooked up with the NodeJS + MongoDB API that you already have running.

Deploy API to AWS with a React front end


This video shows how to setup a production ready web server from scratch on AWS and
deploy the Node.js + MongoDB API with a React + Redux client app. The tutorial used
in the video is available at React + Node.js on AWS - How to Deploy a MERN Stack
App to Amazon EC2.

Running an Angular 8 client application with the NodeJS API


For full details about the example Angular 8 application see the post Angular 8 -
User Registration and Login Example & Tutorial. But to get up and running quickly
just follow the below steps.

Download or clone the Angular 8 tutorial code from


https://github.com/cornflourblue/angular-8-registration-login-example
Install all required npm packages by running npm install from the command line in
the project root folder (where the package.json is located).
Remove or comment out the line below the comment // provider used to create fake
backend located in the /src/app/app.module.ts file.
Start the application by running npm start from the command line in the project
root folder, this will launch a browser displaying the application and it should be
hooked up with the NodeJS + MongoDB API that you already have running.

Deploy API to AWS with an Angular front end


This video shows how to setup a production ready web server from scratch on AWS and
deploy the Node.js + MongoDB API with an Angular client app. The tutorial used in
the video is available at Angular + Node.js on AWS - How to Deploy a MEAN Stack App
to Amazon EC2.

Running a Vue client application with the NodeJS API


For full details about the example Vue application see the post Vue + Vuex - User
Registration and Login Tutorial & Example. But to get up and running quickly just
follow the below steps.

Download or clone the Vue tutorial code from https://github.com/cornflourblue/vue-


vuex-registration-login-example
Install all required npm packages by running npm install from the command line in
the project root folder (where the package.json is located).
Remove or comment out the 2 lines below the comment // setup fake backend located
in the /src/index.js file.
Start the application by running npm start from the command line in the project
root folder, this will launch a browser displaying the application and it should be
hooked up with the NodeJS + MongoDB API that you already have running.

Deploy API to AWS with a Vue front end


This video shows how to setup a production ready web server from scratch on AWS and
deploy the Node.js + MongoDB API with a Vue client app. The tutorial used in the
video is available at Vue.js + Node.js on AWS - How to Deploy a MEVN Stack App to
Amazon EC2.

Deploying the API to Heroku


For details on how to deploy the api to the Heroku Cloud see the post Deploy to
Heroku - Node + Mongo API for Authentication, Registration and User Management.
It includes detailed instructions for deploying to Heroku with the Heroku CLI and
instructions on testing with Postman.

NodeJS Authentication API Project Structure


The project is structured into "feature folders" (users) "non-feature / shared
component folders" (_helpers). Shared component folders contain code that can be
used by multiple features and other parts of the application, and are prefixed with
an underscore to group them together so it's easier to see what's what at a glance.

The example only contains the single users feature, but this can be easily extended
to handle any other feature by copying the users folder and following the same
pattern.

_helpers
db.js
error-handler.js
jwt.js
users
user.model.js
user.service.js
users.controller.js
config.json
server.js

NodeJS Helpers Folder


Path: /_helpers
The helpers folder contains all the bits and pieces that don't fit into other
folders but don't justify having a folder of their own.

Back to top

NodeJS MongoDB Database Wrapper


Path: /_helpers/db.js
The MongoDB wrapper connects to MongoDB using Mongoose and exports an object
containing all of the database model objects in the application (currently only
User). It provides an easy way to access any part of the database from a single
point.

const config = require('config.json');


const mongoose = require('mongoose');
const connectionOptions = { useCreateIndex: true, useNewUrlParser: true,
useUnifiedTopology: true, useFindAndModify: false };
mongoose.connect(process.env.MONGODB_URI || config.connectionString,
connectionOptions);
mongoose.Promise = global.Promise;

module.exports = {
User: require('../users/user.model')
};
Back to top

NodeJS Global Error Handler Middleware


Path: /_helpers/error-handler.js
The global error handler is used catch all errors and remove the need for redundant
error handler code throughout the application. It's configured as middleware in the
main server.js file.

module.exports = errorHandler;
function errorHandler(err, req, res, next) {
if (typeof (err) === 'string') {
// custom application error
return res.status(400).json({ message: err });
}

if (err.name === 'ValidationError') {


// mongoose validation error
return res.status(400).json({ message: err.message });
}

if (err.name === 'UnauthorizedError') {


// jwt authentication error
return res.status(401).json({ message: 'Invalid Token' });
}

// default to 500 server error


return res.status(500).json({ message: err.message });
}
Back to top

NodeJS JWT Middleware


Path: /_helpers/jwt.js
The node JWT middleware checks that the JWT token received in the http request from
the client is valid before allowing access to the API, if the token is invalid a
"401 Unauthorized" response is sent to the client.

JWT authentication is used on all routes except for the authenticate and register
routes which are public.

const expressJwt = require('express-jwt');


const config = require('config.json');
const userService = require('../users/user.service');

module.exports = jwt;

function jwt() {
const secret = config.secret;
return expressJwt({ secret, algorithms: ['HS256'], isRevoked }).unless({
path: [
// public routes that don't require authentication
'/users/authenticate',
'/users/register'
]
});
}

async function isRevoked(req, payload, done) {


const user = await userService.getById(payload.sub);

// revoke token if user no longer exists


if (!user) {
return done(null, true);
}

done();
};
Back to top
NodeJS Users Folder
Path: /users
The users folder contains all code that is specific to the users feature of the
api.

Back to top

NodeJS Mongoose User Model


Path: /users/user.model.js
The user model uses Mongoose to define the schema for the users collection saved in
MongoDB. The exported Mongoose model object gives full access to perform CRUD
(create, read, update, delete) operations on users in MongoDB, see the user service
below for examples.

schema.set('toJSON', { ... }); configures which user properties are included when
converting MongoDB records to JSON objects which are returned in API responses.

virtuals: true includes the Mongoose virtual id property which is a copy of the
MongoDB _id property.
versionKey: false excludes the Mongoose version key (__v).
transform: function (doc, ret) { ... } removes the MongoDB _id property and
password hash so they are not included in API responses.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const schema = new Schema({


username: { type: String, unique: true, required: true },
hash: { type: String, required: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
createdDate: { type: Date, default: Date.now }
});

schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform: function (doc, ret) {
delete ret._id;
delete ret.hash;
}
});

module.exports = mongoose.model('User', schema);


Back to top

NodeJS User Service


Path: /users/user.service.js
The user service contains the core business logic for user authentication and
management in the node api, it encapsulates all interaction with the mongoose user
model and exposes a simple set of methods which are used by the users controller
below.

The top of the file contains the service method definitions so it's easy to see all
methods at a glance, the rest of the file contains the method implementations.

const config = require('config.json');


const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const db = require('_helpers/db');
const User = db.User;

module.exports = {
authenticate,
getAll,
getById,
create,
update,
delete: _delete
};

async function authenticate({ username, password }) {


const user = await User.findOne({ username });
if (user && bcrypt.compareSync(password, user.hash)) {
const token = jwt.sign({ sub: user.id }, config.secret, { expiresIn:
'7d' });
return {
...user.toJSON(),
token
};
}
}

async function getAll() {


return await User.find();
}

async function getById(id) {


return await User.findById(id);
}

async function create(userParam) {


// validate
if (await User.findOne({ username: userParam.username })) {
throw 'Username "' + userParam.username + '" is already taken';
}

const user = new User(userParam);

// hash password
if (userParam.password) {
user.hash = bcrypt.hashSync(userParam.password, 10);
}

// save user
await user.save();
}

async function update(id, userParam) {


const user = await User.findById(id);

// validate
if (!user) throw 'User not found';
if (user.username !== userParam.username && await User.findOne({ username:
userParam.username })) {
throw 'Username "' + userParam.username + '" is already taken';
}
// hash password if it was entered
if (userParam.password) {
userParam.hash = bcrypt.hashSync(userParam.password, 10);
}

// copy userParam properties to user


Object.assign(user, userParam);

await user.save();
}

async function _delete(id) {


await User.findByIdAndRemove(id);
}
Back to top

NodeJS Express Users Controller


Path: /users/users.controller.js
The users controller defines all user routes for the api, the route definitions are
grouped together at the top of the file and the implementations are below.

Express is the web server used by the api, it's one of the most popular web
application frameworks for NodeJS.

const express = require('express');


const router = express.Router();
const userService = require('./user.service');

// routes
router.post('/authenticate', authenticate);
router.post('/register', register);
router.get('/', getAll);
router.get('/current', getCurrent);
router.get('/:id', getById);
router.put('/:id', update);
router.delete('/:id', _delete);

module.exports = router;

function authenticate(req, res, next) {


userService.authenticate(req.body)
.then(user => user ? res.json(user) : res.status(400).json({ message:
'Username or password is incorrect' }))
.catch(err => next(err));
}

function register(req, res, next) {


userService.create(req.body)
.then(() => res.json({}))
.catch(err => next(err));
}

function getAll(req, res, next) {


userService.getAll()
.then(users => res.json(users))
.catch(err => next(err));
}

function getCurrent(req, res, next) {


userService.getById(req.user.sub)
.then(user => user ? res.json(user) : res.sendStatus(404))
.catch(err => next(err));
}

function getById(req, res, next) {


userService.getById(req.params.id)
.then(user => user ? res.json(user) : res.sendStatus(404))
.catch(err => next(err));
}

function update(req, res, next) {


userService.update(req.params.id, req.body)
.then(() => res.json({}))
.catch(err => next(err));
}

function _delete(req, res, next) {


userService.delete(req.params.id)
.then(() => res.json({}))
.catch(err => next(err));
}
Back to top

NodeJS App Config


Path: /config.json
The app config file contains configuration data for the api.

IMPORTANT: The "secret" property is used by the api to sign and verify JWT tokens
for authentication, update it with your own random string to ensure nobody else can
generate a JWT to gain unauthorised access to your application.

{
"connectionString": "mongodb://localhost/node-mongo-registration-login-api",
"secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN
SECRET, IT CAN BE ANY STRING"
}
Back to top

NodeJS Main Server Entrypoint


Path: /server.js
The server.js file is the entry point into the api, it configures application
middleware, binds controllers to routes and starts the Express web server for the
api.

require('rootpath')();
const express = require('express');
const app = express();
const cors = require('cors');
const bodyParser = require('body-parser');
const jwt = require('_helpers/jwt');
const errorHandler = require('_helpers/error-handler');

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


app.use(bodyParser.json());
app.use(cors());

// use JWT auth to secure the api


app.use(jwt());
// api routes
app.use('/users', require('./users/users.controller'));

// global error handler


app.use(errorHandler);

// start server
const port = process.env.NODE_ENV === 'production' ? (process.env.PORT || 80) :
4000;
const server = app.listen(port, function () {
console.log('Server listening on port ' + port);
});
Back to top

Subscribe or Follow Me For Updates


Subscribe to my YouTube channel or follow me on Twitter or GitHub to be notified
when I post new content.

Subscribe on YouTube at https://www.youtube.com/c/JasonWatmore


Follow me on Twitter at https://twitter.com/jason_watmore
Follow me on GitHub at https://github.com/cornflourblue
Feeds formats available: RSS, Atom, JSON

Tags: NodeJS, MongoDB, AWS, Heroku, Authentication and Authorization, JavaScript

Share: Facebook Twitter

More NodeJS Posts


Sequelize + MySQL - Create database if it doesn't exist
Node.js + MySQL - Boilerplate API with Email Sign Up, Verification, Authentication
& Forgot Password
Node.js + MySQL - Simple API for Authentication, Registration and User Management
Node.js + Express API - Request Schema Validation with Joi
Node.js - Send Emails via SMTP with Nodemailer
Node.js - Hash and Verify Passwords with Bcrypt
Node.js + MongoDB API - JWT Authentication with Refresh Tokens
Node + Mongo - Boilerplate API with Email Sign Up, Verification, Authentication &
Forgot Password
Vue.js + Node.js on AWS - How to Deploy a MEVN Stack App to Amazon EC2
Angular + Node.js on AWS - How to Deploy a MEAN Stack App to Amazon EC2
React + Node.js on AWS - How to Deploy a MERN Stack App to Amazon EC2
Vue.js + Node - Server Side Pagination Tutorial & Example
React + Node - Server Side Pagination Tutorial & Example
Angular 8 + Node - Server Side Pagination Tutorial & Example
Deploy to Heroku - Node + Mongo API for Authentication, Registration and User
Management
Node.js - Role Based Authorization Tutorial with Example API
Setup Node.js + MongoDB Production Server on Ubuntu 18.04 - Ubuntu 19.04
NodeJS - Basic Authentication Tutorial with Example API
NodeJS - JWT Authentication Tutorial with Example API
Node - Get Public Key From Private Key with JavaScript
Angular 2 - Refresh Without 404 in Node & IIS
MEAN with Angular 2/5 - User Registration and Login Example & Tutorial
MEANie - Lightweight MEAN Stack CMS & Blogging Platform
AngularJS - Enable HTML5 Mode Page Refresh Without 404 Errors in NodeJS and IIS
NodeJS - Setup a Simple HTTP Server / Local Web Server
Getting Foreman working on Windows 7 after installing the Heroku Toolbelt

ABOUT
I'm a web developer in Sydney Australia and the technical lead at Point Blank
Development, I've been building websites and web applications in Sydney since 1998.

Find me on:

Subscribe to Feed: RSS, Atom, JSON

MONTHS
2020
November (2)
October (16)
September (26)
August (5)
July (22)
June (5)
May (7)
April (17)
March (5)
February (6)
January (5)
2019
2018
2017
2016
2015
2014
2013
2012
2011
TAGS
Alerts Angular Angular 10 Angular 2 Angular 4 Angular 5 Angular 6 Angular 7 Angular
8 Angular 9 Angular Directive Angular UI Router AngularJS Animation ASP.NET ASP.NET
Core ASP.NET Web API Authentication and Authorization AWS Axios Azure Basic
Authentication Blazor Bootstrap C# Chai CKEditor CSS3 DDD Deployment Design
Patterns Dynamic LINQ EF Core ELMAH ES6 Exchange Facebook Fetch Fluent NHibernate
Formik Google Analytics Google API Google Maps API Google Plus Heroku HTML5 HTTP
IIS Insecure Content Instagram API Ionic Framework iOS iPhone JavaScript jQuery JWT
LinkedIn LINQ Login MEAN Stack MERN Stack MEVN Stack Mocha Modal MongoDB Moq MVC
MVC5 MySQL NGINX ngMock NHibernate Ninject NodeJS npm Pagination Pinterest Razor
Pages React React Hooks Redmine Redux Registration Repository RxJS Security
Sequelize Shell Scripting Sinon SinonJS SSH TDD Terraform Twitter TypeScript Ubuntu
Umbraco Unit of Work Unit Testing URL Rewrite Validation Vue Vue 3 Vuex Webpack
Windows Server 2008
Powered by MEANie
© 2020 JasonWatmore.com
SPONSOR
Authentic Jobs — Your new development career awaits. Check out the latest listings.

You might also like