Welcome to the developer guide for the My Net Worth App API. This document provides an in-depth look into the technological aspects of the application, aimed at helping developers understand, set up, and contribute to the project effectively.
- Project Overview
- Architecture
- Technologies Used
- Project Structure
- Database Connection
- Application Entry Point
- Modules and Packages
- Coding Standards and Best Practices
- Testing
- Contributing
- Back to User Guide
My Net Worth is a personal financial manager and planner application that enables users to keep an account of their assets, currencies, and overall net worth. It allows users to:
- Track assets and their estimated values.
- Manage different currencies, including fiat and cryptocurrencies.
- Calculate net worth effortlessly.
- Define custom currencies and asset types.
- Manage multiple wallets with different currencies.
- Record and review transaction history.
The application uses a three-tier architecture:
- Handles SSL termination
- Manages rate limiting
- Controls CORS
- Routes requests to application server
- Provides security features:
- Request size limitations
- Origin validation
- DDoS protection
- HTTP/HTTPS redirection
- Handles business logic
- Manages API endpoints
- Processes requests
- Communicates with database
- Stores application data
- Handles data persistence
- Manages transactions
Client -> Nginx (443/80) -> FastAPI (5000) -> MongoDB
- General endpoints protection
- Enhanced security for authentication endpoints
- Burst handling for traffic spikes
- TLS 1.2/1.3 support
- Automatic HTTP to HTTPS redirection
- Certificate management through volume mounts
- Origin whitelist validation
- Pre-flight request handling
- Controlled header exposure
- Python 3.8+
- FastAPI: High-performance web framework for building APIs.
- MongoDB: NoSQL database for storing application data.
- MongoEngine: Object-Document Mapper (ODM) for MongoDB.
- Pydantic: Data validation and settings management.
- PyJWT: Handling JSON Web Tokens for authentication.
- Passlib: Secure password hashing.
- zxcvbn : Checks password strength.
- Blinker: Signal support for MongoEngine.
- Uvicorn: ASGI server for running the application.
- python-dotenv: Manage environment variables.
The project is organized into several directories and modules:
my-net-worth-api/
├── app/
│ ├── api/
│ │ ├── controllers/
│ │ │ └── ... (business logic)
│ │ ├── endpoints/
│ │ │ └── ... (API routes)
│ │ ├── crud/
│ │ │ └── ... (database interaction modules)
│ ├── main.py (application entry point)
├── database/
│ ├── database.py (database connection and initialization)
│ ├── initialize_db.py (database initialization scripts)
├── docs/
│ └── ... (includes all docs other than main readme)
├── models/
│ ├── enums.py (enum classes used across the app)
│ ├── models.py (all MongoEngine models)
│ ├── schemas.py (Pydantic validators for user input)
│ ├── validator_utilities.py (general validators)
│ └── validators.py (model specific validators)
│
├── commons/
│ ├── exception_handlers.py (custom exception handlers)
│ ├── ... (additional shared modules)
├── tests/
│ └── ... (test cases)
├── nginx/
│ ├── conf.d/ (Nginx configuration)
│ └── ssl/
│ ├── certs/ (SSL certificates)
│ └── private/ (Private keys)
├── .env (environment variables)
├── requirements.txt (main project dependencies)
├── requirements-dev.txt (project dependencies for development)
.
.
.
The database connection is managed centrally in the database/database.py
module:
The entry point of the application is app/main.py
. It initializes the FastAPI app, includes routers, and sets up exception handlers.
Custom exception handlers are added for consistent error responses:
from fastapi import HTTPException
from commons.exception_handlers import base_exception_handler, http_exception_handler
app.add_exception_handler(HTTPException, http_exception_handler)
app.add_exception_handler(Exception, base_exception_handler)
Located in app/api/endpoints/
, each module defines routes related to a specific feature, such as:
asset_routes.py
wallet_routes.py
transaction_routes.py
Example Route Definition:
from fastapi import APIRouter, Depends, Path
from app.api.controllers.auth_controller import has_role
from app.api.controllers.wallet_controller import WalletController
from models.enums import RoleEnum as R
from models.schemas import WalletCreateSchema, ResponseSchema
router = APIRouter(prefix="/wallets", tags=["Wallet"])
@router.post(
"",
response_model=ResponseSchema,
)
async def create_wallet_route(
wallet_schema: WalletCreateSchema, user=Depends(has_role(R.USER))
) -> ResponseSchema:
"""
Create a new wallet for the user.
Args:
wallet_schema (WalletCreateSchema): The schema containing wallet details.
user (User): The current user, injected by dependency.
Returns:
ResponseSchema: The response containing the created wallet's ID and a success message.
"""
wallet_id = await WalletController.create_wallet(wallet_schema, user)
return ResponseSchema(data={"id": wallet_id}, message="Wallet created successfully")
Located in app/api/controllers/
, controllers contain the business logic and interact with models.
Example Controller Method:
class WalletController:
@staticmethod
async def create_wallet(wallet_schema: WalletCreateSchema, user: User):
# Business logic to create a wallet
wallet = Wallet(**wallet_schema.dict(), user_id=user.id)
wallet.save()
return str(wallet.id)
Defined in models/models.py
, using MongoEngine for ODM.
Example Model Definition:
from mongoengine import Document, StringField, ReferenceField, ListField
from models.user import User
class Wallet(Document):
name = StringField(required=True)
type = StringField(required=True)
user_id = ReferenceField(User, required=True)
balances_ids = ListField(ReferenceField('Balance'))
Defined in models/schemas.py
, using Pydantic for request and response validation.
Example Schema:
from pydantic import BaseModel
class WalletCreateSchema(BaseModel):
name: str
type: str
balances_ids: list
Custom exception handlers in commons/exception_handlers.py
ensure consistent error responses.
Example Exception Handler:
from fastapi.responses import JSONResponse
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"exception_name": exc.__class__.__name__, "detail": exc.detail},
)
- PEP 8: Follow Python's PEP 8 style guide for code consistency.
- Type Hinting: Use type annotations for better code readability and tooling support.
- Dependency Injection: Utilize FastAPI's
Depends
for injecting dependencies like the current user. - Modular Design: Keep the code modular by separating concerns into different modules and packages.
- Documentation: Include Google-style docstrings for all endpoints.
- Error Handling: Use centralized exception handlers for consistent error responses.
- Security: Implement proper authentication and authorization checks.
-
Decimal precision is controlled using
PRECISION_LIMIT_IN_DB
to ensure consistent precision across monetary fields. -
The
to_dict()
methods in models enable serialization for API responses, convertingObjectId
fields to strings and including related data as needed.
The My Net Worth App API
includes a comprehensive suite of tests to ensure the functionality and reliability of the application. These tests are written using pytest
. Now the main focus of tests are E2E test of routes. There can be different kind of tests written but for now I have written positive tests that checks intended usage of routes, some validation and negative tests are also included.
-
Install Required Packages: Ensure you have all the necessary packages installed. You can do this by running:
pip install -r requirements-dev.txt
-
Configure Test Database: The tests use a separate test database to avoid affecting production data. Ensure your test database is configured correctly in
database/database.py
. -
Environment Variables: Make sure your
.env
file is set up correctly with any necessary environment variables for testing.
-
Run All Tests: To run all tests in the project, navigate to the root directory of your project and execute:
pytest
-
Run Specific Tests: You can run tests in a specific file or directory by specifying the path. For example, to run tests for wallet routes:
pytest tests/E2E/functional_tests/test_wallet_routes.py
-
Verbose Output: For more detailed output, use the
-v
flag:pytest -v
-
Fixtures: The tests use
pytest
fixtures for setup and teardown operations. These are defined intests/E2E/functional_tests/conftest.py
and include fixtures for database connections, test clients, and test data setup. -
Async Tests: The tests are marked with
@pytest.mark.asyncio
to support asynchronous operations, which is essential for testing async FastAPI endpoints. -
Test Organization: Tests are organized by functionality, with separate files for different API routes and features. For example:
test_wallet_routes.py
for wallet-related teststest_category_routes.py
for category-related tests
We welcome contributions! Please follow these guidelines:
-
Fork the Repository
-
Create a Feature Branch
git checkout -b feature/your-feature-name
-
Commit Your Changes
git commit -m "Description of your changes"
-
Push to the Branch
git push origin feature/your-feature-name
-
Open a Pull Request
Use the links below to navigate between different sections of the documentation: