A RESTful API backend for managing personal expenses, built with Node.js, Express, MongoDB, and Firebase Authentication.
This backend service provides a complete API for expense management, including user authentication, expense CRUD operations, and expense reporting. The application uses Firebase Admin SDK for secure user authentication and MongoDB for data persistence.
- User Authentication: Register, login, logout, and profile management using Firebase Authentication
- Expense Management: Create, read, update, and delete expenses
- Expense Reports: Generate monthly expense reports and filter expenses by category
- Secure API: Protected endpoints using Firebase ID token verification
- Node.js - Runtime environment
- Express.js - Web framework
- MongoDB - Database (using Mongoose ODM)
- Firebase Admin SDK - Authentication and user management
- Axios - HTTP client for Firebase API calls
- Node.js (v14 or higher)
- npm or yarn
- MongoDB database (local or cloud instance like MongoDB Atlas)
- Firebase project with Authentication enabled
-
Clone the repository
git clone https://github.com/dakshcodez/ExpenseTrackerBackend_DakshArora.git cd ExpenseTrackerBackend_DakshArora -
Install dependencies
npm install
-
Firebase Service Account Setup
- Go to Firebase Console → Project Settings → Service Accounts
- Generate a new private key and download the JSON file
- Save it as
serviceAccountKey.jsonin thesrc/directory
-
Create
.envfile Create a.envfile in the root directory with the following variables:# Server Configuration PORT=3000 # MongoDB Configuration MONGO_URI=mongodb://localhost:27017/expense_tracker # Or for MongoDB Atlas: # MONGO_URI=mongodb+srv://username:password@cluster.mongodb.net/expense_tracker?retryWrites=true&w=majority # Firebase Configuration FIREBASE_API_KEY=your_firebase_web_api_key
To get your Firebase API Key:
- Go to Firebase Console → Project Settings → General
- Scroll down to "Your apps" section
- Copy the Web API Key
-
Start the server
# Production mode npm start # Development mode (with nodemon for auto-reload) npm run dev
The server will start on http://localhost:3000 (or the port specified in your .env file).
http://localhost:3000/api
Most endpoints require authentication. Include the Firebase ID token in the Authorization header:
Authorization: Bearer <idToken>
Register User
- POST
/api/register - Body:
{ "email": "user@example.com", "password": "securePassword123" } - Response:
{ "message": "User registerd successfully", "uid": "firebase-user-id" }
Login User
- POST
/api/login - Body:
{ "email": "user@example.com", "password": "securePassword123" } - Response:
{ "message": "Login successful", "idToken": "firebase-id-token", "refreshToken": "firebase-refresh-token", "expiresIn": "3600", "uid": "firebase-user-id" }
Logout User
- POST
/api/logout - Headers:
Authorization: Bearer <idToken> - Response:
{ "message": "Logout successful" }
View User Profile
- GET
/api/profile - Headers:
Authorization: Bearer <idToken> - Response:
{ "user": { "uid": "firebase-user-id", "email": "user@example.com", ... } }
All expense endpoints require authentication.
Create Expense
- POST
/api/expenses - Headers:
Authorization: Bearer <idToken> - Body:
{ "title": "Groceries", "amount": 150.50, "category": "Food", "date": "2024-01-15T00:00:00.000Z" // Optional, defaults to current date } - Response:
{ "message": "Expense added successfully", "id": "expense-id" }
Get All Expenses
- GET
/api/expenses - Headers:
Authorization: Bearer <idToken> - Response:
{ "expenses": [ { "_id": "expense-id", "title": "Groceries", "amount": 150.50, "category": "Food", "date": "2024-01-15T00:00:00.000Z" }, ... ] }
Get Expense by ID
- GET
/api/expenses/:id - Headers:
Authorization: Bearer <idToken> - Response:
{ "expense": { "_id": "expense-id", "title": "Groceries", "amount": 150.50, "category": "Food", "date": "2024-01-15T00:00:00.000Z" } }
Update Expense
- PUT
/api/expenses/:id - Headers:
Authorization: Bearer <idToken> - Body:
{ "title": "Updated Groceries", "amount": 175.00, "category": "Food", "date": "2024-01-15T00:00:00.000Z" } - Response:
{ "message": "Expense updated successfully" }
Delete Expense
- DELETE
/api/expenses/:id - Headers:
Authorization: Bearer <idToken> - Response:
{ "message": "Expense deleted successfully" }
All report endpoints require authentication.
Monthly Expense Report
- GET
/api/reports/monthly?month=1&year=2024 - Headers:
Authorization: Bearer <idToken> - Query Parameters:
month: Month number (1-12)year: Year (e.g., 2024)
- Response:
{ "total": 1500.75, "categories": { "Food": 500.25, "Transport": 300.50, "Entertainment": 700.00 } }
Expenses by Category
- GET
/api/reports/category?category=Food - Headers:
Authorization: Bearer <idToken> - Query Parameters:
category: Category name (e.g., "Food", "Transport")
- Response:
{ "expenses": [ { "_id": "expense-id", "title": "Groceries", "amount": 150.50, "date": "2024-01-15T00:00:00.000Z" }, ... ] }
1. Register a new user:
curl -X POST http://localhost:3000/api/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securePassword123"
}'2. Login:
curl -X POST http://localhost:3000/api/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securePassword123"
}'3. Create an expense (use the idToken from login response):
curl -X POST http://localhost:3000/api/expenses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ID_TOKEN_HERE" \
-d '{
"title": "Monthly Groceries",
"amount": 250.75,
"category": "Food"
}'4. Get all expenses:
curl -X GET http://localhost:3000/api/expenses \
-H "Authorization: Bearer YOUR_ID_TOKEN_HERE"5. Get monthly report:
curl -X GET "http://localhost:3000/api/reports/monthly?month=1&year=2024" \
-H "Authorization: Bearer YOUR_ID_TOKEN_HERE"// Login
const loginResponse = await fetch('http://localhost:3000/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'user@example.com',
password: 'securePassword123'
})
});
const loginData = await loginResponse.json();
const idToken = loginData.idToken;
// Create expense
const expenseResponse = await fetch('http://localhost:3000/api/expenses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${idToken}`
},
body: JSON.stringify({
title: 'Monthly Groceries',
amount: 250.75,
category: 'Food'
})
});
const expenseData = await expenseResponse.json();
console.log(expenseData);ExpenseTrackerBackend_DakshArora/
├── src/
│ ├── config/
│ │ ├── db.js # MongoDB connection
│ │ └── firebase.js # Firebase Admin initialization
│ ├── controllers/
│ │ ├── authController.js # Authentication logic
│ │ ├── expenseController.js # Expense CRUD operations
│ │ └── reportController.js # Report generation logic
│ ├── middlewares/
│ │ └── authMiddleware.js # Firebase token verification
│ ├── models/
│ │ ├── Expense.js # Expense schema
│ │ └── User.js # User schema
│ ├── routes/
│ │ ├── authRoutes.js # Authentication routes
│ │ ├── expenseRoutes.js # Expense routes
│ │ └── reportRoutes.js # Report routes
│ ├── serviceAccountKey.json # Firebase service account (not in git)
│ └── index.js # Application entry point
├── .env # Environment variables (not in git)
├── package.json
└── README.md
The API returns standard HTTP status codes:
200- Success201- Created400- Bad Request (missing/invalid parameters)401- Unauthorized (invalid/missing token)404- Not Found409- Conflict (e.g., email already exists)500- Internal Server Error
- Never commit
serviceAccountKey.jsonor.envto version control - Keep your Firebase API key secure
- Use HTTPS in production
- Regularly rotate service account keys
- Validate and sanitize all user inputs
ISC
Daksh Arora
Contributions are welcome! Please feel free to submit a Pull Request.