{"id":29055090,"url":"https://github.com/devsrv/py-starter","last_synced_at":"2026-04-22T21:36:29.340Z","repository":{"id":298859323,"uuid":"1000800890","full_name":"devsrv/py-starter","owner":"devsrv","description":"🚀 Production-ready FastAPI template with daily log rotation, type safety, MongoDB/Redis support, task scheduling, Cloud and Local filesystem and comprehensive error handling.","archived":false,"fork":false,"pushed_at":"2025-06-25T13:55:54.000Z","size":67,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-25T14:49:42.936Z","etag":null,"topics":["fastapi","python","python-boilerplate"],"latest_commit_sha":null,"homepage":"https://itsrav.dev/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devsrv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-12T10:43:19.000Z","updated_at":"2025-06-25T13:55:57.000Z","dependencies_parsed_at":"2025-06-13T09:49:57.805Z","dependency_job_id":null,"html_url":"https://github.com/devsrv/py-starter","commit_stats":null,"previous_names":["devsrv/py-starter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/devsrv/py-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsrv%2Fpy-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsrv%2Fpy-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsrv%2Fpy-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsrv%2Fpy-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devsrv","download_url":"https://codeload.github.com/devsrv/py-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devsrv%2Fpy-starter/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262183456,"owners_count":23271848,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","python","python-boilerplate"],"created_at":"2025-06-27T03:39:47.327Z","updated_at":"2026-04-22T21:36:29.319Z","avatar_url":"https://github.com/devsrv.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FastAPI Production Starter\n\n🚀 Production-ready FastAPI template with daily log rotation, type safety, MongoDB/Mysql/Redis support, task scheduling, and comprehensive error handling.\n\n**Features:** Auto API docs • Daily logs • Type validation • API auth • Background jobs • Health checks • Cloud and local File System\n\nPerfect for microservices and data processing APIs. Skip the boilerplate, start building features.\n\n## Setup\n\n### Modern Approach (Recommended)\n\nUsing `uv` for faster dependency management:\n\n```shell\n# Install uv\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Create virtual environment and install dependencies\nuv venv\n\n# Install all dependencies from pyproject.toml\nuv sync\n\n# Or install with all optional dependency groups\nuv sync --all-extras\n\n# Or install specific optional groups\nuv sync --extra dev --extra types\n\n# Configure environment\ncp .env.example .env\n```\n\n### Traditional Approach\n\n```shell\nsudo apt-get install python3-venv\npython3 -m venv venv\nsource venv/bin/activate\npip install --upgrade pip\npip install -e .  # Install from pyproject.toml\n# Or with optional dependencies:\npip install -e \".[dev,types]\"\ncp .env.example .env\n```\n\n## Development\n\n### Code Quality\n\nUsing `ruff` for fast linting and formatting:\n\n```shell\n# Format code\nuv run ruff format .\n\n# Lint and auto-fix\nuv run ruff check . --fix\n\n# Type checking\nuv run mypy .\n\n# All in one\n.scripts/mypy.sh\n```\n\n### Managing Dependencies\n\n```shell\n# Add a package\nuv add package-name\n\n# Add with specific version\nuv add package-name==1.2.3\n\n# Add as dev dependency\nuv add --dev package-name\n\n# Update a package\nuv lock --upgrade-package \u003cpackage_name\u003e\nuv sync\n```\n\n## Start fastapi\n\n```shell\n# Using uv (no venv activation needed!)\nuvicorn app:app --reload # for local development\nuvicorn app:app --host 0.0.0.0 --port 8000 --workers 4 # in production to expose to the world\n\n# Or if you prefer to activate the venv first:\nsource .venv/bin/activate\nuvicorn app:app --reload\n```\n\n**CORS Configuration**:\n\n-   **Development**: All origins are allowed (`*`) for easier testing\n-   **Production**: You must set `ALLOWED_ORIGINS` in your `.env` file (comma-separated list)\n\n### Endpoints:\n\n```bash\ncurl -X GET http://localhost:8000/health\n\ncurl -X POST http://localhost:8000/test \\\n     -H \"Content-Type: application/json\" \\\n     -H \"X-API-KEY: your-api-key\" \\\n     -d '{\n            \"org_id\": \"1\"\n        }'\n```\n\n## Services required in server\n\n-   Redis\n\n```bash\nsudo apt update\nsudo apt install redis-server\nsudo systemctl status redis\n```\n\n### Check app logs\n\n```bash\ncat storage/logs/app-yyyy-mm-dd.log\ncat storage/logs/error-yyyy-mm-dd.log\n```\n\n## Important\n\nmake sure to call `await app_boot()` in your entry file (if not using `src.app.main.py` and `app.py` as it is already done there)\n\n### DB Usage\n\n```python\nfrom src.db.async_mongo import mongo_manager, get_collection\nfrom src.db.async_mysql import mysql_manager, fetch_one, execute_query, execute_transaction\n\nasync def main():\n    await app_boot()\n\n    try:\n        await mongo_manager.initialize()\n        await mysql_manager.initialize()\n\n        \"\"\"\n        ======================================================\n        Mongo Query\n        ======================================================\n        \"\"\"\n        users_collection = await get_collection(\"users\") # using default database\n        user = users_collection.find_one({\"_id\": user_id})\n\n        analytics_db = mongo_manager.get_database(\"analytics\") # use a different database\n        user_stats = await analytics_db.user_stats.find_one({\"user_id\": user_id})\n\n        \"\"\"\n        ======================================================\n        Mysql Query\n        ======================================================\n        \"\"\"\n        user = await fetch_one(\"SELECT * FROM users WHERE id = %s\", (user_id,))\n        users = await execute_query(\"SELECT * FROM users WHERE active = %s\", (True,))\n\n        queries = [\n            (\"UPDATE accounts SET balance = balance - %s WHERE id = %s\", (amount, from_account)),\n            (\"INSERT INTO transactions (from_account, to_account, amount) VALUES (%s, %s, %s)\",\n            (from_account, to_account, amount))\n        ]\n        await execute_transaction(queries)\n\n    except Exception as e:\n        logger.error(f\"Critical error in batch generate execution: {str(e)}\")\n        await async_report(f\"Critical error in batch generate execution: {str(e)}\", NotificationType.ERROR)\n        raise\n    finally:\n        await mongo_manager.close()\n        await mysql_manager.close()\n```\n\n### Helper \u0026 Utilities\n\n```python\nawait async_report(\"Message ...\", NotificationType.WARNING) # notify (google chat)\n\nget_md5(\"value\") # md5 hash\n\nutcnow() # based on utc\nnow() # based on app timezone\nto_app_timezone(date) # convert date to app tz\n```\n\n## API Rate Limit\n\n### Websocket\n\n#### Basic Usage (Connection Rate Limiting)\n\n```python\nfrom src.utils.ws_rate_limiter import ws_rate_limit\n\n@router.websocket(\"/endpoint\")\n@ws_rate_limit(requests=10, window=60, scope=\"connection\")\n@require_ws_auth\nasync def websocket_endpoint(websocket: WebSocket):\n    await websocket.accept()\n    # Your WebSocket logic here\n```\n\n**Parameters:**\n\n-   `requests`: Maximum number of connections allowed (default: 10)\n-   `window`: Time window in seconds (default: 60)\n-   `scope`: \"connection\" for limiting new connections, \"message\" for limiting messages\n\n#### Advanced Usage (Message Rate Limiting)\n\nFor rate limiting individual messages within an active WebSocket connection:\n\n```python\nfrom src.utils.ws_rate_limiter import check_message_rate_limit\n\n@router.websocket(\"/endpoint\")\n@require_ws_auth\nasync def websocket_endpoint(websocket: WebSocket):\n    await websocket.accept()\n\n    try:\n        while True:\n            data = await websocket.receive_text()\n\n            # Check rate limit for each message\n            if not await check_message_rate_limit(\n                websocket,\n                \"generate\",\n                requests=20,\n                window=60\n            ):\n                await websocket.send_json({\n                    \"type\": \"error\",\n                    \"content\": \"Rate limit exceeded. Please slow down.\"\n                })\n                continue\n\n            # Process message...\n    except WebSocketDisconnect:\n        pass\n```\n\n### REST API\n\nFollow app.py\n\n```python\n@limiter.limit(\"10/minute\")\nasync def test(request: Request, response: Response):\n    #...\n```\n\n## Task Scheduling\n\n#### Using decorators (recommended)\n\n```python\nfrom .async_scheduler import scheduler\n\n@scheduler.schedule(\"*/2 * * * *\", name=\"data_sync\")\nasync def sync_data():\n    # async function\n\n@scheduler.schedule(\"0 9 * * 1-5\", name=\"weekday_report\")\ndef generate_weekday_report():\n    # This is a sync function - it will run in an executor\n```\n\n#### Using convenience methods\n\n```python\nasync def check_queue():\n    # ...\n\n scheduler.everyMinute(check_queue, name=\"queue_check\")\n # everyMinute | everyFiveMinutes | everyTenMinutes | everyThirtyMinutes | hourly | hourlyAt | daily | dailyAt | weekly | weeklyOn | monthly | monthlyOn\n\n```\n\n#### Adding tasks with custom parameters\n\n```python\nasync def send_notification(user_id: int, message: str):\n   # ...\n\ntask = scheduler.add_task(\n   send_notification,\n   \"0 10 * * *\",  # Daily at 10 AM\n   name=\"daily_reminder\",\n   # Arguments for the function\n   123,  # user_id\n   message=\"Don't forget to check your tasks!\"\n)\ntask.max_retries = 5\ntask.retry_delay = 120  # 2 minutes\n```\n\n### Run while local development\n\n```bash\npython -m src.schedule.example_usage.py # create your own schedule task files\n```\n\n### Setup in Production\n\n```bash\nwhich python # inside code root while your venv is activated\n\n# should return something like: /home/sourav/apps/py-starter/venv/bin/python\n```\n\nNow refer to `src/schedule/stub/README.md` and replace `/home/ubuntu/apps/aw-ai-resume-parser/venv/bin/python` with your `\u003cwhich python\u003e` path\n\n## Filesystem\n\nRefer `src.filesystem.file_manager.py` to check all supported methods\n\n```python\n\"\"\"Quick Guide of how to use the cloud file manager.\"\"\"\n\nfile_manager = FileManager() # using default filemanager driver (check boot.py)\n\n# To manually register or use a driver (preferably in boot.py)\nminio_storage = S3CompatibleStorage.for_minio(\n    bucket_name=Config.MINIO_BUCKET,\n    endpoint_url=Config.MINIO_ENDPOINT,\n    access_key=Config.MINIO_ACCESS_KEY,\n    secret_key=Config.MINIO_SECRET_KEY,\n)\nawait file_manager.add_provider(StorageProvider.MINIO, minio_storage, set_as_default=True)\n\n# use multiple adaptars on the fly\nfilesystem = FileManager()\nresume_filesys = self.filesystem.get_provider(StorageProvider.DO_SPACES.value)\n\n# download remote file to tmp\nfile_path = \"media/abc.txt\"\ntemp_file = tempfile.NamedTemporaryFile(delete=False, suffix=Path(file_path).suffix)\ntemp_path = temp_file.name\ntemp_file.close()\n\nawait file_manager.download_to_file(\n    file_path=file_path,\n    local_file_path=temp_path\n)\n\n# Upload\ncontent = b\"Hello, World! This is a test file.\"\nsuccess = await file_manager.upload(\"test/hello.txt\", content, metadata={\"author\": \"Python Script\"})\nprint(f\"Upload successful: {success}\")\n\n# Check if file exists\nexists = await file_manager.exists(\"test/hello.txt\")\nprint(f\"File exists: {exists}\")\n\n# Get file size\nif exists:\n    file_size = await file_manager.size(\"test/hello.txt\")\n    print(f\"File size: {file_size} bytes\")\n\ncopied = await file_manager.copy('test/hello.txt', 'test/hello_copy.txt', source_provider=StorageProvider.S3, dest_provider=StorageProvider.LOCAL)\nprint(f\"File copied in local: {copied}\")\n\n\n\"\"\" Performance improvements with async \"\"\"\nasync def example_performance_improvements():\n    file_manager = FileManager()\n\n    # Process multiple files concurrently instead of sequentially\n    async def process_file(file_path):\n        if await file_manager.exists(file_path):\n            content = await file_manager.download(file_path)\n            # Process content...\n            processed_content = content.upper()\n            await file_manager.upload(f'processed_{file_path}', processed_content)\n            return True\n        return False\n\n    file_paths = ['file1.txt', 'file2.txt', 'file3.txt', 'file4.txt']\n\n    # Process all files concurrently\n    results = await asyncio.gather(*[process_file(path) for path in file_paths])\n\n    # Cross-provider operations with better performance\n    # Copy files from S3 to local storage concurrently\n    async def backup_to_local(file_path):\n        return await file_manager.copy(\n            file_path, f'backup/{file_path}',\n            source_provider=StorageProvider.S3,\n            dest_provider=StorageProvider.LOCAL\n        )\n\n    s3_files = await file_manager.list_files(provider=StorageProvider.S3)\n    backup_results = await asyncio.gather(*[\n        backup_to_local(file.path) for file in s3_files[:10]  # Backup first 10 files\n    ])\n```\n\n## TODO\n\n-   Route Middleware\n-   auto clean older log files error + app\n-   cli arg based commands\n-   email\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsrv%2Fpy-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevsrv%2Fpy-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevsrv%2Fpy-starter/lists"}