A RESTful API built with Node.js, Express.js, and MongoDB that allows users to manage their expenses.
- Features
- Tech Stack
- Project Structure
- Installation & Setup
- API Documentation
- Authentication & Security
- Error Handling
- Contributing
- Contact
- User Authentication: Register and login with JWT-based authentication.
- CRUD Operations: Create, Read, Update, and Delete expense items.
- Filtering: Fetch expense items based on categories or date ranges.
- Security: Passwords are securely hashed, and users can access only their own data.
- Backend: Node.js, Express.js
- Database: MongoDB (Mongoose)
- Authentication: JWT (jsonwebtoken) & bcrypt for password hashing
- Validation: Zod
- Error Handling: Centralized middleware
expense-tracker-api/
│── src/
│ ├── controllers/ # Request handling logic
│ ├── models/ # Mongoose schemas
│ ├── routes/ # API route handlers
│ ├── middlewares/ # Authentication & validation
│ ├── utils/ # Utility functions
│ ├── config/ # Database & environment config
│ ├── app.ts # Express app setup
│ ├── server.ts # Main entry point
│── .env # Environment variables
│── package.json
│── README.md
git clone https://github.com/melaku3/expense-tracker-api.git
cd expense-tracker-apinpm installCreate a .env file in the root directory and configure:
PORT=3000
MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_secret_key
npm startThe API will run at http://localhost:3000.
POST /api/auth/signupRequest Body (JSON):
{
"username": "johndoe",
"email": "johndoe@example.com",
"password": "securepassword"
}Response:
{
"message": "User created successfully"
}POST /api/auth/loginRequest Body (JSON):
{
"email": "johndoe@example.com",
"password": "securepassword"
}Response:
{
"message": "User logged in successfully"
}GET /api/auth/meHeaders:
Authorization: Bearer your_jwt_token
Response:
{
"message": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}POST /api/expensesHeaders:
Authorization: Bearer your_jwt_token
Request Body (JSON):
{
"categoryId": "651234abcd",
"amount": 50,
"description": "Grocery Shopping",
"date": "2023-10-01"
}Response:
{
"message": "Expense created successfully"
}GET /api/expensesHeaders:
Authorization: Bearer your_jwt_token
Response:
[
{
"_id": "651234abcd",
"categoryId": {
"_id": "651234abcd",
"type": "expense",
"name": "Grocery",
"colorCode": "#ff0000",
"description": "Grocery Shopping"
},
"amount": 50,
"description": "Grocery Shopping",
"date": "2023-10-01",
"userId": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}
]GET /api/expenses/:idHeaders:
Authorization: Bearer your_jwt_token
Response:
{
"_id": "651234abcd",
"categoryId": {
"_id": "651234abcd",
"type": "expense",
"name": "Grocery",
"colorCode": "#ff0000",
"description": "Grocery Shopping"
},
"amount": 50,
"description": "Grocery Shopping",
"date": "2023-10-01",
"userId": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}PATCH /api/expenses/:idHeaders:
Authorization: Bearer your_jwt_token
Request Body (JSON):
{
"amount": 60
}Response:
{
"message": "Expense updated successfully"
}DELETE /api/expenses/:idHeaders:
Authorization: Bearer your_jwt_token
Response:
{
"message": "Expense deleted successfully"
}GET /api/expenses/filterHeaders:
Authorization: Bearer your_jwt_token
Query Parameters:
categoryId, minAmount, maxAmount, startDate, endDate, sortBy, limit, page
Response:
[
{
"_id": "651234abcd",
"categoryId": {
"_id": "651234abcd",
"type": "expense",
"name": "Grocery",
"colorCode": "#ff0000",
"description": "Grocery Shopping"
},
"amount": 50,
"description": "Grocery Shopping",
"date": "2023-10-01",
"userId": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}
]POST /api/categoriesHeaders:
Authorization: Bearer your_jwt_token
Request Body (JSON):
{
"name": "Grocery",
"type": "expense",
"description": "Grocery Shopping",
"colorCode": "#ff0000"
}Response:
{
"message": "Category created successfully"
}GET /api/categoriesHeaders:
Authorization: Bearer your_jwt_token
Response:
[
{
"_id": "651234abcd",
"name": "Grocery",
"type": "expense",
"description": "Grocery Shopping",
"colorCode": "#ff0000",
"userId": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}
]GET /api/categories/:idHeaders:
Authorization: Bearer your_jwt_token
Response:
{
"_id": "651234abcd",
"name": "Grocery",
"type": "expense",
"description": "Grocery Shopping",
"colorCode": "#ff0000",
"userId": {
"_id": "651234abcd",
"username": "johndoe",
"email": "johndoe@example.com",
"role": "user"
}
}PATCH /api/categories/:idHeaders:
Authorization: Bearer your_jwt_token
Request Body (JSON):
{
"name": "Supermarket"
}Response:
{
"message": "Category updated successfully"
}DELETE /api/categories/:idHeaders:
Authorization: Bearer your_jwt_token
Response:
{
"message": "Category deleted successfully"
}- JWT Authentication: Users must include a valid JWT token in the Authorization header to access protected routes.
- Password Hashing: Uses bcrypt to securely hash passwords.
- Access Control: Users can only manage their own expenses.
| Error Type | Response Code | Example Message |
|---|---|---|
| Invalid Credentials | 401 | "Invalid email or password" |
| Unauthorized Access | 403 | "Access denied" |
| Resource Not Found | 404 | "Expense not found" |
| Validation Error | 400 | "Field is required" |
| Server Error | 500 | "Internal server error" |
Contributions are welcome! Please fork the repository and create a pull request.
For any issues, feel free to reach out! 🚀
Email: emelaku63@gmail.com
GitHub: melaku3