A RESTful API for managing personal income and expenses.
The application allows users to create financial categories, record income and expense transactions, filter transaction history, and generate monthly financial reports.
- Create categories
- View all categories
- View a category by ID
- Update categories
- Delete unused categories
- Prevent duplicate category names within the same transaction type
- Prevent category type changes when the category already contains transactions
- Create income and expense transactions
- View all transactions
- View a transaction by ID
- Update transactions
- Delete transactions
- Filter transactions by:
- transaction type
- category
- start date
- end date
- Total income
- Total expenses
- Current balance
- Expense totals grouped by category
- Positive transaction amounts
- Maximum of two decimal places
- Category and transaction type consistency
- Valid date ranges
- Protection against deleting categories that contain transactions
- Category controller tests
- Transaction controller tests
- Monthly report service tests
- SQLite in-memory test database
- C#
- .NET 8
- ASP.NET Core Web API
- Entity Framework Core
- SQLite
- Swagger / OpenAPI
- xUnit
- Git
expense-tracker-api
├── src
│ └── ExpenseTracker.Api
│ ├── Controllers
│ ├── Data
│ ├── DTOs
│ │ ├── Categories
│ │ ├── Reports
│ │ └── Transactions
│ ├── Enums
│ ├── Migrations
│ ├── Models
│ ├── Services
│ └── Program.cs
├── tests
│ └── ExpenseTracker.Tests
│ ├── Controllers
│ ├── Helpers
│ └── Services
├── ExpenseTracker.sln
└── README.md
The application uses two main entities.
A category describes the purpose of a financial transaction.
Fields:
IdNameType
A category can have one of two types:
IncomeExpense
A financial transaction represents one income or expense operation.
Fields:
IdAmountInCentsTypeDescriptionDateCategoryId
Amounts are stored as integer cents to avoid floating-point precision issues.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/categories |
Get all categories |
| GET | /api/categories/{id} |
Get a category by ID |
| POST | /api/categories |
Create a category |
| PUT | /api/categories/{id} |
Update a category |
| DELETE | /api/categories/{id} |
Delete a category |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/transactions |
Get and filter transactions |
| GET | /api/transactions/{id} |
Get a transaction by ID |
| POST | /api/transactions |
Create a transaction |
| PUT | /api/transactions/{id} |
Update a transaction |
| DELETE | /api/transactions/{id} |
Delete a transaction |
Available transaction filters:
GET /api/transactions?type=Expense
GET /api/transactions?categoryId=2
GET /api/transactions?from=2026-07-01&to=2026-07-31
GET /api/transactions?type=Expense&categoryId=2&from=2026-07-01&to=2026-07-31
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/reports/monthly |
Generate a monthly financial report |
Example:
GET /api/reports/monthly?year=2026&month=7
{
"name": "Food",
"type": "Expense"
}{
"name": "Salary",
"type": "Income"
}{
"amount": 45.50,
"type": "Expense",
"description": "Groceries",
"date": "2026-07-09",
"categoryId": 2
}{
"amount": 2500.00,
"type": "Income",
"description": "Monthly salary",
"date": "2026-07-09",
"categoryId": 1
}{
"year": 2026,
"month": 7,
"totalIncome": 2800,
"totalExpense": 77.9,
"balance": 2722.1,
"expensesByCategory": [
{
"categoryName": "Food",
"amount": 65.5
},
{
"categoryName": "Transport",
"amount": 12.4
}
]
}Install:
- .NET 8 SDK
- Git
Check the installed .NET version:
dotnet --versiongit clone https://github.com/foolin1/expense-tracker-api.git
cd expense-tracker-apidotnet restoredotnet builddotnet tool restoredotnet ef database update `
--project .\src\ExpenseTracker.Api\ExpenseTracker.Api.csproj `
--startup-project .\src\ExpenseTracker.Api\ExpenseTracker.Api.csprojDatabase migrations are also applied automatically when the application starts.
dotnet run --project .\src\ExpenseTracker.Api\ExpenseTracker.Api.csprojOpen the local address displayed in the terminal.
The root URL automatically redirects to Swagger UI:
http://localhost:<port>/swagger
Run all automated tests:
dotnet testThe test project uses a separate in-memory SQLite database. The local application database is not modified while tests are running.
The application follows a simple layered structure:
HTTP Request
↓
Controller
↓
Application validation
↓
Entity Framework Core
↓
SQLite database
Monthly report calculations are separated into ReportService, while controllers handle HTTP requests and responses.
DTO classes are used to separate the public API contract from database entities.
The API returns standard HTTP status codes:
200 OK— successful request201 Created— resource created204 No Content— resource deleted400 Bad Request— invalid input404 Not Found— resource not found409 Conflict— duplicate resource or invalid operation
- User authentication and authorization
- Separate financial accounts
- Recurring transactions
- Budgets and spending limits
- Pagination
- CSV export
- Integration tests using
WebApplicationFactory - PostgreSQL support
- Docker configuration
Educational backend portfolio project built with ASP.NET Core and Entity Framework Core.