Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Subscription Tracker API

A Node.js and Express backend for tracking user subscriptions. It leverages MongoDB via Mongoose for data persistence, employs JSON Web Tokens for authentication, and integrates Arcjet for bot detection and rate limiting. The application is modularized into configuration, models, controllers, routes, and middleware layers for maintainability and scalability.

Configuration

Environment Setup (config/env.js)

Loads environment variables from a file corresponding to the current NODE_ENV (defaulting to development).

  • dotenv reads .env.development.local or .env.production.local.
  • Exports critical values: PORT, NODE_ENV, DB_URI, JWT_SECRET, JWT_EXPIRES_IN, ARCJET_ENV, ARCJET_KEY.

Arcjet Security (config/arcjet.js)

Configures Arcjet to protect against common attacks:

  • shield: SQL injection protection
  • detectBot: Bot detection with live blocking
  • tokenBucket: Rate limiting (5 token refill every 10 s, capacity 10)

All rules run in LIVE mode, key sourced from ARCJET_KEY.

Database Connection

MongoDB Connector (database/mongodb.js)

Establishes a Mongoose connection using DB_URI.

  • Throws an error without DB_URI.
  • Logs success or exits on failure.
import mongoose from 'mongoose';
import { DB_URI, NODE_ENV } from '../config/env.js';

if (!DB_URI) {
  throw new Error('Please define the MONGODB_URI environment variable.');
}

const connectToDatabase = async () => {
  try {
    await mongoose.connect(DB_URI);
    console.log(`Connected to database in ${NODE_ENV} mode`);
  } catch (error) {
    console.error('Database connection error:', error);
    process.exit(1);
  }
};

export default connectToDatabase;

Data Models

User Model (models/user.model.js)

Defines users with validation rules:

Field Type Constraints
name String Required, 2–50 chars, trimmed
email String Required, unique, trimmed, lowercase, valid
password String Required, min 6 chars

Timestamps are added automatically.

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, minLength: 2, maxLength: 50, trim: true },
  email: {
    type: String, required: true, unique: true,
    lowercase: true, match: [/\S+@\S+\.\S+/, 'Invalid email']
  },
  password: { type: String, required: true, minLength: 6 }
}, { timestamps: true });

export default mongoose.model('User', userSchema);

Subscription Model (models/subscription.model.js)

Captures subscription details and auto-computes renewalDate.

Field Type Constraints
name String Required, 2–100 chars
price Number Required, 0–1000
currency String Enum: USD, ZAR, GBP (default ZAR)
frequency String Enum: daily, weekly, monthly, yearly
category String Enum: sport, news, entertainment, lifestyle, technology
paymentMethod String Required
status String Enum: active, cancelled, expired (default active)
startDate Date Required, must be ≤ current date
renewalDate Date Required, must be > startDate; calculated in a pre-save hook if omitted
user ObjectId Reference to User, required, indexed
subscriptionSchema.pre('save', function (next) {
  if (!this.renewalDate) {
    const periods = { daily: 1, weekly: 7, monthly: 30, yearly: 365 };
    this.renewalDate = new Date(this.startDate);
    this.renewalDate.setDate(this.renewalDate.getDate() + periods[this.frequency]);
  }
  if (this.renewalDate < new Date()) {
    this.status = 'expired';
  }
  next();
});

export default mongoose.model('Subscription', subscriptionSchema);

Middleware

JSON & URL-Encoded Parsers

  • express.json()
  • express.urlencoded({ extended: false })

Cookie Parser

Parses cookies into req.cookies.

Arcjet Middleware (middlewares/arcjet.middleware.js)

Applies Arcjet protection to all requests. Blocks or logs based on decision.

Authentication Middleware (middlewares/auth.middleware.js)

Verifies JWT from Authorization: Bearer <token>.

  • Attaches req.user on success.
  • Returns 401 on failure.

Error Handler (middlewares/error.middleware.js)

Catches errors and responds with:

  • 404 on Mongoose CastError
  • 400 on duplicate key or validation errors
  • 500 for others

Logs original error to console.

Controllers

Auth Controller (controllers/auth.controller.js)

Handles sign-up, sign-in, and sign-out operations.

// Sign Up
export const signUp = async (req, res, next) => {
  // Creates user, hashes password, issues JWT
};

// Sign In
export const signIn = async (req, res, next) => {
  // Verifies credentials, issues JWT
};

// Sign Out
export const signOut = async (req, res, next) => {
  // TODO: Implement token invalidation or cookie clearing
};

User Controller (controllers/user.controller.js)

Provides user retrieval endpoints.

export const getUsers = async (req, res, next) => { /* Lists all users */ };
export const getUser  = async (req, res, next) => { /* Gets one user */ };

Subscription Controller (controllers/subscription.controller.js)

Handles subscription creation.

export const createSubscription = async (req, res, next) => {
  // Associates subscription with req.user and saves
};

Routes

Authentication Routes (routes/auth.routes.js)

  • POST /api/v1/auth/sign-up → signUp
  • POST /api/v1/auth/sign-in → signIn
  • POST /api/v1/auth/sign-out → signOut

User Routes (routes/user.routes.js)

  • GET /api/v1/users/ → getUsers
  • GET /api/v1/users/:id → getUser (protected)
  • POST /api/v1/users/ → Placeholder
  • PUT /api/v1/users/:id → Placeholder
  • DELETE /api/v1/users/:id → Placeholder

Subscription Routes (routes/subscription.route.js)

  • GET /api/v1/subscriptions/ → Placeholder
  • GET /api/v1/subscriptions/:id → Placeholder
  • POST /api/v1/subscriptions/ → createSubscription (protected)
  • PUT /api/v1/subscriptions/:id → Placeholder
  • DELETE /api/v1/subscriptions/:id → Placeholder
  • PUT /api/v1/subscriptions/user/id → Placeholder
  • PUT /api/v1/subscriptions/:id/cancel→ Placeholder
  • GET /api/v1/subscriptions/upcoming-renewals → Placeholder

Application Entry Point (app.js)

Bootstraps the Express server:

  • Applies parsers, cookie and Arcjet middleware
  • Mounts routers under /api/v1/…
  • Global error handler
  • Serves a welcome message at /
  • Listens on PORT and connects to MongoDB on startup
import express from 'express';
import { PORT } from './config/env.js';
import connectToDatabase from './database/mongodb.js';
import arcjeMiddleware from './middlewares/arcjet.middleware.js';
// … other imports …

const app = express();
app.use(express.json(), express.urlencoded({ extended: false }), cookieParser(), arcjeMiddleware);
// Mount routers and error handler…
app.listen(PORT, async () => {
  console.log(`Running on http://localhost:${PORT}`);
  await connectToDatabase();
});
export default app;

Scripts & Dependencies (package.json)

Script Command
start node app.js
dev nodemon app.js

Key dependencies include express, mongoose, jsonwebtoken, bcryptjs, cookie-parser, and @arcjet/node for security.


❤️ Happy coding!

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages