A minimal banking-style backend built with Express and Mongoose. This project provides a simple REST API for user authentication, account management, ledger-based balance tracking, and money transfers.
This project is designed to demonstrate a basic banking workflow where each financial movement is recorded in an immutable ledger. Instead of storing only a mutable balance field, the application calculates balances from debit and credit entries, making the system more transparent and auditable.
- Node.js 18 or newer
- npm
- A MongoDB connection string (local MongoDB or MongoDB Atlas)
- A Gmail account or SMTP-compatible email service for sending emails
- Create a .env file in the project root and set the required environment variables.
- Install dependencies:
npm install- Run the server:
npm run devYou can also start it in production mode with:
npm startCreate a .env file with the following variables:
PORT=3000
MONGO_URI=mongodb://127.0.0.1:27017/banking-backend
JWT_SECRET=your_secret_key_here
EMAIL_USER=your_email@gmail.com
CLIENT_ID=your_google_client_id
CLIENT_SECRET=your_google_client_secret
REFRESH_TOKEN=your_google_refresh_tokenImportant files and folders:
- server.js - application entry point, connects to MongoDB and starts the server
- src/app.js - Express app setup and route mounting
- src/config/db.js - MongoDB connection helper
- src/routes/auth.routes.js - authentication routes
- src/routes/account.routes.js - account routes
- src/routes/transaction.routes.js - transaction routes
- src/controllers/auth.controllers.js - register, login, and logout logic
- src/controllers/account.controllers.js - account creation and balance logic
- src/controllers/transaction.controllers.js - transfer and initial funds transaction logic
- src/model/user.models.js - user schema and model
- src/model/account.models.js - account schema and model
- src/model/transaction.models.js - transaction schema and model
- src/model/ledger.models.js - ledger schema and model
- src/middleware/auth.middleware.js - JWT authentication and authorization middleware
- src/services/email.service.js - email notification service
- User registration, login, and logout
- JWT authentication with cookie support
- Account creation and balance lookup
- Fund transfer and initial-funds transactions
- Ledger-based balance tracking
- Idempotency protection using idempotency keys
- Email notifications for registration and transactions
All routes are mounted under the /api base path.
| Method | Path | Purpose | Auth Required |
|---|---|---|---|
| POST | /api/auth/register | Register a new user | No |
| POST | /api/auth/login | Login an existing user | No |
| POST | /api/auth/logout | Logout the current user and blacklist the token | Yes |
| POST | /api/accounts/create | Create a new bank account for the authenticated user | Yes |
| GET | /api/accounts/ | Get all accounts belonging to the authenticated user | Yes |
| GET | /api/accounts/balance/:accountId | Get the balance of a specific account | Yes |
| POST | /api/transactions/ | Create a money transfer transaction between two accounts | Yes |
| POST | /api/transactions/system/initial-funds | Create an initial funds transaction for a system user | Yes |
- Method: POST
- Path: /api/auth/register
- Body:
{
"name": "John Doe",
"email": "john@example.com",
"password": "123456"
}Success response:
{
"user": {
"_id": "user_id",
"email": "john@example.com",
"name": "John Doe"
},
"token": "jwt_token",
"message": "User Created Successfully"
}- Method: POST
- Path: /api/auth/login
- Body:
{
"email": "john@example.com",
"password": "123456"
}Success response:
{
"user": {
"_id": "user_id",
"email": "john@example.com",
"name": "John Doe"
},
"token": "jwt_token",
"message": "User LoggedIn Successfully"
}- Method: POST
- Path: /api/auth/logout
- Requires authentication
- Method: POST
- Path: /api/accounts/create
- Requires authentication
- Method: GET
- Path: /api/accounts/
- Requires authentication
- Method: GET
- Path: /api/accounts/balance/:accountId
- Requires authentication
- Method: POST
- Path: /api/transactions/
- Requires authentication
- Body:
{
"fromAccount": "account_id_here",
"toAccount": "account_id_here",
"amount": 500,
"idempotencyKey": "txn-001"
}Success response:
{
"status": "success",
"message": "Transaction completed successfully",
"transaction": {
"_id": "transaction_id",
"status": "COMPLETED"
}
}- Method: POST
- Path: /api/transactions/system/initial-funds
- Body:
{
"toAccount": "account_id_here",
"amount": 1000,
"idempotencyKey": "initial-funds-001"
}- User: stores login credentials, profile data, and system-user flag
- Account: links a user to a bank account with status and currency
- Transaction: stores transfer details and status
- Ledger: stores immutable debit/credit entries for each transaction
- TokenBlacklist: stores invalidated JWT tokens after logout
The transfer flow works as follows:
- Validate the request body.
- Check that both accounts exist.
- Check whether a transaction already exists for the provided idempotency key.
- Validate that both accounts are active.
- Calculate the sender balance from the ledger.
- Create a transaction in PENDING state.
- Create a DEBIT ledger entry for the sender.
- Create a CREDIT ledger entry for the receiver.
- Mark the transaction as COMPLETED.
- Commit the MongoDB session.
- Send an email notification.
The API returns JSON errors for invalid input, missing authentication, invalid accounts, insufficient funds, and server issues.
Common status codes:
- 400 Bad Request: missing or invalid request data
- 401 Unauthorized: missing or invalid token
- 403 Forbidden: user is not authorized for the requested action
- 404 Not Found: account or user not found
- 422 Unprocessable Entity: duplicate user email
- 500 Internal Server Error: unexpected server issue
- Make sure MONGO_URI is valid and includes the correct database name.
- Ensure the MongoDB server is running before starting the app.
- Check the terminal logs for MongoDB connection status and server start messages.
- Use Postman, Insomnia, or curl to test the endpoints.
- If email sending fails, verify the Gmail OAuth2 credentials in the environment variables.