{"id":19274369,"url":"https://github.com/alex-oleshkevich/starlette-dispatch","last_synced_at":"2026-06-17T19:33:00.434Z","repository":{"id":258537430,"uuid":"851047725","full_name":"alex-oleshkevich/starlette-dispatch","owner":"alex-oleshkevich","description":"Routing extensions and dependency injection for Starlette.","archived":false,"fork":false,"pushed_at":"2025-02-09T13:39:33.000Z","size":161,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-23T20:45:55.132Z","etag":null,"topics":["asgi","dependency-injection","routing","starlette"],"latest_commit_sha":null,"homepage":"","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/alex-oleshkevich.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","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}},"created_at":"2024-09-02T10:26:34.000Z","updated_at":"2025-02-09T13:39:35.000Z","dependencies_parsed_at":"2025-01-05T14:34:10.706Z","dependency_job_id":"8aa7b11b-77f9-4496-90e9-6bd89be03a16","html_url":"https://github.com/alex-oleshkevich/starlette-dispatch","commit_stats":null,"previous_names":["alex-oleshkevich/starlette-dispatch"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/alex-oleshkevich/starlette-dispatch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-dispatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-dispatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-dispatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-dispatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alex-oleshkevich","download_url":"https://codeload.github.com/alex-oleshkevich/starlette-dispatch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oleshkevich%2Fstarlette-dispatch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34463552,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["asgi","dependency-injection","routing","starlette"],"created_at":"2024-11-09T20:45:58.645Z","updated_at":"2026-06-17T19:33:00.414Z","avatar_url":"https://github.com/alex-oleshkevich.png","language":"Python","funding_links":[],"categories":["Extensions"],"sub_categories":["Routing"],"readme":"from starlette.routing import Routefrom examples.dependencies import Variable\n\n# Starlette Dispatch\n\nRouting extensions and dependency injection library for Starlette.\n\n![PyPI](https://img.shields.io/pypi/v/starlette_dispatch)\n![GitHub](https://img.shields.io/github/license/alex-oleshkevich/starlette_dispatch)\n![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/pypi/starlette_dispatch)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/starlette_dispatch)\n![GitHub Release Date](https://img.shields.io/github/release-date/alex-oleshkevich/starlette_dispatch)\n\n## Installation\n\nInstall `starlette_dispatch` using PIP:\n\n```bash\npip install starlette_dispatch\n```\n\n## Features\n\n- __Route groups.__ Group routes by common path prefix and common middleware.\n- __Route method decorators.__ Convenient decorators for common HTTP methods.\n- __Dependency injection.__ Route handlers can request dependencies by adding a parameter with the dependency type hint.\n- __Backward compatible__ with Starlette. You can use it with your existing Starlette application.\n- __No performance overhead.__ Dependency injection takes exact the same time as if you would write the handler\n  manually.\n- __Fully typed.__ Starlette Dispatch is fully typed and supports type hints.\n- __Async support.__ Starlette Dispatch supports async handlers and async dependencies.\n\nAnd the most important -- it does not erase route handler signature. You can compose it with any other decorators.\n\n## Quick start\n\nStarlette Dispatch does not require any changes to your existing Starlette application.\nYou can use it with your existing Starlette application.\n\nHere is a simple snippet that demonstrates dependency injection and route group usage:\n\n```python\nimport typing\n\nfrom starlette.applications import Starlette\nfrom starlette.authentication import SimpleUser\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.authentication import AuthenticationMiddleware\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\nfrom starlette.routing import Route\n\nfrom starlette_dispatch import RouteGroup, RequestResolver\n\nadmin_middleware = [\n    Middleware(AuthenticationMiddleware, backend=...)\n]\nadmin_routes = RouteGroup('/admin', middleware=admin_middleware)\n\nCurrentUser = typing.Annotated[SimpleUser, RequestResolver(lambda r: r.user)]\n\n\nasync def index_view(request: Request, user: CurrentUser) -\u003e JSONResponse:\n    \"\"\"This is your landing page.\"\"\"\n    return JSONResponse({'message': f'Hello, {user}!'})\n\n\n@admin_routes.get('/')\nasync def admin_index_view(request: Request) -\u003e JSONResponse:\n    \"\"\"This is your admin landing page.\"\"\"\n    return JSONResponse({'message': 'Hello, admin!'})\n\n\napp = Starlette(\n    routes=[\n        Route('/', index_view),  # regular Starlette route\n        *admin_routes,\n    ]\n)\n```\n\n## Route groups\n\nA route group is a way to group routes by common path prefix and common middleware.\nInstead of writing the same prefix for each route, you can define a group and add routes to it\nusing convenient decorators.\n\n\u003e Route groups support all common HTTP methods and add some extra helpers like `get_or_post`.\n\n```python\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\n\nfrom starlette_dispatch import RouteGroup\n\ngroup = RouteGroup('/group')\n\n\n@group.get('/')\ndef my_view(request: Request) -\u003e JSONResponse:\n    return JSONResponse({'message': 'Hello, world!'})\n\n\n@group.get_or_post('/')\ndef form_view(request: Request) -\u003e JSONResponse:\n    return JSONResponse({'message': 'Hello, world!'})\n```\n\n### Multiple routes on a single handler\n\nYou can call route decorators multiple times on a single handler.\nThis way you can share the same handler for multiple routes without creating a new handler.\n\n```python\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\n\nfrom starlette_dispatch import RouteGroup, FromPath\n\ngroup = RouteGroup('/group')\n\n\n@group.get('/new')\n@group.get('/edit/{id}')\ndef create_view(request: Request, id: FromPath[int | None]) -\u003e JSONResponse:\n    ...\n```\n\n### Route injections\n\nEach route handler can request a dependency by adding a parameter with the dependency type hint.\nThe dependency will be properly resolved and injected into the handler on handler call.\nSee more about dependency injection below.\n\n\u003e For each injection Starlette Dispatch creates a resolver function.\n\u003e This means, it does not add a noticeable overhead to your application and takes exact the same time as if you would\n\u003e write\n\u003e the handler manually.\n\n```python\nimport typing\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\n\nfrom starlette_dispatch import RouteGroup, VariableResolver\n\n\nclass User: ...\n\n\nuser = User()\nCurrentUser = typing.Annotated[str, VariableResolver(user)]\n\ngroup = RouteGroup('/')\n\n\n@group.get('/')\ndef index_view(request: Request, user: CurrentUser) -\u003e JSONResponse:\n    return JSONResponse({'message': f'Hello, {user}!'})\n```\n\n### Route middleware\n\nEach route can have its own middleware.\n\u003e If route group has middleware, it will be merged with route middleware. Route middleware has a higher priority.\n\n```python\nimport typing\n\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\n\nfrom starlette_dispatch import RouteGroup, VariableResolver\n\n\nclass User: ...\n\n\nuser = User()\nCurrentUser = typing.Annotated[str, VariableResolver(user)]\n\ngroup = RouteGroup('/')\n\n\n@group.get('/')\ndef my_view(request: Request, user: CurrentUser) -\u003e JSONResponse:\n    return JSONResponse({'message': f'Hello, {user}!'})\n```\n\n## Dependency injection\n\nIn a nutshell, the dependency is a type, annotated with a value or a factory function that resolves to the value.\nThe factory function is called dependency resolver.\n\n### Variable dependency\n\nVariable dependency is a resolver that returns a simple value.\n\n```python\nimport typing\n\nfrom starlette_dispatch import RouteGroup, VariableResolver\n\nValue = typing.Annotated[str, VariableResolver('hello')]\n\ngroup = RouteGroup('/')\n\n\n@group.get('/')\ndef my_view(value: Value) -\u003e None:\n    assert value == 'hello'\n```\n\n### Factory dependency\n\nFactory dependency is a resolver that creates a value on each call. The result can be cached globally or per request.\nThe factory can have dependencies and can be async.\n\nRequest cached dependencies are resolved once per request and cached for the duration of the request.\nIn order to use request cached dependencies, you need to use `DependencyScope.REQUEST` scope.\nIf you want to cache the dependency globally, you need to use `DependencyScope.SINGLETON` scope.\n\n```python\nimport typing\n\nfrom starlette_dispatch import FactoryResolver, RouteGroup, DependencyScope\n\n\ndef make_dependency():\n    return 'hello'\n\n\nasync def async_dependency():\n    return 'hello'\n\n\nValue = typing.Annotated[str, FactoryResolver(make_dependency)]\nAsyncValue = typing.Annotated[str, FactoryResolver(async_dependency)]\nCachedValue = typing.Annotated[str, FactoryResolver(make_dependency, scope=DependencyScope.SINGLETON)]\nRequestCachedValue = typing.Annotated[str, FactoryResolver(make_dependency, scope=DependencyScope.REQUEST)]\n\ngroup = RouteGroup('/')\n\n\n@group.get('/')\ndef my_view(value: Value, async_value: AsyncValue, cached_value: CachedValue) -\u003e None:\n    assert value == 'hello'\n    assert async_value == 'hello'\n    assert cached_value == 'hello'\n```\n\n#### Factory function dependencies\n\nThe factory function itself can have dependencies. They are defined in the same way as regular dependencies.\n\n```python\nimport typing\n\nfrom starlette_dispatch import FactoryResolver, RouteGroup\n\n\ndef parent_dependency():\n    return 'hello'\n\n\nParentValue = typing.Annotated[str, FactoryResolver(parent_dependency)]\n\n\ndef make_dependency(parent: ParentValue):\n    return parent + ' world'\n\n\nValue = typing.Annotated[str, FactoryResolver(make_dependency)]\n\ngroup = RouteGroup('/')\n\n\n@group.get('/')\ndef my_view(value: Value) -\u003e None:\n    assert value == 'hello world'\n```\n\n#### Predefined dependencies\n\nThere are several predefined dependencies: `starlette.requests.Request`,\n`starlette_dispatch.injections.DependencySpec`.\n\n`Request` is a Starlette request object and `DependencySpec` is a special object that contains meta information about\nthe dependency. `DependencySpec` object is very useful in complex cases.\n\n```python\nfrom starlette.requests import Request\n\nfrom starlette_dispatch import DependencySpec\n\n\ndef make_dependency(request: Request, spec: DependencySpec):\n    assert request  # Starlette request object\n    assert spec.param_name  # name of the parameter\n    assert spec.param_type  # type of the parameter\n    assert spec.optional  # is the parameter optional\n    assert spec.default  # default value of the parameter\n    assert spec.annotation  # type annotation of the parameter\n```\n\n### Request resolver\n\nIf your dependency available in the request object, instead of creating a factory function,\nyou can use a `RequestDependency` resolver. It takes a function that accepts `Request` and `DependencySpec` (optionally)\nobjects.\n\n```python\nimport typing\nfrom starlette_dispatch import RequestResolver\n\n# example dependency that resolves to a value from query parameter\nValue = typing.Annotated[str, RequestResolver(lambda request, spec,: request.query_params['value'])]\nNoSpecValue = typing.Annotated[str, RequestResolver(lambda request: request.query_params['value'])]\n```\n\n### Custom resolver\n\nYou are not limited to predefined resolvers. You can create your own resolver by subclassing `DependencyResolver`\nand implementing the `resolve` method.\n\n```python\nfrom starlette.requests import Request\n\nfrom starlette_dispatch import DependencyResolver, DependencySpec, ResolveContext\n\n\nclass MyResolver(DependencyResolver):\n    async def resolve(self, context: ResolveContext, spec: DependencySpec):\n        \"\"\"Use request and spec objects to create a value.\"\"\"\n        return 'my dependency value'\n```\n\n## Dependencies with decorators\n\nAlmost any view decorator can work with Starlette Dispatch if it accepts this signature:\n`async def view(request) -\u003e Response`.\nHowever, this has some requirements:\n\n1. it should return an async function of `async def view(request, **kwargs)`\n2. it should call `functools.wraps` on the inner view, otherwise the view will lose its dependencies\n3. it should pass `**kwargs` to the inner view, as Starlette Dispatch passes dependencies via kwargs.\n\nFull listing:\n\n```python\nimport functools\nfrom starlette.requests import Request\nfrom starlette_dispatch import RouteGroup\nfrom starlette.responses import Response, RedirectResponse\n\n\ndef login_required(fn):\n    @functools.wraps(fn)\n    async def view(request, **kwargs):\n        if not request.user.is_authenticated:\n            return RedirectResponse('/')\n        return await fn(request, **kwargs)\n\n    return view\n\n\ngroup = RouteGroup()\n\n\n@group.get('/')\n@login_required\nasync def view(request: Request) -\u003e Response: ...\n```\n\n## Contrib and support\n\n### Simple dependency definition\n\nInstead of using resolver classes, you can use these shortcuts to define dependencies.\n\n```python\nimport typing\n\nSimpleValueDependency = typing.Annotated[str, 'simple_value']\nLambdaDependency = typing.Annotated[str, lambda: 'some value']\nRequestOnlyLambdaDependency = typing.Annotated[str, lambda request: request.query_params['value']]\nRequestAndSpecLambdaDependency = typing.Annotated[str, lambda request, spec: ...]\n```\n\n### `FromPath` - inject path parameter as a dependency\n\n```python\nfrom starlette_dispatch import FromPath, RouteGroup\n\ngroup = RouteGroup('/')\n\n\n@group.get('/{value}')\ndef my_view(value: FromPath[str]) -\u003e None:\n    assert value is not None\n```\n\nIf path value does not exist in `Request.path_parameters` then it will fail with error.\nHowever, you can mark dependency as optional and then it will be `None` if path value does not exist.\n\n```python\nfrom starlette_dispatch import FromPath, RouteGroup\n\ngroup = RouteGroup('/')\n\n\ndef my_view(value: FromPath[str] | None) -\u003e None:\n    assert value is None\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oleshkevich%2Fstarlette-dispatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falex-oleshkevich%2Fstarlette-dispatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oleshkevich%2Fstarlette-dispatch/lists"}