https://github.com/adriangb/asapi
An opinionated set of utilities / patterns for FastAPI
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (9.2%) to scientific vocabulary
Repository
An opinionated set of utilities / patterns for FastAPI
Basic Info
- Host: GitHub
- Owner: adriangb
- Language: Python
- Default Branch: main
- Size: 135 KB
Statistics
- Stars: 25
- Watchers: 1
- Forks: 2
- Open Issues: 3
- Releases: 1
Metadata Files
README.md
asapi
A thin opinionated wrapper around FastAPI. Because it's wrapping FastAPI you can work it into your existing projects.
Explicit composition root
FastAPI uses callbacks inside of Depends to do it's dependency injection.
This forces you to end up using multiple layers of Depends to compose your application.
The creation of these Depends resources often ends up distributed across modules so it's hard to know where something is initialized.
FastAPI also has no application-level dependencies, so you end up having to use globals to share resources across requests.
asapi solves this by having an explicit composition root where you can define all your dependencies in one place.
Endpoints then use Injected[DependencyType] to get access to the dependencies they need.
Example
```python from future import annotations
import anyio from fastapi import FastAPI from psycopg_pool import AsyncConnectionPool from asapi import FromPath, Injected, serve, bind
app = FastAPI()
@app.get("/hello/{name}") async def hello( name: FromPath[str], pool: Injected[AsyncConnectionPool], ) -> str: async with pool.connection() as conn: async with conn.cursor() as cur: await cur.execute("SELECT '¡Hola ' || %(name)s || '!'", {"name": name}) res = await cur.fetchone() assert res is not None return res[0] ```
TODO: in the future I'd like to provide a wrapper around APIRouter and FastAPI that also forces you to mark every argument to an endpoint as Injected, Query, Path, Body, which makes it explicit where arguments are coming from with minimal boilerplate.
Run in your event loop
FastAPI recommends using Uvicorn to run your application (note: if you're using Gunicorn you probably don't need to unless you're deploying on a a 'bare meta' server with multiple cores like a large EC2 instance).
But using uvicorn app:app from the command line has several issues:
- It takes control of the event loop and startup out of your hands. You have to rely on Uvicorn to configure the event loop, configure logging, etc.
- You'll have to use ASGI lifespans to initialize your resources, or the globals trick mentioned above.
- You can't run anything else in the event loop (e.g. a background worker).
asapi solves this by providing a serve function that you can use to run your application in your own event loop.
```python from future import annotations
import anyio from asapi import serve from fastapi import FastAPI
app = FastAPI()
@app.get("/") async def root() -> dict[str, str]: return {"message": "Hello World"}
async def main(): await serve(app, 8000)
if name == "main": anyio.run(main) ```
Now you have full control of the event loop and can make database connections, run background tasks, etc.
Combined with the explicit composition root, you can initialize all your resources in one place, bind them to an application instance that is specific to this event loop and inject them into the endpoints that need them, all without global state or multiple layers of Depends.
```python from future import annotations
import logging from typing import Any import anyio from fastapi import FastAPI, APIRouter from psycopg_pool import AsyncConnectionPool from asapi import FromPath, Injected, serve, bind
router = APIRouter()
@router.get("/hello/{name}") async def hello(name: FromPath[str], pool: Injected[AsyncConnectionPool]) -> str: async with pool.connection() as conn: async with conn.cursor() as cur: await cur.execute("SELECT '¡Hola ' || %(name)s || '!'", {"name": name}) res = await cur.fetchone() assert res is not None return res[0]
def createapp(pool: AsyncConnectionPool[Any]) -> FastAPI: app = FastAPI() bind(app, AsyncConnectionPool, pool) app.includerouter(router) return app
async def main() -> None: logging.basicConfig(level=logging.INFO)
async with AsyncConnectionPool(
"postgres://postgres:postgres@localhost:54320/postgres"
) as pool:
app = create_app(pool)
await serve(app, 9000)
if name == "main": anyio.run(main) ```
Owner
- Name: Adrian Garcia Badaracco
- Login: adriangb
- Kind: user
- Location: Chicago, IL
- Repositories: 172
- Profile: https://github.com/adriangb
GitHub Events
Total
- Create event: 2
- Issues event: 4
- Release event: 1
- Watch event: 8
- Delete event: 1
- Issue comment event: 18
- Push event: 35
- Pull request review event: 1
- Pull request event: 3
- Fork event: 2
Last Year
- Create event: 2
- Issues event: 4
- Release event: 1
- Watch event: 8
- Delete event: 1
- Issue comment event: 18
- Push event: 35
- Pull request review event: 1
- Pull request event: 3
- Fork event: 2
Committers
Last synced: over 1 year ago
Top Committers
| Name | Commits | |
|---|---|---|
| Adrian Garcia Badaracco | 1****b | 23 |
Issues and Pull Requests
Last synced: 11 months ago
All Time
- Total issues: 4
- Total pull requests: 7
- Average time to close issues: about 5 hours
- Average time to close pull requests: 2 months
- Total issue authors: 2
- Total pull request authors: 3
- Average comments per issue: 1.0
- Average comments per pull request: 0.14
- Merged pull requests: 4
- Bot issues: 0
- Bot pull requests: 2
Past Year
- Issues: 3
- Pull requests: 3
- Average time to close issues: about 2 hours
- Average time to close pull requests: 4 minutes
- Issue authors: 1
- Pull request authors: 2
- Average comments per issue: 0.33
- Average comments per pull request: 0.0
- Merged pull requests: 2
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- sanmai-NL (3)
- dineshbvadhia (2)
Pull Request Authors
- adriangb (4)
- dependabot[bot] (2)
- Kludex (2)
Top Labels
Issue Labels
Pull Request Labels
Dependencies
- coverage ^7.5.3 develop
- psycopg ^3.1.19 develop
- pyright ^1.1.365 develop
- pytest ^8.2.0 develop
- ruff ^0.4.6 develop
- anyio ^4.3.0
- fastapi ^0.111.0
- python ^3.8
- typing-extensions ^4.12.0
- uvicorn ^0.30.0
- actions/checkout v4 composite
- actions/setup-python v5 composite
- actions/setup-python v4 composite
- ncipollo/release-action v1 composite
- re-actors/alls-green release/v1 composite
- snok/install-poetry v1 composite
- annotated-types 0.7.0
- anyio 4.8.0
- asapi 0.7.0
- attrs 24.3.0
- certifi 2024.12.14
- cffi 1.17.1
- cfgv 3.4.0
- click 8.1.8
- colorama 0.4.6
- coverage 7.6.10
- distlib 0.3.9
- exceptiongroup 1.2.2
- fastapi 0.115.6
- filelock 3.17.0
- h11 0.14.0
- httpcore 1.0.7
- httpx 0.28.1
- identify 2.6.6
- idna 3.10
- iniconfig 2.0.0
- nodeenv 1.9.1
- outcome 1.3.0.post0
- packaging 24.2
- platformdirs 4.3.6
- pluggy 1.5.0
- pre-commit 3.8.0
- psycopg 3.2.4
- psycopg-binary 3.2.4
- psycopg-pool 3.2.4
- pycparser 2.22
- pydantic 2.10.5
- pydantic-core 2.27.2
- pyright 1.1.392.post0
- pytest 8.3.4
- pyyaml 6.0.2
- ruff 0.4.10
- sniffio 1.3.1
- sortedcontainers 2.4.0
- starlette 0.41.3
- tomli 2.2.1
- trio 0.25.1
- typing-extensions 4.12.2
- tzdata 2025.1
- uvicorn 0.30.6
- virtualenv 20.29.1