A robust RESTful backend for a digital ledger system built with Node.js and Express. It supports user authentication with JWT blacklisting, account management, and double-entry style transactions β with email notifications powered by Gmail API via Nodemailer.
βββ config/
β βββ db.js # MongoDB connection setup
βββ controllers/
β βββ accountController.js # Account creation & retrieval logic
β βββ authController.js # Signup, login, logout logic
β βββ transactionController.js # Transaction creation logic
βββ middleware/
β βββ authMiddleware.js # JWT auth & system user guards
βββ models/
β βββ Account.js # Account schema
β βββ BlackList.js # JWT blacklist schema
β βββ Ledger.js # Ledger entry schema
β βββ Transaction.js # Transaction schema
β βββ User.js # User schema
βββ routes/
β βββ accountRoutes.js # Account-related routes
β βββ authRoutes.js # Auth-related routes
β βββ transactionRoutes.js # Transaction-related routes
βββ services/
β βββ email.service.js # Gmail API + Nodemailer email service
βββ app.js # Express app setup & route mounting
βββ server.js # Server entry point
βββ package.json
- π JWT Authentication β Secure signup, login, and logout
- π« Token Blacklisting β Invalidated tokens stored in DB to prevent reuse after logout
- π€ System User β A privileged system user for seeding initial/internal transactions
- π Account Management β Create accounts and fetch all accounts for the logged-in user
- πΈ Transactions β Create transactions with ledger entries; initial funding by system user
- π§ Email Notifications β Gmail API + Nodemailer for transactional emails
- π‘οΈ Middleware Guards β
authUserfor regular users,authSystemMiddlewarefor system-only routes
- Node.js v18+
- MongoDB (local or Atlas)
- A Google Cloud project with Gmail API enabled and OAuth2 credentials
git clone https://github.com/Manvendra-2006/Backend-Ledger.git
cd Backend-Ledger
npm installCreate a .env file in the root directory:
PORT=5000
MONGO_URI=mongodb://localhost:27017/ledger_db
JWT_SECRET=your_jwt_secret_here
# Gmail API (OAuth2)
GMAIL_CLIENT_ID=your_google_client_id
GMAIL_CLIENT_SECRET=your_google_client_secret
GMAIL_REFRESH_TOKEN=your_oauth2_refresh_token
GMAIL_USER=your_email@gmail.com
# System User
SYSTEM_USER_EMAIL=system@ledger.internal
SYSTEM_USER_PASSWORD=your_system_password
SYSTEM_SECRET_KEY=your_system_secret_key# Development
npm run dev
# Production
npm start| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /signup |
Public | Register a new user |
| POST | /login |
Public | Login and receive JWT token |
| POST | /logout |
Private | Logout and blacklist the current token |
// Request Body
{
"name": "John Doe",
"email": "john@example.com",
"password": "securepassword"
}
// Response 201
{
"message": "User registered successfully",
"token": "<jwt_token>"
}// Request Body
{
"email": "john@example.com",
"password": "securepassword"
}
// Response 200
{
"message": "Login successful",
"token": "<jwt_token>"
}// Headers
Authorization: Bearer <jwt_token>
// Response 200
{
"message": "Logged out successfully"
}
| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | / |
Private | Create a new account for logged-in user |
| GET | / |
Private | Get all accounts of the logged-in user |
| GET | /:accountId/balance |
Private | Get balance of a specific account |
// Headers
Authorization: Bearer <jwt_token>
// No request body needed β account is created for the logged-in user automatically
// Response 201
{
"message": "Account created",
"account": { ... }
}// Headers
Authorization: Bearer <jwt_token>
// Response 201
{
"message": "Logged-in-user account is get",
"accounts": [ { ... }, { ... } ]
}// Headers
Authorization: Bearer <jwt_token>
// Response 201
{
"accountId": "64abc...",
"balance": 5000
}| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | / |
Private | Transfer money between two accounts |
| POST | /system/initial |
System Only | System user credits initial funds to a user account |
// Headers
Authorization: Bearer <jwt_token>
// Request Body
{
"fromAccount": "64abc...",
"toAccount": "64def...",
"amount": 500,
"idempotenezkey": "unique-key-uuid-here"
}
// Response 201
{
"message": "Transaction completed successfully",
"transaction": { ... }
}
β οΈ idempotenezkeyis required and must be unique per transaction request. If a transaction with the same key already exists, the API returns its current status (COMPLETED,PENDING,FAILED, orREVERSED) without creating a duplicate.
// Headers
Authorization: Bearer <system_jwt_token>
// Request Body
{
"toAccount": "64abc...",
"amount": 10000,
"idempotenezkey": "unique-key-uuid-here"
}
// Response 201
{
"message": "Intial funds transaction completed successfully",
"transaction": { ... }
}Validates the JWT from the Authorization header and checks that it is not blacklisted. Attaches the decoded user to req.user.
Extends authUser to also verify an additional x-system-key header, ensuring only the privileged system user can access protected system routes.
Uses Nodemailer with Google OAuth2 (Gmail API) for sending transactional emails. After every successful user-to-user transaction, an email notification is automatically sent to the sender.
// services/email.service.js
// Called internally after createTransaction completes
sendtransactionEmail(userEmail, userName, amount, toAccount)Make sure your Google Cloud project has the Gmail API enabled and you've completed the OAuth2 consent flow to generate a valid refresh token.
| Model | Description |
|---|---|
User |
Stores user credentials and profile |
Account |
Represents a financial account owned by a user |
Transaction |
Records a transfer between two accounts |
Ledger |
Double-entry ledger records (debit/credit per transaction) |
BlackList |
Stores invalidated JWT tokens for logout enforcement |
- Passwords are hashed with bcrypt before storage
- JWT tokens are stateless but blacklisted on logout via DB check
- System routes are double-guarded with JWT + a secret key header
.envvariables keep all secrets out of source code (never commit.env)
| Layer | Technology |
|---|---|
| Runtime | Node.js (ESM β "type": "module") |
| Framework | Express.js v5 |
| Database | MongoDB + Mongoose v9 |
| Auth | jsonwebtoken + bcryptjs |
nodemailer + Gmail API (OAuth2) |
|
| Dev Server | nodemon (npm run dev) |
| Environment | dotenv |
| Other | cookie-parser, cors |
- Ensure the system user is seeded in the database (with
systemUser: true) before using the/system/initialendpoint. - The
Ledgermodel follows double-entry bookkeeping β every transaction creates both aDEBITentry on the sender's account and aCREDITentry on the receiver's account. - All transaction operations run inside a MongoDB session (
startSession+commitTransaction) to guarantee atomicity. - The
idempotenezkeyfield on transactions prevents duplicate processing β always send a unique key per request (e.g. a UUID). - Transactions go through status stages:
PENDINGβCOMPLETED(orFAILED/REVERSEDon errors). - Token blacklist entries can be cleaned up periodically (e.g., via a cron job) to remove expired tokens.