NestJS-inspired application architecture on top of Starlette
Project description
bustan
bustan is an architecture-first Python web framework for teams that like Starlette's runtime but want stronger application structure.
It brings NestJS-style modules, controllers, providers, constructor injection, lifecycle hooks, and a request pipeline to Python, while keeping direct access to Starlette when you need an escape hatch.
Why bustan
- Use modules as real composition boundaries instead of ad hoc import graphs.
- Keep controllers thin and move business logic into DI-managed providers.
- Apply guards, pipes, interceptors, and exception filters in a predictable order.
- Build on top of Starlette instead of replacing the ASGI layer with a closed runtime.
- Test applications with focused module builders and provider overrides.
Status
bustanis alpha and still pre-0.1.0.- The first public compatibility target is
0.1.0alpha. - Package metadata currently targets Python
>=3.13. - The Python floor is intentionally narrow while the public surface, packaging, and release process are still settling.
- Python
3.13remains the floor because the project is still tightening its first public contract and release automation around one supported runtime before widening support. - Compatibility is currently promised only for
bustan,bustan.errors, andbustan.testing. - Internal modules such as
bustan.container,bustan.routing,bustan.params, andbustan.metadataare still implementation details. - Alpha stability means behavior may still change between pre-
0.1.0releases, but the project is already treatingbustan,bustan.errors, andbustan.testingas the intended long-term public surface. - No benchmark suite or benchmark claims are published yet.
Installation
Use from source today
This repository is ready to use directly in a local development environment:
uv sync --group dev
That installs the framework, test dependencies, linting, typing, and the local CLI entry point.
Use the CLI from a source checkout
uv run bustan new my-app
create is available as an alias:
uv run bustan create my-app
Use as a published package
Once the PyPI distribution name is confirmed and published, the intended install path is:
uv add bustan
# or
pip install bustan
Once published, the CLI entry point will also be usable through uvx:
uvx bustan create my-app
The distribution name bustan still needs to be confirmed at publish time. The current repository is prepared for that name, but the final PyPI availability check should happen immediately before the first release.
Five-Minute Quickstart
Create an application module, one provider, and one controller:
from bustan import controller, create_app, get, injectable, module
@Injectable
class GreetingService:
def greet(self) -> dict[str, str]:
return {"message": "hello from bustan"}
@Controller("/hello")
class GreetingController:
def __init__(self, greeting_service: GreetingService) -> None:
self.greeting_service = greeting_service
@Get("/")
def read_greeting(self) -> dict[str, str]:
return self.greeting_service.greet()
@Module(
controllers=[GreetingController],
providers=[GreetingService],
exports=[GreetingService],
)
class AppModule:
pass
app = create_app(AppModule)
Run it locally:
uv run uvicorn app:app --reload
Call the route:
curl http://127.0.0.1:8000/hello
Expected response:
{"message":"hello from bustan"}
What You Get Today
The current implementation already includes:
- module discovery and validation
- export-based provider visibility across modules
- constructor injection for providers and controllers
- controller route compilation into Starlette
- response coercion for
Response,dict,list, dataclass instances, andNone - request binding for
Request, path params, query params, and JSON body input - request-scoped providers with per-request caching
- guards, pipes, interceptors, and exception filters
- module and application lifecycle hooks wired through Starlette lifespan
- test helpers for temporary modules, test apps, and provider overrides
- an initial CLI scaffold for new applications
Supported Public API
The first compatibility boundary is intentionally small.
Stable import paths:
bustanbustan.errorsbustan.testing
Example supported imports:
from bustan import __version__, controller, create_app, get, injectable, module
from bustan.errors import ProviderResolutionError
from bustan.testing import create_test_app, override_provider
The generated API reference for the stable surface lives in docs/API_REFERENCE.md.
Guides
- docs/README.md
- docs/FIRST_APP.md
- docs/ROUTING.md
- docs/REQUEST_PIPELINE.md
- docs/REQUEST_SCOPED_PROVIDERS.md
- docs/LIFECYCLE.md
- docs/STABILITY.md
- docs/TROUBLESHOOTING.md
- docs/COMPARISONS.md
- docs/ESCAPE_HATCHES.md
- docs/VERSIONING.md
Open Source Project Docs
- docs/README.md
- CONTRIBUTING.md
- CODE_OF_CONDUCT.md
- SECURITY.md
- GOVERNANCE.md
- CHANGELOG.md
- docs/RELEASE_CHECKLIST.md
Examples
The repository includes focused examples beyond the minimal quickstart:
examples/blog_api/app.py: a small reference-style blog API with request context and module exportsexamples/multi_module_app/app.py: feature modules with exported providersexamples/graph_inspection/app.py: print the discovered module graphexamples/request_scope_pipeline_app/app.py: request-scoped providers shared across guards, interceptors, and controllersexamples/testing_overrides/app.py: test-time provider overrides withbustan.testing
Run them with:
uv run python examples/blog_api/app.py
uv run python examples/graph_inspection/app.py
uv run python examples/request_scope_pipeline_app/app.py
uv run python examples/testing_overrides/app.py
Testing Utilities
bustan.testing is the intended entry point for test-time application assembly.
Use create_test_app() to start an app with one or more providers replaced:
from bustan.testing import create_test_app
application = create_test_app(
AppModule,
provider_overrides={GreetingService: FakeGreetingService()},
)
Use override_provider() when you want a scoped override that is restored automatically:
from bustan.testing import override_provider
with override_provider(application, GreetingService, FakeGreetingService()):
...
Use create_test_module() when you want a temporary module class for an isolated test instead of declaring one manually.
Support
Use GitHub Issues for questions, bug reports, feature requests, and adoption feedback:
Do not use public issues for sensitive security reports. Follow the private disclosure guidance in SECURITY.md.
Roadmap
Near-term priorities for the first public adoption push:
- publish and verify the first PyPI release end to end
- widen runtime support beyond Python
3.13once release automation is routine - collect external adopter feedback before calling any release stable
- publish a fuller reference app or companion tutorial repository
- move to a dedicated docs site if the docs set outgrow README-driven discovery
Development
Install hooks once after cloning if you want local pre-commit and pre-push checks:
uv run lefthook install
For full contributor expectations, see CONTRIBUTING.md.
Run the main checks with:
uv run python scripts/generate_api_reference.py --check
uv run python scripts/check_markdown_links.py
uv run ruff check .
uv run ty check src tests examples scripts
uv run pytest
uv run pytest --cov=bustan --cov-report=term-missing --cov-report=xml
Project Direction
bustan is trying to be opinionated about application structure, not to hide Starlette or compete on benchmark claims.
If you want a small ASGI core with explicit module boundaries, DI-managed services, and a predictable request pipeline, that is the target use case.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bustan-1.0.0.tar.gz.
File metadata
- Download URL: bustan-1.0.0.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
139115fe44e67994dc2eb5829f6a80ab14652de7223bc5fb9ca8424308dc8144
|
|
| MD5 |
4d911b7e98aaedfa9a4794859f87c293
|
|
| BLAKE2b-256 |
d5c7e1be2397dd380c331f6637f8e634dc1fa8f841d87e838cf4b9afc70638a4
|
Provenance
The following attestation bundles were made for bustan-1.0.0.tar.gz:
Publisher:
release-please.yml on bustanhq/bustan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bustan-1.0.0.tar.gz -
Subject digest:
139115fe44e67994dc2eb5829f6a80ab14652de7223bc5fb9ca8424308dc8144 - Sigstore transparency entry: 1238893762
- Sigstore integration time:
-
Permalink:
bustanhq/bustan@e1965000f6d124ed2d2fe70927d7df251bd590d9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bustanhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@e1965000f6d124ed2d2fe70927d7df251bd590d9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bustan-1.0.0-py3-none-any.whl.
File metadata
- Download URL: bustan-1.0.0-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a274eed8ebb088319e66389c2916d9d4c90621cdf473fd98388ff0a45c3c11d9
|
|
| MD5 |
8ae89af9fc6bc052a7c37a01f37ab855
|
|
| BLAKE2b-256 |
f236cb2701c65e1341ba2cbaf319db42a122402362f11e19134b7a2a4e81d537
|
Provenance
The following attestation bundles were made for bustan-1.0.0-py3-none-any.whl:
Publisher:
release-please.yml on bustanhq/bustan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bustan-1.0.0-py3-none-any.whl -
Subject digest:
a274eed8ebb088319e66389c2916d9d4c90621cdf473fd98388ff0a45c3c11d9 - Sigstore transparency entry: 1238893841
- Sigstore integration time:
-
Permalink:
bustanhq/bustan@e1965000f6d124ed2d2fe70927d7df251bd590d9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/bustanhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@e1965000f6d124ed2d2fe70927d7df251bd590d9 -
Trigger Event:
push
-
Statement type: