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.pywallet_routes.pytransaction_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: listCustom 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
Dependsfor 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_DBto ensure consistent precision across monetary fields. -
The
to_dict()methods in models enable serialization for API responses, convertingObjectIdfields 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
.envfile 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
-vflag:pytest -v
-
Fixtures: The tests use
pytestfixtures for setup and teardown operations. These are defined intests/E2E/functional_tests/conftest.pyand include fixtures for database connections, test clients, and test data setup. -
Async Tests: The tests are marked with
@pytest.mark.asyncioto 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.pyfor wallet-related teststest_category_routes.pyfor 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: