A comprehensive REST API for personal expense tracking with user authentication, budget management, financial reporting, and AI-powered financial insights.
- Description
- Features
- Technology Stack
- Quick Start
- API Documentation
- Data Models
- Authentication
- Security
- Testing
- Deployment
- Contributing
- Troubleshooting
- FAQ
- License
Expense Tracker API is a robust backend service built with Node.js and Express.js that enables users to manage their personal finances effectively. The API provides comprehensive expense tracking, income management, budget monitoring, and detailed financial reporting with user authentication and security features.
- ✅ Complete user authentication system (register, login, logout, password reset)
- ✅ Full CRUD operations for expenses, income, categories, and budgets
- ✅ Advanced financial reporting (weekly/monthly with budget comparisons)
- ✅ AI-powered financial insights and personalized recommendations
- ✅ Category-based expense organization
- ✅ Budget tracking with spending alerts
- ✅ Recurring transaction support
- 🔒 Session-based authentication with secure cookies
- 🛡️ Rate limiting and brute-force protection
- ✅ Input validation and sanitization
- 🚀 Optimized database queries with aggregation pipelines
- 📊 Comprehensive logging with Morgan
- 📖 Well-documented API with examples
- 🧪 Ready for testing framework integration
- 🐳 Docker support for easy deployment
- 📈 Monitoring and error tracking capabilities
- Node.js - Runtime environment
- Express.js - Web application framework
- MongoDB - NoSQL database
- Mongoose - ODM for MongoDB
- Passport.js - Authentication middleware
- passport-local-mongoose - Password hashing and validation
- Helmet - Security headers
- express-rate-limit - Rate limiting
- express-mongo-sanitize - NoSQL injection prevention
- CORS - Cross-origin resource sharing
- Joi - Schema validation
- Nodemailer - Email services
- Morgan - HTTP request logger
- dotenv - Environment variable management
- express-session - Session management
- swagger-jsdoc - API documentation generation
- swagger-ui-express - API documentation UI
- Nodemon - Development auto-restart
- PM2 - Production process manager
- Node.js (v14 or higher)
- MongoDB (v4.4 or higher)
- npm or yarn
-
Clone the repository
git clone https://github.com/DevOlabode/expense-tracker-API.git
-
Install dependencies
npm install
-
Environment Setup Create a
.envfile in the root directory:PORT=3000 DB_URL=mongodb://localhost:27017/expenseTracker SECRET=your_super_secret_session_key_here NODE_ENV=development EMAIL_USER=your_email@gmail.com EMAIL_PASS=your_app_password AI_KEY=your_groq_api_key_here
-
Start MongoDB Ensure MongoDB is running on your system or update
DB_URLfor cloud instance. -
Run the application
# Development mode (with auto-restart) npm run dev # Production mode npm start
The API will be available at http://localhost:3000
http://localhost:3000
The API includes interactive documentation powered by Swagger UI. Access it at:
http://localhost:3000/api-docs
Get homepage message.
Response (200):
{
"msg": "The Homepage"
}All responses follow this structure:
{
"success": true,
"data": { ... },
"message": "Operation successful"
}{
"success": false,
"error": "Error description",
"code": "ERROR_CODE"
}Register a new user account.
Request Body:
{
"username": "johndoe",
"email": "john.doe@example.com",
"password": "SecurePass123!"
}Response (201):
{
"msg": "User registered successfully"
}Validation Rules:
- Username: 3-30 characters, alphanumeric
- Email: Valid email format
- Password: 8-32 characters, must contain uppercase, lowercase, and number
Authenticate user and establish session.
Request Body:
{
"username": "johndoe",
"password": "SecurePass123!"
}Note: Login uses username, not email.
Response (200):
{
"msg": "Logged in successfully"
}Destroy user session.
Response (200):
{
"msg": "Logged out successfully"
}Get current user profile information.
Response (200):
{
"_id": "60f1b2b3c4d5e6f7g8h9i0j1",
"username": "johndoe",
"email": "john.doe@example.com"
}Delete logged-in user account.
Response (200):
{
"msg": "Account Deleted Successfully"
}Request password reset code.
Request Body:
{
"email": "john.doe@example.com"
}Verify the reset code sent to email.
Request Body:
{
"email": "john.doe@example.com",
"resetCode": "123456"
}Set new password after code verification.
Request Body:
{
"email": "john.doe@example.com",
"newPassword": "NewSecurePass123!"
}Reset password while logged in (requires current password).
Request Body:
{
"currentPassword": "SecurePass123!",
"newPassword": "NewSecurePass456!",
"confirmPassword": "NewSecurePass456!"
}Response (200):
{
"message": "Password updated successfully."
}Create a new expense entry.
Request Body:
{
"name": "Grocery Shopping",
"amount": 85.50,
"category": "60f1b2b3c4d5e6f7g8h9i0j1",
"description": "Weekly groceries at Whole Foods",
"paymentMethod": "Card",
"recurring": false,
"date": "2023-09-15"
}Note: Category is optional.
Response (200):
{
"msg": "Expense Created Successfully",
"expense": {
"_id": "60f1b2b3c4d5e6f7g8h9i0j2",
"name": "Grocery Shopping",
"amount": 85.5,
"category": "60f1b2b3c4d5e6f7g8h9i0j1",
"description": "Weekly groceries at Whole Foods",
"paymentMethod": "Card",
"recurring": false,
"date": "2023-09-15T00:00:00.000Z",
"user": "60f1b2b3c4d5e6f7g8h9i0j1",
"createdAt": "2023-09-15T10:30:00.000Z"
}
}Retrieve all expenses for the authenticated user.
Response (200):
{
"msg": "All Expenses",
"expenses": [
{
"_id": "60f1b2b3c4d5e6f7g8h9i0j2",
"name": "Grocery Shopping",
"amount": 85.5,
"category": {
"_id": "60f1b2b3c4d5e6f7g8h9i0j1",
"name": "Food & Dining"
},
"date": "2023-09-15T00:00:00.000Z"
}
]
}Get a specific expense by ID.
Update an expense entry.
Delete an expense entry.
Create a new income entry.
Request Body:
{
"source": "Salary",
"amount": 3500.00,
"recurring": true,
"notes": "Monthly salary payment",
"date": "2023-09-01"
}Retrieve all income entries.
Get a specific income entry.
Update an income entry.
Delete an income entry.
Create a new expense category.
Request Body:
{
"name": "Transportation",
"description": "Car, public transport, fuel expenses"
}Retrieve all categories.
Get a specific category.
Update a category.
Delete a category.
Create a new budget.
Request Body:
{
"name": "Monthly Food Budget",
"category": "60f1b2b3c4d5e6f7g8h9i0j1",
"amount": 600.00,
"period": "monthly",
"startDate": "2023-09-01",
"endDate": "2023-09-30"
}Retrieve all budgets.
Get a specific budget.
Update a budget.
Delete a budget.
Generate a comprehensive weekly financial report.
Query Parameters:
start(required): Start date (YYYY-MM-DD)end(required): End date (YYYY-MM-DD)
Example Request:
GET /reports/weekly?start=2023-09-01&end=2023-09-07
Response (200):
{
"week": {
"start": "2023-09-01",
"end": "2023-09-07"
},
"totalIncome": 3500.00,
"totalExpenses": 450.75,
"balance": 3049.25,
"expensesByCategory": [
{
"category": "Food & Dining",
"total": 150.50
},
{
"category": "Transportation",
"total": 75.25
}
],
"budgetComparison": [
{
"category": "Food & Dining",
"budget": 300.00,
"spent": 150.50,
"remaining": 149.50,
"percentageUsed": "50.17"
}
]
}Generate a comprehensive monthly financial report.
Query Parameters:
month(required): Month number (1-12)year(required): Year (YYYY)
Example Request:
GET /reports/monthly?month=9&year=2023
Response (200):
{
"month": "9",
"year": "2023",
"totalIncome": 3500.00,
"totalExpenses": 1850.75,
"balance": 1649.25,
"expensesByCategory": [
{
"category": "Food & Dining",
"total": 450.50
},
{
"category": "Transportation",
"total": 275.25
},
{
"category": "Entertainment",
"total": 125.00
}
],
"budgetComparison": [
{
"category": "Food & Dining",
"budget": 600.00,
"spent": 450.50,
"remaining": 149.50,
"percentageUsed": "75.08"
}
]
}Generate AI-powered financial insights and personalized recommendations.
Note: Requires AI_KEY environment variable to be set with a valid Groq API key.
Response (200):
{
"msg": "AI Report",
"report": {
"summary": "A brief overview of the user's financial health.",
"spendingInsights": [
{
"category": "Food",
"status": "Overspending",
"comment": "Spending on food exceeds the allocated budget by 25%."
}
],
"recommendations": [
"Consider reducing dining out expenses to stay within budget.",
"Set aside a fixed percentage of income for emergency savings."
],
"savingsOpportunities": [
{
"category": "Subscriptions",
"suggestion": "Review and cancel unused subscriptions to save monthly costs."
}
]
}
}Error Response (500):
{
"error": "AI service unavailable or API key not configured"
}{
username: String (required, unique),
email: String (required, unique),
resetPasswordToken: String,
resetPasswordExpires: Date
}{
name: String (required),
amount: Number (required, positive),
date: Date (default: now),
category: ObjectId (ref: 'Category'),
description: String,
paymentMethod: String (enum: ['Cash', 'Card', 'Bank Transfer', 'Other']),
recurring: Boolean (default: false),
user: ObjectId (ref: 'User', required)
}{
source: String (required),
amount: Number (required, positive),
date: Date (default: now),
recurring: Boolean (default: false),
notes: String,
user: ObjectId (ref: 'User', required)
}{
name: String (required),
description: String,
user: ObjectId (ref: 'User', required)
}{
name: String (required),
category: ObjectId (ref: 'Category'),
amount: Number (required, positive),
period: String (enum: ['monthly', 'yearly'], default: 'monthly'),
startDate: Date (default: now),
endDate: Date,
user: ObjectId (ref: 'User', required)
}The API uses session-based authentication with Passport.js:
- User registers/logs in
- Server creates a session and sends session cookie
- Client includes cookie in subsequent requests
- Server validates session for protected routes
- Secure: HTTPS only in production
- HTTP-only: Prevents XSS attacks
- SameSite: Protects against CSRF
- Max Age: 24 hours
- Minimum 8 characters
- Maximum 32 characters
- Must contain at least one uppercase letter
- Must contain at least one lowercase letter
- Must contain at least one number
- Session-based authentication with secure cookies
- Password hashing using PBKDF2 via passport-local-mongoose
- Rate limiting on authentication endpoints (5 attempts per 15 minutes)
- Session security with httpOnly, secure, and sameSite flags
- Input validation using Joi schemas
- MongoDB injection prevention with express-mongo-sanitize
- XSS protection via Helmet security headers
- CORS configuration for allowed origins
- HTTPS enforcement in production
- Security headers via Helmet (HSTS, CSP, X-Frame-Options, etc.)
- Request logging for monitoring and debugging
- Store sensitive data in environment variables
- Use strong, randomly generated session secrets
- Regularly update dependencies for security patches
- Implement proper error handling to avoid information leakage
- Use HTTPS in production environments
Automated tests are implemented using Jest and Supertest for integration testing, with MongoDB Memory Server for database mocking.
-
Run all tests
npm test -
Run tests in watch mode
npm run test:watch
-
Run tests with coverage
npm run test:coverage
tests/
├── setup.js # Test configuration and database setup
├── integration/ # Integration tests for API endpoints
│ └── app.test.js # Main integration test file
├── unit/ # Unit tests (to be added)
└── fixtures/ # Test data fixtures (to be added)
const request = require('supertest');
const app = require('../index');
describe('Authentication', () => {
test('should register a new user', async () => {
const response = await request(app)
.post('/register')
.send({
username: 'testuser',
email: 'test@example.com',
password: 'TestPass123!'
});
expect(response.status).toBe(201);
});
});- Unit Tests: Test individual functions and utilities
- Integration Tests: Test API endpoints and database interactions
- End-to-End Tests: Test complete user workflows
npm run dev# Install PM2 globally
npm install -g pm2
# Start the application
pm2 start index.js --name "expense-tracker-api"
# Save PM2 configuration
pm2 save
# Set up PM2 to start on boot
pm2 startup-
Create Dockerfile
FROM node:16-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 3000 CMD ["npm", "start"]
-
Create docker-compose.yml
version: '3.8' services: app: build: . ports: - "3000:3000" environment: - DB_URL=mongodb://mongo:27017/expenseTracker depends_on: - mongo mongo: image: mongo:5.0 ports: - "27017:27017" volumes: - mongo_data:/data/db volumes: mongo_data:
-
Run with Docker Compose
docker-compose up -d
- Set
NODE_ENV=production - Use production MongoDB instance (MongoDB Atlas recommended)
- Configure proper CORS origins
- Set up SSL/TLS certificates
- Use environment-specific configuration
We welcome contributions! Please follow these guidelines:
- Fork the repository
- Create a feature branch
git checkout -b feature/your-feature-name
- Make your changes
- Run tests (when implemented)
- Commit your changes
git commit -m 'Add: brief description of changes' - Push to your branch
git push origin feature/your-feature-name
- Create a Pull Request
- Follow existing code style and naming conventions
- Add Joi validation schemas for new endpoints
- Include proper error handling and logging
- Update documentation for any new features
- Write tests for new functionality
- Use meaningful commit messages
type(scope): description
Types: feat, fix, docs, style, refactor, test, chore
Problem: MongoError: failed to connect to server
Solution:
- Ensure MongoDB is running locally
- Check
DB_URLin.envfile - For cloud MongoDB, verify connection string and IP whitelist
Problem: Unable to login after registration Solution:
- Verify password meets requirements (8-32 chars, uppercase, lowercase, number)
- Check that session cookies are being sent with requests
- Ensure
SECRETis set in environment variables
Problem: Access-Control-Allow-Origin header missing
Solution:
- Check CORS configuration in
index.js - Add your frontend URL to allowed origins
- Ensure credentials are included in requests
Problem: Password reset emails not being received Solution:
- Verify
EMAIL_USERandEMAIL_PASSin.env - For Gmail, use App Passwords instead of regular password
- Check spam folder
Enable debug logging by setting:
NODE_ENV=development
DEBUG=express:*- Monitor database query performance
- Check for N+1 query problems in reports
- Consider adding database indexes for frequently queried fields
Q: Can I use this API with a frontend framework? A: Yes! The API is designed to work with any frontend framework. Configure CORS to allow your frontend's domain.
Q: How do I reset my password?
A: Use the /forgot-password endpoint to receive a reset code via email, then use /verify-reset-code and /new-password to complete the reset.
Q: Are transactions supported? A: Currently, the API doesn't support database transactions. This is planned for future releases.
Q: Can I import/export data? A: Not currently implemented, but you can use the GET endpoints to retrieve data and process it as needed.
Q: What's the maximum number of expenses I can store? A: No hard limit is set, but performance may degrade with very large datasets. Consider pagination for large result sets.
Q: Is the API rate limited? A: Yes, authentication endpoints are rate limited to 5 attempts per 15 minutes to prevent brute force attacks.
This project is licensed under the ISC License - see the LICENSE file for details.
Samuel Olabode
- GitHub: @DevOlabode
- Email: samuelolabode@example.com
The API is configured with CORS to allow requests from frontend applications. In development, it allows http://localhost:3000 and http://localhost:5173. Update the CORS origins in index.js for production deployments.
For questions, bug reports, or feature requests:
- Check the Issues page
- Create a new issue with detailed information
- Include error messages, steps to reproduce, and your environment details
⭐ If you find this project helpful, please give it a star on GitHub!