Building a Secure Authentication API with Node.js, Express, Sequelize, and MySQL

In today's digital landscape, secure authentication mechanisms are critical to protect user data and ensure privacy. Building a robust authentication API using Node.js, Express, Sequelize, and MySQL offers a scalable and efficient solution. Node.js provides a powerful runtime environment, while Express simplifies server setup and routing. Sequelize, an ORM for Node.js, streamlines database interactions with MySQL, facilitating data management. This guide will walk you through creating a secure authentication API, covering user registration, login, and token-based authentication using JSON Web Tokens (JWT).

Project Structure

auth-api/
├── config/
│   └── db.config.js
├── controllers/
│   └── auth.controller.js
├── models/
│   ├── index.js
│   └── user.model.js
├── routes/
│   └── auth.routes.js
├── tests/
│   └── auth.tests.js
├── .env
├── .gitignore
├── index.js
├── package.json
└── README.md

Step-by-Step Guide

Step 1: Initialize the Project

  1. Create the Project Directory:

     mkdir auth-api
     cd auth-api
    
  2. Initialize a Node.js Project:

     npm init -y
    
  3. Install Dependencies:

     npm install express sequelize mysql2 bcryptjs jsonwebtoken dotenv
    

Step 2: Set Up Configuration

  1. Create theconfigDirectory:

     mkdir config
    
  2. Createdb.config.jsin theconfig Directory:

     //code// config/db.config.js
     module.exports = {
       HOST: process.env.DB_HOST,
       USER: process.env.DB_USER,
       PASSWORD: process.env.DB_PASSWORD,
       DB: process.env.DB_NAME,
       dialect: "mysql",
       pool: {
         max: 5,
         min: 0,
         acquire: 30000,
         idle: 10000,
       },
     };
    

Step 3: Set Up Models

  1. Create themodelsDirectory:

     mkdir models
    
  2. Createindex.jsin themodels Directory:

     const { Sequelize, DataTypes } = require('sequelize');
     require('dotenv').config();
    
     const dbConfig = {
       HOST: process.env.DB_HOST,
       USER: process.env.DB_USER,
       PASSWORD: process.env.DB_PASSWORD,
       DB: process.env.DB_NAME,
       dialect: "mysql",
       pool: {
         max: 5,
         min: 0,
         acquire: 30000,
         idle: 10000
       }
     };
    
     const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
       host: dbConfig.HOST,
       dialect: dbConfig.dialect,
       pool: dbConfig.pool,
     });
    
     const db = {};
     db.Sequelize = Sequelize;
     db.sequelize = sequelize;
    
     db.user = require('./user.model.js')(sequelize, DataTypes);
    
     module.exports = db;
    
  3. Createuser.model.jsin themodels Directory:

     module.exports = (sequelize, DataTypes) => {
       const User = sequelize.define('user', {
         username: {
           type: DataTypes.STRING,
           allowNull: false,
           unique: true
         },
         email: {
           type: DataTypes.STRING,
           allowNull: false,
           unique: true
         },
         password: {
           type: DataTypes.STRING,
           allowNull: false
         }
       });
       return User;
     };
    

Step 4: Set Up Controllers

  1. Create thecontrollersDirectory:

     mkdir controllers
    
  2. Createauth.controller.jsin thecontrollers Directory:

     //code// controllers/auth.controller.js
     const db = require('../models');
     const User = db.user;
     const jwt = require('jsonwebtoken');
     const bcrypt = require('bcryptjs');
    
     // Signup function
     exports.signup = async (req, res) => {
       try {
         const { username, email, password } = req.body;
    
         // Check if the username or email already exists
         const existingUser = await User.findOne({ where: { email } });
         if (existingUser) {
           return res.status(400).send({ message: "Email is already registered." });
         }
    
         // Hash password
         const hashedPassword = bcrypt.hashSync(password, 8);
    
         // Save user to database
         const user = await User.create({ username, email, password: hashedPassword });
    
         res.status(201).send({ message: "User registered successfully!" });
       } catch (err) {
         res.status(500).send({ message: err.message });
       }
     };
    
     // Login function
     exports.login = async (req, res) => {
       try {
         const { email, password } = req.body;
    
         // Find user by email
         const user = await User.findOne({ where: { email } });
         if (!user) {
           return res.status(404).send({ message: "User not found." });
         }
    
         // Check password
         const passwordIsValid = bcrypt.compareSync(password, user.password);
         if (!passwordIsValid) {
           return res.status(401).send({ accessToken: null, message: "Invalid Password!" });
         }
    
         // Sign JWT token
         const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
           expiresIn: 86400 // 24 hours
         });
    
         res.status(200).send({
           id: user.id,
           username: user.username,
           email: user.email,
           accessToken: token
         });
       } catch (err) {
         res.status(500).send({ message: err.message });
       }
     };
    
     exports.AllSignUpUser= async(req,res)=>{
    
       try {
    
         const users = await User.findAll();
    
         res.status(200).send({ users });
    
       } catch (err) {
    
         res.status(500).send({ message: err.message });
    
       }
     }
    

Step 5: Set Up Routes

  1. Create theroutesDirectory:

     mkdir routes
    
  2. Createauth.routes.jsin theroutes Directory:

     const express = require('express');
     const router = express.Router();
     const authController = require('../controller/auth.controller');
    
     router.get('/', function (req, res) {
         res.send("you are enter at auth.routes.js ")
     });
    
     router.post('/signup', authController.signup);
     router.post('/login', authController.login);
     router.get('/allsignupusers',authController.AllSignUpUser)
    
     module.exports = router;
     // /api/auth/signup
    

Step 6: Set Up the Main Application

  1. Createindex.js in the Root Directory:

     //code// index.js
     const express = require("express");
     const bodyParser = require("body-parser");
     const cors = require("cors");
     const db = require("./models");
     const authRoutes = require("./routes/auth.routes");
    
     const app = express();
    
     app.use(cors());
     app.use(bodyParser.json());
     app.use(bodyParser.urlencoded({ extended: true }));
    
     app.use("/api/auth", authRoutes);
    
     db.sequelize.sync()
       .then(() => {
         console.log("Database synced successfully.");
       })
       .catch((err) => {
         console.log("Failed to sync database: " + err.message);
       });
    
     const PORT = process.env.PORT || 3000;
     app.listen(PORT, () => {
       console.log(`Server is running on port ${PORT}.`);
     });
    

Step 7: Set Up Environment Variables

  1. Create.env in the Root Directory:

     DB_HOST=your_database_host
     DB_USER=your_database_username
     DB_PASSWORD=your_database_password
     DB_NAME=your_database_name
     JWT_SECRET=your_secret_key
    

Step 8: Set Up Git Ignore

  1. Create.gitignore in the Root Directory:

     node_modules/
     .env
    

Step 9: Run the Application

  1. Start the Server:

     node index.js
    

    Your authentication API should now be running at http://localhost:3000.

How to Check API Functionality Using Postman

Once You have your authentication API up and running, you can use Postman to test its endpoints and ensure everything works correctly. Follow these steps to check the API functionality using Postman.

Prerequisites

  • Ensure your server is running by executing node index.js in your project directory.

  • Download and install Postman if you haven't already.

Testing the API Endpoints

1. Test the Sign Up Endpoint

Endpoint:POST /api/auth/signup

  1. Open Postman and create a new request.

  2. Set the request type toPOST.

  3. Enter the UR****L:http://localhost:3000/api/auth/signup

  4. Go to theBody tab and select raw.

  5. Set the format toJSON.

  6. Enter the following JSON data:

     {
       "username": "testuser",
       "email": "testuser@example.com",
       "password": "password123"
     }
    
  7. ClickSend.

Expected Response:

  • Status: 201 Created

  • Body:

      {
        "message": "User created successfully!",
        "user": {
          "id": 1,
          "username": "testuser",
          "email": "testuser@example.com",
          "password": "$2a$10$..."
        }
      }
    

2. Test the Login Endpoint

Endpoint:POST /api/auth/login

  1. Open Postman and create a new request.

  2. Set the request type toPOST.

  3. Enter the URL:http://localhost:3000/api/auth/login

  4. Go to theBody tab and select raw.

  5. Set the format toJSON.

  6. Enter the following JSON data:

     {
       "email": "testuser@example.com",
       "password": "password123"
     }
    
  7. ClickSend.

Expected Response:

  • Status: 200 OK

  • Body:

      {
        "message": "Login successful",
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      }
    

3. Test an Admin Functionality (Optional)

For admin functionalities, you need to implement specific routes and permissions. Below is an example of how to test fetching all users (assuming you have an admin route set up).

Endpoint:GET /api/auth/users (Assuming this endpoint lists all users and is protected)

  1. Open Postman and create a new request.

  2. Set the request type toGET.

  3. Enter the URL:http://localhost:3000/api/auth/users

  4. Go to theHeaders tab.

  5. Add a new header:

    • Key: Authorization

    • Value: Bearer YOUR_JWT_TOKEN (Replace YOUR_JWT_TOKEN with the token received from the login endpoint)

  6. ClickSend.

Expected Response:

  • Status: 200 OK

  • Body:

      [
        {
          "id": 1,
          "username": "testuser",
          "email": "testuser@example.com"
        },
        // other users...
      ]
    

Summary of Postman Testing

  1. Sign Up a New User:

    • Use the POST /api/auth/signup endpoint.

    • Verify the response indicates a successful user creation.

  2. Log In with the New User:

    • Use the POST /api/auth/login endpoint.

    • Verify the response returns a JWT token.

  3. Access Protected Routes:

    • Use the GET /api/auth/users endpoint (or any other protected route).

    • Include the JWT token in the Authorization header.

    • Verify you receive the expected data for authenticated requests.

By following these steps, you can thoroughly test your authentication API using Postman, ensuring all endpoints function as expected and your API provides the intended security and usability.

To dive into advanced unit testing techniques, navigate to the next article by click here.

Conclusion

This step-by-step guide has walked you through creating a secure authentication API with Node.js, Express, Sequelize, and MySQL. Each section covers the creation and setup of necessary folders and files, ensuring a clear understanding of the project's structure and workflow. This setup will allow you to build, run, and extend the authentication functionality as needed.

You can check out the full project on GitHub: Github Account :- Dhruv9099

Feel free to leave feedback or ask questions in the comments. Your input is valuable and helps improve future projects!

0
Subscribe to my newsletter

Read articles from Dhruvkumar Maisuria directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dhruvkumar Maisuria
Dhruvkumar Maisuria

About Me As a dedicated tech enthusiast with a passion for continuous learning, I possess a robust background in software development and network engineering. My journey in the tech world is marked by an insatiable curiosity and a drive to solve complex problems through innovative solutions. I have honed my skills across a diverse array of technologies, including Python, AWS, SQL, and Golang, and have a strong foundation in web development, API testing, and cloud computing. My hands-on experience ranges from creating sophisticated applications to optimizing network performance, underscored by a commitment to excellence and a meticulous attention to detail. In addition to my technical prowess, I am an avid advocate for knowledge sharing, regularly contributing to the tech community through blogs and open-source projects. My proactive approach to professional development is demonstrated by my ongoing exploration of advanced concepts in programming and networking, ensuring that I stay at the forefront of industry trends. My professional journey is a testament to my adaptability and eagerness to embrace new challenges, making me a valuable asset in any dynamic, forward-thinking team. Whether working on a collaborative project or independently, I bring a blend of analytical thinking, creative problem-solving, and a deep-seated passion for technology to every endeavor.