{"id":43918166,"url":"https://github.com/dperezcabrera/pico-fastapi","last_synced_at":"2026-02-20T23:09:38.219Z","repository":{"id":322009321,"uuid":"1087916089","full_name":"dperezcabrera/pico-fastapi","owner":"dperezcabrera","description":"pico-fastapi integrates Pico-IoC with FastAPI, providing real inversion-of-control, controller-based routing, scoped dependency resolution, and clean application lifecycle management—without global state or manual wiring.","archived":false,"fork":false,"pushed_at":"2026-02-16T08:37:41.000Z","size":165,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-16T16:17:22.812Z","etag":null,"topics":["async","asyncio","controllers","dependency-injection","fastapi","ioc","pico-framework","rest-api","scoped-dependencies","web-framework"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dperezcabrera.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-11-01T22:50:40.000Z","updated_at":"2026-02-16T08:37:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dperezcabrera/pico-fastapi","commit_stats":null,"previous_names":["dperezcabrera/pico-fastapi"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/dperezcabrera/pico-fastapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dperezcabrera%2Fpico-fastapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dperezcabrera%2Fpico-fastapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dperezcabrera%2Fpico-fastapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dperezcabrera%2Fpico-fastapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dperezcabrera","download_url":"https://codeload.github.com/dperezcabrera/pico-fastapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dperezcabrera%2Fpico-fastapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667171,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["async","asyncio","controllers","dependency-injection","fastapi","ioc","pico-framework","rest-api","scoped-dependencies","web-framework"],"created_at":"2026-02-06T22:02:59.858Z","updated_at":"2026-02-20T23:09:38.204Z","avatar_url":"https://github.com/dperezcabrera.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📦 pico-fastapi\n\n[![PyPI](https://img.shields.io/pypi/v/pico-fastapi.svg)](https://pypi.org/project/pico-fastapi/)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dperezcabrera/pico-fastapi)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n![CI (tox matrix)](https://github.com/dperezcabrera/pico-fastapi/actions/workflows/ci.yml/badge.svg)\n[![codecov](https://codecov.io/gh/dperezcabrera/pico-fastapi/branch/main/graph/badge.svg)](https://codecov.io/gh/dperezcabrera/pico-fastapi)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-fastapi\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-fastapi)\n[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-fastapi\u0026metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-fastapi)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=dperezcabrera_pico-fastapi\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-fastapi)\n[![Docs](https://img.shields.io/badge/Docs-pico--fastapi-blue?style=flat\u0026logo=readthedocs\u0026logoColor=white)](https://dperezcabrera.github.io/pico-fastapi/)\n\n# Pico-FastAPI\n\n**[Pico-FastAPI](https://github.com/dperezcabrera/pico-fastapi)** seamlessly integrates **[Pico-IoC](https://github.com/dperezcabrera/pico-ioc)** with **[FastAPI](https://github.com/fastapi/fastapi)**, bringing true inversion of control and constructor-based dependency injection to one of the fastest and most elegant Python web frameworks.\n\nIt provides scoped lifecycles, automatic controller registration, and clean architectural boundaries, without global state and without FastAPI’s function-based dependency system.\n\n\u003e 🐍 Requires Python 3.11+\n\u003e ⚡ Built on FastAPI  \n\u003e ✅ Fully async-compatible  \n\u003e ✅ Real IoC with constructor injection  \n\u003e ✅ Supports singleton, request, session, and websocket scopes\n\nWith Pico-FastAPI you get the speed, clarity, and async performance of FastAPI, enhanced by a real IoC container for clean, testable, and maintainable applications.\n\n---\n\n## 🎯 Why pico-fastapi\n\nFastAPI’s built-in dependency system is function-based, which often ties business logic to the framework. Pico-FastAPI moves dependency resolution into the IoC container, promoting separation of concerns and testability.\n\n| Concern | FastAPI Default | pico-fastapi |\n|----------|-----------------|---------------|\n| Dependency injection | Function-based | Constructor-based |\n| Architecture | Framework-driven | Domain-driven |\n| Testing | Simulate DI calls | Override components in container |\n| Scopes | Manual or ad-hoc | Automatic (singleton, request, session, websocket) |\n\n---\n\n## 🧱 Core Features\n\n- Controller classes with `@controller`\n- Route decorators: `@get`, `@post`, `@put`, `@delete`, `@patch`, `@websocket`\n- Constructor injection for controllers and services\n- Automatic registration into FastAPI\n- Scoped resolution via middleware for request, session, and websocket\n- Full Pico-IoC feature set: profiles, overrides, interceptors, cleanup hooks\n\n---\n\n## 📦 Installation\n\n```bash\npip install pico-fastapi\n```\n\n---\n\n## 🚀 Quick Example\n\n```python\nfrom pico_fastapi import controller, get\n\n@controller(prefix=\"/api\")\nclass ApiController:\n    def __init__(self, service: \"MyService\"):\n        self.service = service\n\n    @get(\"/hello\")\n    async def hello(self):\n        return {\"msg\": self.service.greet()}\n```\n\n```python\nfrom pico_ioc import component\n\n@component\nclass MyService:\n    def greet(self) -\u003e str:\n        return \"hello from service\"\n```\n\n```python\nfrom pico_ioc import init\nfrom fastapi import FastAPI\n\ncontainer = init(\n    modules=[\n        \"controllers\",\n        \"services\",\n        \"pico_fastapi\",\n    ]\n)\n\napp = container.get(FastAPI)\n```\n---\n\n# 🚀 Quick Example (with pico-boot auto-discovery)\n\n### 1. Controller\n\n```python\nfrom pico_fastapi import controller, get\n\n@controller(prefix=\"/api\")\nclass ApiController:\n    def __init__(self, service: \"MyService\"):\n        self.service = service\n\n    @get(\"/hello\")\n    async def hello(self):\n        return {\"msg\": self.service.greet()}\n```\n\n### 2. Service\n\n```python\nfrom pico_ioc import component\n\n@component\nclass MyService:\n    def greet(self) -\u003e str:\n        return \"hello from service\"\n```\n\n### 3. App Initialization (Using pico-boot)\n\n```python\nfrom pico_boot import init\nfrom fastapi import FastAPI\n\n# No need to declare \"pico_fastapi\" anymore.\n# pico-fastapi is auto-discovered via entry points.\n\ncontainer = init(\n    modules=[\n        \"controllers\",\n        \"services\",\n    ]\n)\n\napp = container.get(FastAPI)\n```\n\n---\n\n## 💬 WebSocket Example\n\n```python\nfrom pico_fastapi import controller, websocket\nfrom fastapi import WebSocket\n\n@controller\nclass ChatController:\n    @websocket(\"/ws\")\n    async def chat(self, websocket: WebSocket):\n        await websocket.accept()\n        while True:\n            msg = await websocket.receive_text()\n            await websocket.send_text(f\"Echo: {msg}\")\n```\n\n---\n\n## 🧪 Testing with Overrides\n\n```python\nfrom pico_ioc import init\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\n\nclass FakeService:\n    def greet(self) -\u003e str:\n        return \"test\"\n\ncontainer = init(\n    modules=[\"controllers\", \"services\", \"pico_fastapi\"],\n    overrides={\"MyService\": FakeService()}\n)\n\napp = container.get(FastAPI)\nclient = TestClient(app)\n\nassert client.get(\"/api/hello\").json() == {\"msg\": \"test\"}\n```\n\n---\n\n## 📁 Static Files Configuration Example\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Protocol, runtime_checkable\nfrom fastapi import FastAPI\nfrom starlette.staticfiles import StaticFiles\nfrom pico_ioc import component, configured\nfrom pico_fastapi import FastApiConfigurer\n\n@configured(target=\"self\", prefix=\"fastapi\", mapping=\"tree\")\n@dataclass\nclass StaticSettings:\n    static_dir: str = \"public\"\n    static_url: str = \"/static\"\n\n@component\nclass StaticFilesConfigurer(FastApiConfigurer):\n    priority = -100\n    def __init__(self, settings: StaticSettings):\n        self.settings = settings\n    def configure_app(self, app: FastAPI) -\u003e None:\n        app.mount(self.settings.static_url, StaticFiles(directory=self.settings.static_dir), name=\"static\")\n```\n\n```python\nfrom pico_ioc import init, configuration, YamlTreeSource\nfrom fastapi import FastAPI\n\ncontainer = init(\n    modules=[\n        \"pico_fastapi\",\n        \"static_config\",\n    ],\n    config=configuration(\n        YamlTreeSource(\"config.yml\")\n    ),\n)\n\napp = container.get(FastAPI)\n```\n\n```yaml\nfastapi:\n  title: \"My App\"\n  version: \"1.0.0\"\n  debug: true\n  static_dir: \"public\"\n  static_url: \"/assets\"\n```\n\n---\n\n## 🔐 JWT Authentication Configuration Example\n\n```python\nimport base64\nimport json\nimport hmac\nimport hashlib\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom fastapi import FastAPI, Request\nfrom starlette.middleware.base import BaseHTTPMiddleware\nfrom pico_ioc import component, configured, PicoContainer\nfrom pico_fastapi import FastApiConfigurer\n\ndef _b64url_decode(data: str) -\u003e bytes:\n    padding = \"=\" * (-len(data) % 4)\n    return base64.urlsafe_b64decode(data + padding)\n\ndef _verify_hs256(token: str, secret: str) -\u003e Optional[dict]:\n    parts = token.split(\".\")\n    if len(parts) != 3:\n        return None\n    header_b64, payload_b64, sig_b64 = parts\n    signing_input = f\"{header_b64}.{payload_b64}\".encode()\n    expected = hmac.new(secret.encode(), signing_input, hashlib.sha256).digest()\n    try:\n        signature = _b64url_decode(sig_b64)\n    except Exception:\n        return None\n    if not hmac.compare_digest(signature, expected):\n        return None\n    try:\n        payload_json = _b64url_decode(payload_b64)\n        return json.loads(payload_json.decode())\n    except Exception:\n        return None\n\nclass JwtMiddleware(BaseHTTPMiddleware):\n    def __init__(self, app, container: PicoContainer, secret: str):\n        super().__init__(app)\n        self.container = container\n        self.secret = secret\n    async def dispatch(self, request: Request, call_next):\n        auth = request.headers.get(\"Authorization\", \"\")\n        if auth.startswith(\"Bearer \"):\n            token = auth.split(\" \", 1)[1]\n            claims = _verify_hs256(token, self.secret)\n            if claims is not None:\n                request.state.jwt_claims = claims\n        response = await call_next(request)\n        return response\n\n@dataclass\nclass JwtSettings:\n    secret: str = \"changeme\"\n    header: str = \"Authorization\"\n\n@component\nclass JwtConfigurer(FastApiConfigurer):\n    priority = 10\n    def __init__(self, container: PicoContainer, settings: JwtSettings):\n        self.container = container\n        self.settings = settings\n    def configure_app(self, app: FastAPI) -\u003e None:\n        app.add_middleware(JwtMiddleware, container=self.container, secret=self.settings.secret)\n```\n\n```python\nfrom pico_ioc import init\nfrom fastapi import FastAPI, Request\nfrom pico_fastapi import controller, get\n\n@controller(prefix=\"/api\")\nclass ProfileController:\n    def __init__(self):\n        pass\n    @get(\"/me\")\n    async def me(self, request: Request):\n        claims = getattr(request.state, \"jwt_claims\", None)\n        if claims is None:\n            return {\"error\": \"not authenticated\"}, 401\n        return {\"sub\": claims.get(\"sub\")}\n\ncontainer = init(\n    modules=[\n        \"pico_fastapi\",\n        \"jwt_config\",\n        \"controllers\",\n    ]\n)\n\napp = container.get(FastAPI)\n```\n\n---\n\n## ⚙️ How It Works\n\n* Controller classes are discovered and registered automatically\n* Each route executes within its own request or websocket scope\n* All dependencies are resolved via Pico-IoC\n* Cleanup and teardown occur at FastAPI lifespan\n\nNo global state and no implicit singletons.\n\n---\n\n## AI Coding Skills\n\nInstall [Claude Code](https://code.claude.com) or [OpenAI Codex](https://openai.com/index/introducing-codex/) skills for AI-assisted development with pico-fastapi:\n\n```bash\ncurl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash -s -- fastapi\n```\n\n| Command | Description |\n|---------|-------------|\n| `/add-controller` | Add FastAPI controllers with route decorators |\n| `/add-component` | Add components, factories, interceptors, settings |\n| `/add-tests` | Generate tests for pico-framework components |\n\nAll skills: `curl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash`\n\nSee [pico-skills](https://github.com/dperezcabrera/pico-skills) for details.\n\n---\n\n## 📝 License\n\nMIT\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdperezcabrera%2Fpico-fastapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdperezcabrera%2Fpico-fastapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdperezcabrera%2Fpico-fastapi/lists"}