Subscription Management System with NodeJS and ExpressJS

In this article, we’ll walk through the step-by-step process of creating a Subscription Management System with NodeJS and ExpressJS. This application will provide users with the ability to subscribe to various plans, manage their subscriptions, and include features like user authentication and authorization using JWT (JSON Web Tokens). We’ll use MongoDB as our database to store user information and subscription plans.

Prerequisites:

Approach for Creating Subscription Management System with NodeJS and ExpressJS

  • Identify key features like user User authentication and authorization, Subscription plan management (CRUD operations), User subscription management, and Secure APIs using JWT (JSON Web Tokens).
  • Install Node.js, npm, ExpressJS, and other project dependencies.
  • Create a new project directory and initialize it.
  • Ensure MongoDB is installed and running locally or use a cloud-based MongoDB instance. Create a database for your Subscription Management System.
  • Create Mongoose models to represent User and Subscription Plan entities.
  • Implement Authentication, Authorization Middleware, Subscription Plan Management.

Steps to Create the NodeJS App and Installing Module

Step 1: Create a NodeJS project using the following command.

npm init -y

Step 2: Install Express.js and other necessary dependencies.

npm install express mongoose jsonwebtoken bcryptjs dotenv 

Step 3: Create folders for different parts of the application such as models, routes, and middleware. Inside each folder, create corresponding files for different components of the application.

Step 4: Set up a MongoDB database either locally or using a cloud-based service like MongoDB Atlas. Define Mongoose models for the data entities such as User, SubscriptionPlan.

Project Structure:

Project Folder Structure

The updated dependencies in package.json file will look like:

"dependencies": {
"bcryptjs": "^2.4.3",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0"
}

Example: Below is an example of Travel Planning App with NodeJS and ExpressJS.

JavaScript
// authenticate.js

const jwt = require('jsonwebtoken');
const User = require('../models/User');

module.exports = async (req, res, next) => {
    try {
        const token = req.headers.authorization?.split(' ')[1];

        if (!token) {
            return res.status(401).json({
                message: 'Authentication failed: Token not provided'
            });
        }

        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        const user = await User.findById(decoded.userId);

        if (!user) {
            return res.status(401).json({
                message: 'Authentication failed: User not found'
            });
        }

        req.user = user;
        next();
    } catch (error) {
        console.error('Authentication error:', error);
        return res.status(401).json({
            message: 'Authentication failed: Invalid token'
        });
    }
};
JavaScript
// SubscriptionPlan.js

const mongoose = require('mongoose');

const subscriptionPlanSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true
    },
    duration: {
        type: String,
        required: true
    },
    features: {
        type: [String],
        required: true
    },
});

module.exports = mongoose.model('SubscriptionPlan', subscriptionPlanSchema);
JavaScript
// User.js

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
    email: {
        type: String,
        unique: true,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    role: {
        type: String,
        enum: ['user', 'admin'],
        default: 'user'
    },
});

userSchema.pre('save', async function (next) {
    if (!this.isModified('password')) return next();
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
});

userSchema.methods.comparePassword = async function (password) {
    return bcrypt.compare(password, this.password);
};

module.exports = mongoose.model('User', userSchema);
JavaScript
// routes/auth.js
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const User = require('../models/User.js');
const bcrypt = require('bcryptjs');

router.post('/signup', async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = new User({ email, password });
        await user.save();
        res.status(201).json({ message: 'User registered successfully' });
    } catch (error) {
        res.status(500).json({ message: 'Failed to register user' });
    }
});

router.post('/login', async (req, res) => {
    const { email, password } = req.body;

    try {
        // Check if the user exists in the database
        const user = await User.findOne({ email });
        if (!user) {
            return res.status(401).json({
                message: "Login failed: User not found"
            });
        }

        // Verify the password
        const isMatch = await bcrypt.compare(password, user.password);
        if (!isMatch) {
            return res.status(401).json({
                message: "Login failed: Invalid password"
            });
        }

        // Generate a JWT token
        const token = jwt.sign({ userId: user._id, email: user.email },
            "your_screte_key", { expiresIn: '1h' });

        // Return the token as a response
        res.status(200).json({ token });
    } catch (error) {
        console.error('Login error:', error);
        res.status(500).json({ message: "Login failed" });
    }
});

module.exports = router;
JavaScript
// subscriptionPlans.js

// routes/subscriptionPlans.js
const express = require('express');
const router = express.Router();
const SubscriptionPlan = require('../models/SubscriptionPlan.js');

// Create a new subscription plan
router.post('/', async (req, res) => {
    try {
        const plan = new SubscriptionPlan(req.body);
        await plan.save();
        res.status(201).json(plan);
    } catch (error) {
        res.status(500).json({
            message: 'Failed to create subscription plan'
        });
    }
});

// Get all subscription plans
router.get('/', async (req, res) => {
    try {
        const plans = await SubscriptionPlan.find();
        res.json(plans);
    } catch (error) {
        res.status(500).json({
            message: 'Failed to fetch subscription plans'
        });
    }
});

// Update a subscription plan
router.put('/:id', async (req, res) => {
    try {
        const { id } = req.params;
        const updatedPlan = await SubscriptionPlan.findByIdAndUpdate(id, req.body, { new: true });
        res.json(updatedPlan);
    } catch (error) {
        res.status(500).json({
            message: 'Failed to update subscription plan'
        });
    }
});

// Delete a subscription plan
router.delete('/:id', async (req, res) => {
    try {
        const { id } = req.params;
        await SubscriptionPlan.findByIdAndDelete(id);
        res.json({
            message: 'Subscription plan deleted successfully'
        });
    } catch (error) {
        res.status(500).json({
            message: 'Failed to delete subscription plan'
        });
    }
});

module.exports = router;
JavaScript
// app.js

const express = require('express');
const dotenv = require('dotenv');
const db = require('./db');
const authRoutes = require('./routes/auth');
const subscriptionPlanRoutes = require('./routes/subscriptionPlans');

dotenv.config();
const app = express();

app.use(express.json());

app.use('/auth', authRoutes);
app.use('/subscription-plans', subscriptionPlanRoutes);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
JavaScript
// db.js
const mongoose = require('mongoose');

mongoose.connect('mongodb+srv://admin:<password>@cluster0.fcugwoe.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
db.once('open', () => {
  console.log('Connected to MongoDB');
});

Start your server using the following command:

node app.js

Output:

Subscription Management System with NodeJS and ExpressJS