What’s changed in 3.0?¶
This document is an overview of the changes between version 2.11.x and 3.0. For a detailed list of all changes, including changes between versions leading up to the 3.0 release, consult the 3.x Changelog.
Note
The 2.11 release line is unaffected by this change
Imports¶
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Removal of StaticFileConfig¶
The StaticFilesConfig has been removed, alongside these related parameters and
functions:
Litestar.static_files_configLitestar.url_for_static_assetRequest.url_for_static_asset
create_static_files_router() is a drop-in replacement for StaticFilesConfig,
and can simply be added to the route_handlers like any other regular handler.
Usage of url_for_static_assets should be replaced with a url_for("static", ...)
call.
Implicit Optional Default Parameters¶
In v2, if a handler was typed with an optional parameter it would be implicitly given a default value of None. For
example, if the following handler is called with no query parameter, the value None would be passed in to the
handler for the param parameter:
@get("/")
def my_handler(param: int | None) -> ...:
...
This legacy behavior originates from our history of using Pydantic v1 models to represent handler signatures. In v3, we
no longer make this implicit conversion. If you want to have a default value of None for an optional parameter, you
must explicitly set it:
@get("/")
def my_handler(param: int | None = None) -> ...:
...
Path parameters must be marked¶
Only a parameter marked with FromPath or PathParameter
is read from the path. In v2, a parameter whose name matched a path segment was read from
the path even when it was marked as a query, header or cookie parameter. In v3 it is read
from the location it is marked with:
@get("/{item_id:int}")
def my_handler(item_id: FromQuery[int]) -> ...: # read from the query string
...
A parameter named like a path segment that declares no location, such as the deprecated
item_id: int, raises ImproperlyConfiguredException when the
application is created. Declare it as item_id: FromPath[int].
OpenAPI Controller Replaced by Plugins¶
In version 3.0, the OpenAPI controller pattern, deprecated in v2.8, has been removed in favor of a more flexible plugin system.
Elimination of OpenAPIController Subclassing¶
Previously, users configured elements such as the root path and styling by subclassing OpenAPIController and setting it
on the OpenAPIConfig.openapi_controller attribute. As of version 3.0, this pattern has been removed. Instead, users
are required to transition to using UI plugins for configuration.
Migration Steps:
Remove any implementations subclassing
OpenAPIController.Use the
OpenAPIConfig.render_pluginsattribute to configure the OpenAPI UI made available to your users. If no plugin is supplied, we automatically add theScalarRenderPluginfor the default configuration.Use the
OpenAPIConfig.openapi_routerattribute for additional configuration.
See the OpenAPI UI Plugins documentation for more information on how to configure OpenAPI plugins.
Changes to Endpoint Configuration¶
The OpenAPIConfig.enabled_endpoints attribute is no longer available in version 3.0.0. This attribute previously
enabled a set of endpoints that would serve different OpenAPI UIs. In the new version, only the openapi.json
endpoint is enabled by default, alongside the Scalar UI plugin as the default.
To adapt to this change, you should explicitly configure any additional endpoints you need by properly setting up the
necessary plugins within the OpenAPIConfig.render_plugins parameter.
Modification to root_schema_site Handling¶
The root_schema_site attribute, which enabled serving a particular UI at the OpenAPI root path, has been removed in
version 3.0. The new approach automatically assigns the first OpenAPIRenderPlugin listed in the
OpenAPIConfig.render_plugins list to serve at the /schema endpoint, unless a plugin has been defined with
the root path (/), in which case that plugin will be used.
For those previously using the root_schema_site attribute, the migration involves ensuring that the UI intended to
be served at the /schema endpoint is the first plugin listed in the OpenAPIConfig.render_plugins.
Removal of the RapiDoc OpenAPI UI plugin¶
The RapidocRenderPlugin has been removed due to lack of upstream maintenance.
If you were using RapidocRenderPlugin, migrate to one of the other bundled UI plugins
via OpenAPIConfig.render_plugins, e.g. ScalarRenderPlugin (the default),
SwaggerRenderPlugin, RedocRenderPlugin or StoplightRenderPlugin.
Deprecated app parameter for Response.to_asgi_response has been removed¶
The parameter app for to_asgi_response() has been removed.
If you need access to the app instance inside a custom to_asgi_response method,
replace the usages of app with request.app.
Deprecated scope state utilities removed¶
Litestar has previously made available utilities for storing and retrieving data in the ASGI scope state. These utilities have been removed in version 3.0.0. If you need to store data in the ASGI scope state, you should use do so using a namespace that is unique to your application and unlikely to conflict with other applications.
The following utilities have been removed:
get_litestar_scope_stateset_litestar_scope_statedelete_litestar_scope_state
Deprecated utility function is_sync_or_async_generator removed¶
The utility function is_sync_or_async_generator has been removed as it is no longer used internally.
If you were relying on this utility, you can define it yourself as follows:
from inspect import isasyncgenfunction, isgeneratorfunction
def is_sync_or_async_generator(obj: Any) -> bool:
return isgeneratorfunction(obj) or isasyncgenfunction(obj)
Deprecated DTOField declaration via Pydantic’s Field.extra removed¶
Declaring a DTOField through Pydantic’s Field.extra, that is
Field(**dto_field("read-only")), is no longer supported. The mark was already being
ignored in 2.x and only produced a deprecation warning.
Remove the declaration: it leaves an internal key in the field’s json_schema_extra,
which can make OpenAPI schema generation fail.
Before:
import pydantic
from litestar.dto import dto_field
class User(pydantic.BaseModel):
id: int = pydantic.Field(**dto_field("read-only"))
After:
from typing import Annotated
import pydantic
from litestar.dto import DTOField
class User(pydantic.BaseModel):
id: Annotated[int, DTOField(mark="read-only")]
Removal of semantic HTTP route handler classes¶
The semantic HTTPRouteHandler classes have been removed in favour of functional
decorators. route, get, post, patch, put, head and delete
are now all decorator functions returning HTTPRouteHandler
instances.
As a result, customizing the decorators directly is not possible anymore. Instead, to
use a route handler decorator with a custom route handler class, the handler_class
parameter to the decorator function can be used:
Before:
class my_get_handler(get):
... # custom handler
@my_get_handler()
async def handler() -> Any:
...
After:
class MyHTTPRouteHandler(HTTPRouteHandler):
... # custom handler
@get(handler_class=MyHTTPRouteHandler)
async def handler() -> Any:
...
Deprecated app parameter for Response.to_asgi_response has been removed¶
The parameter app for to_asgi_response() has been removed.
If you need access to the app instance inside a custom to_asgi_response method,
replace the usages of app with request.app.
Removal of deprecated litestar.middleware.exceptions module and ExceptionHandlerMiddleware¶
The deprecated litestar.middleware.exceptions module and the
ExceptionHandlerMiddleware have been removed. Since ExceptionHandlerMiddleware
has been applied automatically behind the scenes if necessary, no action is required.
Update MessagePack media type to application/vnd.msgpack¶
Change the default media type of MessagePack from application/x-msgpack to the
newly introduced official application/vnd.msgpack.
https://www.iana.org/assignments/media-types/application/vnd.msgpack
Deprecated resolve_ methods on route handlers¶
All resolve_ methods on the route handlers
(e.g. HTTPRouteHandler.resolve_response_headers) have been
deprecated and will be removed in 4.0. The attributes can now safely be accessed
directly (e.g. HTTPRouteHandlers.response_headers).
Removal of CLIPluginProtocol¶
The Protocol CLIPluginProtocol has been removed in favour
of the abstract CLIPluginProtocol. The functionality and interface remain the same,
the only difference being that plugins that wish to provide this functionality are now
required to inherit from CLIPlugin.
Removal of OpenAPISchemaPluginProtocol¶
The Protocol OpenAPISchemaPluginProtocol has been removed in favour
of the abstract OpenAPISchemaPlugin. The functionality and
interface remain the same, the only difference being that plugins that wish to provide
this functionality are now required to inherit from
OpenAPISchemaPlugin.
Dropped support for starlette middleware protocol¶
The starlette middleware protocol is no longer supported.
Only the “factory” pattern will now be supported, i.e. a callable that receives an ASGI callable as its only argument and returns another ASGI callable:
def middleware(app: ASGIApp) -> ASGIApp:
...
See also
Built-in middleware migrated to ASGIMiddleware¶
Litestar’s built-in middleware are being moved from the legacy AbstractMiddleware and
MiddlewareProtocol bases onto ASGIMiddleware.
CORSMiddleware, ResponseCacheMiddleware and CSRFMiddleware have made this
move. ResponseCacheMiddleware and CSRFMiddleware have also been moved into
litestar.middleware._internal, removing them from the public API; for CSRF this
includes the litestar.middleware.csrf module and its generate_csrf_token /
generate_csrf_hash helpers.
These classes are constructed by Litestar itself from their configuration objects (for
CSRF, via a from_config classmethod on the middleware), so applications that only
configure them - for CORS, via
CORSConfig, for response caching, via
ResponseCacheConfig and the handler-level
cache argument, and for CSRF, via CSRFConfig - are
unaffected, with one exception: exclude
patterns are now matched against the handler’s path template
(e.g. /user/{user_id:int}) at startup, instead of the request path
(e.g. /user/1) at runtime. Patterns targeting literal paths keep working; patterns
written to match expanded path parameters must be rewritten against the template.
Code that composed one of them directly into an ASGI stack must drop the app
argument, pass the settings as keyword arguments rather than a configuration object, and
apply the instance to the next ASGI app:
# before
middleware = CORSMiddleware(app=next_app, config=cors_config)
# after
middleware = CORSMiddleware(
allow_origins=cors_config.allow_origins,
allow_methods=cors_config.allow_methods,
allow_headers=cors_config.allow_headers,
allow_credentials=cors_config.allow_credentials,
allow_origin_regex=cors_config.allow_origin_regex,
expose_headers=cors_config.expose_headers,
max_age=cors_config.max_age,
)
asgi_app = middleware(next_app)
The settings are copied on construction, so mutating CORSConfig after the application
has been created no longer affects the middleware.
Because ASGIMiddleware.__call__ returns a closure rather than the middleware
instance, a middleware stack can no longer be introspected by walking the .app
attribute of each layer.
Subclasses must also rename __call__ to handle, which receives the next ASGI app
as an additional next_app argument in place of self.app.
Removal of SerializationPluginProtocol¶
The Protocol SerializationPluginProtocol has been removed in favour
of the abstract SerializationPlugin. The functionality and
interface remain the same, the only difference being that plugins that wish to provide
this functionality are now required to inherit from
SerializationPlugin.
Removal of body in streaming responses¶
The unsupported body parameter of ASGIStreamingResponse
and ASGIFileResponse has been removed.
This does not change any behaviour, as this parameter was previously ignored.
polyfactory package removed from default dependencies¶
The polyfactory library has been moved from the
default dependencies to the litestar[polyfactory] package extra. It
is also included in litestar[full].
pyyaml package removed from default dependencies¶
The PyYAML library, used to render the
OpenAPI schema as YAML has been moved from the default dependencies to the
litestar[yaml] package extra.
litestar-htmx package removed from default dependencies¶
The litestar-htmx package powering
the HTMX plugin has been moved to the litestar[htmx] extra.
click, rich and rich-click packages removed from default dependencies¶
The click, rich
and rich-click libraries powering the
CLI have been moved from the default dependencies to the
litestar[cli] package extra. They are also included in litestar[standard] and
litestar[full].
Installing litestar on its own therefore no longer provides the litestar command.
Invoking it without the extra raises a
MissingDependencyException
naming the extra to install:
pip install 'litestar[cli]'
httpx package removed from default dependencies¶
The httpx library, on which the
test clients are based, has been moved from the default
dependencies to the litestar[testing] package extra. It is also included in
litestar[full].
Importing anything from litestar.testing without the extra installed raises a
MissingDependencyException
naming the extra to install:
pip install 'litestar[testing]'
rich-click>=1.9 is now required by the CLI¶
The litestar[cli] extra now requires rich-click>=1.9, which is needed for the
themed CLI output. Previously rich-click was capped at <1.9,
which silently disabled the theme, as the underlying option only exists from 1.9 onwards.
This only affects applications that additionally pin rich-click<1.9 themselves.
Improved file system handling / fsspec integration¶
A more coherent file system integration was added, with improved support for fsspec. This new implementation is more stable, performant and consistent, and includes new features such as random access to all supported file systems as well as streaming (optionally with offsets, even if the underlying file system does not support it natively).
See also
Removal of resolve_symlinks parameter to create_static_files_router¶
resolve_symlinks parameter of
create_static_files_router() has been removed in favour of
the new
allow_symlinks_outside_directory
parameter.
Attention
This has been intentionally made a breaking change because the new parameter has
slightly different behaviour and defaults to False instead of True.
Middleware configuration constraints¶
ASGIMiddlewares can now express constraints for how
they are applied in the middleware stack, which are then validated on application
startup.
See also
copy_scope defaults to True for ASGI handlers¶
Every asgi() handler, mounted or not, now receives a copy of the ASGI
scope, so the wrapped application can no longer add, replace or remove top-level scope
keys seen by the outer application. In 2.x the default was None, which passed the
original scope through, warned at registration and warned again when the wrapped
application mutated the scope. Both warnings are gone.
The copy is shallow, so nested values such as scope["state"] are still shared with the
outer scope.
Set copy_scope=False where the wrapped application is meant to communicate with the
outer application through the scope:
@asgi("/some/sub-path", is_mount=True, copy_scope=False)
async def my_asgi_app(scope: Scope, receive: Receive, send: Send) -> None: ...
You need the opt-out if the wrapped application writes a top-level scope key the outer
application reads, such as scope["user"], or calls
set_session() or
clear_session(), both of which rebind
scope["session"]. A mounted Starlette or FastAPI application also sets
scope["endpoint"] and scope["route"], which outer Sentry or OpenTelemetry layers
read after the response to name the transaction. All of these now fail silently.
copy_scope is set per handler; there is no router- or application-level equivalent, so
an application with several mounts opts out on each handler. A handler_class subclass
that pinned copy_scope by passing it to super().__init__() must now accept it as a
parameter and forward it, because asgi() always passes it.
See also