diff --git a/.buildinfo b/.buildinfo
index b2e6262..a121562 100644
--- a/.buildinfo
+++ b/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 48bd624a1e2c91349b6c4e0ee21cd6e5
+config: e5a35904bc7bb78a12349affaec49f9c
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/.doctrees/api.doctree b/.doctrees/api.doctree
index 4898908..cd61576 100644
Binary files a/.doctrees/api.doctree and b/.doctrees/api.doctree differ
diff --git a/.doctrees/changes.doctree b/.doctrees/changes.doctree
index 7ef336f..afee43e 100644
Binary files a/.doctrees/changes.doctree and b/.doctrees/changes.doctree differ
diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle
index 6d7a69d..492732d 100644
Binary files a/.doctrees/environment.pickle and b/.doctrees/environment.pickle differ
diff --git a/.doctrees/guide-config.doctree b/.doctrees/guide-config.doctree
new file mode 100644
index 0000000..fab8f65
Binary files /dev/null and b/.doctrees/guide-config.doctree differ
diff --git a/.doctrees/index.doctree b/.doctrees/index.doctree
index 90c799e..aaba530 100644
Binary files a/.doctrees/index.doctree and b/.doctrees/index.doctree differ
diff --git a/.doctrees/quickstart.doctree b/.doctrees/quickstart.doctree
index 1e297e3..586a5ae 100644
Binary files a/.doctrees/quickstart.doctree and b/.doctrees/quickstart.doctree differ
diff --git a/.doctrees/tour.doctree b/.doctrees/tour.doctree
index 05defef..8e731dc 100644
Binary files a/.doctrees/tour.doctree and b/.doctrees/tour.doctree differ
diff --git a/.doctrees/tutorial-auth.doctree b/.doctrees/tutorial-auth.doctree
new file mode 100644
index 0000000..3ee57a1
Binary files /dev/null and b/.doctrees/tutorial-auth.doctree differ
diff --git a/.doctrees/tutorial-middleware.doctree b/.doctrees/tutorial-middleware.doctree
new file mode 100644
index 0000000..7d702be
Binary files /dev/null and b/.doctrees/tutorial-middleware.doctree differ
diff --git a/.doctrees/tutorial-websockets.doctree b/.doctrees/tutorial-websockets.doctree
new file mode 100644
index 0000000..27a0a2c
Binary files /dev/null and b/.doctrees/tutorial-websockets.doctree differ
diff --git a/_modules/index.html b/_modules/index.html
index 394b367..ef1a4f1 100644
--- a/_modules/index.html
+++ b/_modules/index.html
@@ -4,13 +4,13 @@
diff --git a/_modules/responder/api.html b/_modules/responder/api.html
index f175740..618dbe8 100644
--- a/_modules/responder/api.html
+++ b/_modules/responder/api.html
@@ -4,13 +4,13 @@
- responder.api — responder 3.2.0 documentation
+ responder.api — responder 3.4.1 documentation
-
+
@@ -103,6 +103,31 @@
lifespan=None,request_id=False,):
+"""Create a new Responder API instance.
+
+ :param debug: If ``True``, enable debug mode with verbose error pages.
+ :param title: The title of the API, used in OpenAPI documentation.
+ :param version: The version string for the API (e.g. ``"1.0"``).
+ :param description: A longer description of the API for OpenAPI docs.
+ :param terms_of_service: URL to the API's terms of service.
+ :param contact: Contact information dict for the API (``name``, ``url``, ``email``).
+ :param license: License information dict (``name``, ``url``).
+ :param openapi: The OpenAPI version string (e.g. ``"3.0.2"``). Enables OpenAPI schema generation.
+ :param openapi_route: The URL path for the OpenAPI schema (default ``"/schema.yml"``).
+ :param static_dir: Directory for static files. Set to ``None`` to disable. Created automatically if missing.
+ :param static_route: URL prefix for serving static files (default ``"/static"``).
+ :param templates_dir: Directory for Jinja2 templates (default ``"templates"``).
+ :param auto_escape: If ``True``, auto-escape HTML/XML in templates.
+ :param secret_key: Secret key for signing cookie-based sessions. **Always set this in production.**
+ :param enable_hsts: If ``True``, redirect all HTTP requests to HTTPS.
+ :param docs_route: URL path for interactive API docs (e.g. ``"/docs"``). Enables OpenAPI if not already set.
+ :param cors: If ``True``, enable CORS middleware.
+ :param cors_params: Dict of CORS configuration (``allow_origins``, ``allow_methods``, etc.).
+ :param allowed_hosts: List of allowed hostnames (e.g. ``["example.com"]``). Defaults to ``["*"]``.
+ :param openapi_theme: Documentation UI theme: ``"swagger_ui"``, ``"redoc"``, ``"rapidoc"``, or ``"elements"``.
+ :param lifespan: An async context manager for startup/shutdown logic.
+ :param request_id: If ``True``, add ``X-Request-ID`` headers to all responses.
+ """# noqa: E501self.background=BackgroundQueue()self.secret_key=secret_key
@@ -192,17 +217,38 @@
@propertydefstatic_app(self):
+"""The Starlette ``StaticFiles`` application for serving static assets."""ifnothasattr(self,"_static_app"):assertself.static_dirisnotNoneself._static_app=StaticFiles(directory=self.static_dir)returnself._static_app
+
+[docs]defbefore_request(self,websocket=False):
+"""Register a function to run before every request.
+
+ If the hook sets ``resp.status_code``, the route handler is skipped
+ and the response is sent immediately (short-circuiting).
+
+ :param websocket: If ``True``, register as a WebSocket before-request hook instead of HTTP.
+
+ Usage::
+
+ @api.before_request()
+ def check_auth(req, resp):
+ if "Authorization" not in req.headers:
+ resp.status_code = 401
+ resp.media = {"error": "unauthorized"}
+
+ """# noqa: E501
+
defdecorator(f):self.router.before_request(f,websocket=websocket)returnf
- returndecorator
+ returndecorator
+[docs]defadd_middleware(self,middleware_cls,**middleware_config):
- self.app=middleware_cls(self.app,**middleware_config)
+"""Add ASGI middleware to the application.
+
+ Middleware wraps the entire application and can inspect or modify
+ every request and response. Middleware is applied in reverse order —
+ the last middleware added runs first.
+
+ :param middleware_cls: A Starlette-compatible middleware class.
+ :param middleware_config: Keyword arguments passed to the middleware constructor.
+
+ Usage::
+
+ from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
+ api.add_middleware(HTTPSRedirectMiddleware)
+
+ """
+ self.app=middleware_cls(self.app,**middleware_config)
+[docs]defrun(self,**kwargs):
+"""Run the application. Shorthand for :meth:`serve` that inherits the ``debug`` setting.
+
+ :param kwargs: Keyword arguments passed through to :meth:`serve`.
+ """if"debug"notinkwargs:kwargs.update({"debug":self.debug})
- self.serve(**kwargs)
+ self.serve(**kwargs)
+[docs]classRouteGroup:"""A group of routes with a shared URL prefix."""
@@ -633,7 +706,8 @@
returnself.api.route(full_route,**options)defbefore_request(self,**kwargs):
- returnself.api.before_request(**kwargs)
+ returnself.api.before_request(**kwargs)
+[docs]
+classBackgroundQueue:
+"""A queue for running tasks in background threads.
+
+ Uses a ``ThreadPoolExecutor`` sized to the number of CPUs. Access it
+ via ``api.background``.
+
+ Usage::
+
+ # As a decorator — fire and forget
+ @api.background.task
+ def send_email(to, subject):
+ ...
+
+ send_email("user@example.com", "Hello")
+
+ # Direct submission
+ future = api.background.run(send_email, "user@example.com", "Hello")
+
+ # As a callable (supports async functions)
+ await api.background(send_email, "user@example.com", "Hello")
+
+ """
+
+ def__init__(self,n=None):
+"""Create a new background queue.
+
+ :param n: Number of worker threads. Defaults to CPU count.
+ """
+ ifnisNone:
+ n=multiprocessing.cpu_count()
+
+ self.n=n
+ self.pool=concurrent.futures.ThreadPoolExecutor(max_workers=n)
+ self.results=[]
+
+
+[docs]
+ defrun(self,f,*args,**kwargs):
+"""Submit a function to run in a background thread.
+
+ :param f: The function to run.
+ :returns: A ``concurrent.futures.Future`` for the result.
+ """
+ f=self.pool.submit(f,*args,**kwargs)
+ self.results.append(f)
+ returnf
+
+
+
+[docs]
+ deftask(self,f):
+"""Decorator that wraps a function to run in the background thread pool.
+
+ The decorated function returns a ``Future`` instead of blocking.
+ Exceptions are printed to stderr via traceback.
+
+ :param f: The function to wrap.
+ """
+
+ defon_future_done(fs):
+ try:
+ fs.result()
+ exceptException:
+ traceback.print_exc()
+
+ defdo_task(*args,**kwargs):
+ result=self.run(f,*args,**kwargs)
+ result.add_done_callback(on_future_done)
+ returnresult
+
+ returndo_task
+[docs]
+ definstall(self,api):
+"""Install as a before_request hook on the API."""
+
+ @api.route(before_request=True)
+ def_rate_limit(req,resp):
+ self.check(req,resp)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_modules/responder/models.html b/_modules/responder/models.html
index 8931cfd..beec017 100644
--- a/_modules/responder/models.html
+++ b/_modules/responder/models.html
@@ -4,13 +4,13 @@
- responder.models — responder 3.2.0 documentation
+ responder.models — responder 3.4.1 documentation
-
+
@@ -88,7 +88,15 @@
self[key]=value
+
+[docs]classQueryDict(dict):
+"""A dictionary for query string parameters that handles multi-value keys.
+
+ Single-value access returns the last value for a key. Use :meth:`get_list`
+ to retrieve all values for a multi-value parameter.
+ """
+
def__init__(self,query_string):self.update(parse_qs(query_string))
@@ -103,6 +111,8 @@
exceptIndexError:return[]
+
+[docs]defget(self,key,default=None):""" Return the last data value for the passed key. If key doesn't exist
@@ -114,7 +124,8 @@
returndefaultifval==[]:returndefault
- returnval
+ returnval
+[docs]defget_list(self,key,default=None):""" Return the list of values for the key. If key doesn't exist, return a default value. """
- returnself._get_list(key,default,force_list=True)
+ returnself._get_list(key,default,force_list=True)
+
+
+[docs]defitems(self):""" Yield (key, value) pairs, where value is the last item in the list associated with the key. """forkeyinself:
- yieldkey,self[key]
+ yieldkey,self[key]
+
+
+[docs]defitems_list(self):""" Yield (key, value) pairs, where value is the the list. """
- yield fromsuper().items()
+ yield fromsuper().items()
+
+
[docs]classRequest:
+"""An HTTP request, passed to each view as the first argument.
+
+ Provides access to headers, cookies, query parameters, the request body,
+ session data, and more. Most properties are synchronous; reading the body
+ (via :attr:`content`, :attr:`text`, or :meth:`media`) requires ``await``.
+ """
+
__slots__=["_starlette","formats",
@@ -195,6 +223,7 @@
@propertydefmimetype(self):
+"""The MIME type of the request body, from the ``Content-Type`` header."""returnself.headers.get("Content-Type","")@property
@@ -312,6 +341,7 @@
@propertydefis_secure(self):
+"""``True`` if the request was made over HTTPS."""returnself.url.scheme=="https"
@@ -366,6 +396,22 @@
[docs]classResponse:
+"""An HTTP response, passed to each view as the second argument.
+
+ Mutate this object to control what gets sent back to the client. Set
+ :attr:`text`, :attr:`html`, :attr:`media`, or :attr:`content` to define
+ the body. Use :attr:`headers` and :meth:`set_cookie` to control metadata.
+
+ :var text: Set the response body as plain text (sets ``Content-Type: text/plain``).
+ :var html: Set the response body as HTML (sets ``Content-Type: text/html``).
+ :var media: Set a Python object (dict, list) to be serialized as JSON (or negotiated format).
+ :var content: Set the raw response body as bytes.
+ :var status_code: The HTTP status code (e.g. ``200``, ``404``). Defaults to ``200`` if not set.
+ :var headers: A dict of response headers.
+ :var cookies: A ``SimpleCookie`` holding cookies to set on the response.
+ :var session: A dict of session data. Changes are persisted in a signed cookie.
+ """# noqa: E501
+
__slots__=["req","status_code",
@@ -385,28 +431,42 @@
def__init__(self,req,*,formats):self.req=req
- #: The HTTP Status Code to use for the Response.self.status_code:int|None=None
- self.content=None#: A bytes representation of the response body.
+ self.content=Noneself.mimetype=Noneself.encoding=DEFAULT_ENCODING
- self.media=None#: A Python object that will be content-negotiated and
- #: sent back to the client. Typically, in JSON formatting.
+ self.media=Noneself._stream=None
- self.headers={}#: A Python dictionary of ``{key: value}``,
- #: representing the headers of the response.
+ self.headers={}self.formats=formats
- self.cookies:SimpleCookie=SimpleCookie()#: The cookies set in the Response
- self.session=(
- req.session
- )#: The cookie-based session data, in dict form, to add to the Response.
+ self.cookies:SimpleCookie=SimpleCookie()
+ self.session=req.session
+
+[docs]defstream(self,func,*args,**kwargs):
+"""Set up a streaming response from an async generator function.
+
+ The generator yields chunks of bytes that are sent to the client
+ as they are produced, without buffering the full response in memory.
+
+ Usage::
+
+ @api.route("/stream")
+ async def stream_data(req, resp):
+ @resp.stream
+ async def body():
+ for i in range(10):
+ yield f"chunk {i}\\n".encode()
+
+ :param func: An async generator function that yields response chunks.
+ """assertinspect.isasyncgenfunction(func)self._stream=functools.partial(func,*args,**kwargs)
- returnfunc
+ returnfunc
+[docs]defredirect(self,location,*,set_text=True,status_code=HTTP_301):
+"""Redirect the client to a different URL.
+
+ :param location: The URL to redirect to.
+ :param set_text: If ``True``, set a default redirect message as the body.
+ :param status_code: The HTTP status code (default ``301``).
+ """self.status_code=status_codeifset_text:self.text=f"Redirecting to: {location}"
- self.headers.update({"Location":location})
+ self.headers.update({"Location":location})
+[docs]defset_cookie(self,key,
@@ -556,6 +627,25 @@
secure=False,httponly=True,):
+"""Set a cookie on the response with full control over directives.
+
+ :param key: The cookie name.
+ :param value: The cookie value.
+ :param expires: Expiration date string (e.g. ``"Thu, 01 Jan 2026 00:00:00 GMT"``).
+ :param path: URL path the cookie applies to (default ``"/"``).
+ :param domain: Domain the cookie is valid for.
+ :param max_age: Maximum age in seconds before the cookie expires.
+ :param secure: If ``True``, cookie is only sent over HTTPS.
+ :param httponly: If ``True`` (default), cookie is inaccessible to JavaScript.
+
+ Usage::
+
+ resp.set_cookie(
+ "token", value="abc123",
+ max_age=3600, secure=True, httponly=True,
+ )
+
+ """self.cookies[key]=valuemorsel=self.cookies[key]ifexpiresisnotNone:
@@ -567,7 +657,8 @@
ifmax_ageisnotNone:morsel["max-age"]=max_agemorsel["secure"]=secure
- morsel["httponly"]=httponly
+ morsel["httponly"]=httponly
+
def_prepare_cookies(self,starlette_response):cookie_header=(
@@ -594,10 +685,12 @@
@propertydefok(self):
+"""``True`` if the status code is in the 2xx range (success)."""return200<=self.status_code_safe<300@propertydefstatus_code_safe(self)->int:
+"""Return the status code, raising ``RuntimeError`` if it hasn't been set."""ifself.status_codeisNone:raiseRuntimeError("HTTP status code has not been defined")returnself.status_code
diff --git a/_modules/responder/status_codes.html b/_modules/responder/status_codes.html
index 154d5ce..1bd686e 100644
--- a/_modules/responder/status_codes.html
+++ b/_modules/responder/status_codes.html
@@ -4,13 +4,13 @@
- responder.status_codes — responder 3.2.0 documentation
+ responder.status_codes — responder 3.4.1 documentation
-
+
diff --git a/_sources/api.rst.txt b/_sources/api.rst.txt
index fc7851f..7bc0506 100644
--- a/_sources/api.rst.txt
+++ b/_sources/api.rst.txt
@@ -43,6 +43,45 @@ status code, headers, and cookies.
:inherited-members:
+Route Groups
+------------
+
+Group related routes under a shared URL prefix — useful for API versioning
+and organizing large applications.
+
+.. autoclass:: responder.api.RouteGroup
+ :members:
+
+
+Background Queue
+----------------
+
+Run tasks in background threads without blocking the response. Available
+as ``api.background``.
+
+.. autoclass:: responder.background.BackgroundQueue
+ :members:
+
+
+Query Dict
+----------
+
+A dictionary subclass for query string parameters with multi-value support.
+
+.. autoclass:: responder.models.QueryDict
+ :members:
+
+
+Rate Limiter
+------------
+
+In-memory token bucket rate limiter. Limits requests per client IP address
+and returns ``429 Too Many Requests`` when exceeded.
+
+.. autoclass:: responder.ext.ratelimit.RateLimiter
+ :members:
+
+
Status Code Helpers
-------------------
diff --git a/_sources/changes.md.txt b/_sources/changes.md.txt
index 3cf2d0b..c485090 100644
--- a/_sources/changes.md.txt
+++ b/_sources/changes.md.txt
@@ -7,6 +7,44 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
+## [v3.2.0] - 2026-03-22
+
+### Added
+
+- Pydantic auto-validation: `request_model` validates input, returns 422 on failure
+- Pydantic auto-serialization: `response_model` strips extra fields from responses
+- Server-Sent Events: `@resp.sse` for real-time streaming
+- `resp.stream_file()` for streaming large files without loading into memory
+- `@api.after_request()` hooks
+- `api.group("/prefix")` for route groups and API versioning
+- `api.graphql("/path", schema=schema)` one-liner GraphQL setup
+- `api = responder.API(request_id=True)` for automatic request ID generation
+- Built-in rate limiter: `RateLimiter(requests=100, period=60).install(api)`
+- MessagePack format support: `await req.media("msgpack")`
+- `req.is_json`, `req.path_params`, `req.client` properties
+- `api.exception_handler()` decorator for custom error handling
+- Lifespan context manager support
+- `uuid` and `path` route convertors
+- PEP 561 `py.typed` marker
+- Pydantic support for OpenAPI schema generation
+
+### Changed
+
+- Dependencies flattened: `pip install responder` gets everything
+- Core deps reduced to starlette + uvicorn
+- TestClient lazy-loaded (no httpx import in production)
+- Before-request hooks can short-circuit by setting status code
+- Removed poethepoet task runner
+
+### Fixed
+
+- Multipart parser losing headers when parts have multiple headers
+- `url_for()` with typed route params (`{id:int}`)
+- `resp.body` encoding crash on bytes content
+- GraphQL text query missing `await`
+- Streaming responses not sending Content-Type headers
+- Python 3.9 compatibility for union type syntax
+
## [v3.0.0] - 2026-03-22
### Added
diff --git a/_sources/guide-config.rst.txt b/_sources/guide-config.rst.txt
new file mode 100644
index 0000000..ce62f6e
--- /dev/null
+++ b/_sources/guide-config.rst.txt
@@ -0,0 +1,172 @@
+Configuration
+=============
+
+Every application needs different settings for different environments —
+debug mode in development, real secrets in production, different database
+URLs for testing. This guide covers how to manage configuration cleanly.
+
+
+Environment Variables
+---------------------
+
+The simplest and most universal approach. Environment variables work
+everywhere — locally, in Docker, on cloud platforms — and keep secrets
+out of your source code::
+
+ import os
+ import responder
+
+ api = responder.API(
+ debug=os.getenv("DEBUG", "false").lower() == "true",
+ secret_key=os.environ["SECRET_KEY"],
+ cors=os.getenv("CORS_ENABLED", "false").lower() == "true",
+ )
+
+Some variables Responder handles automatically:
+
+- ``PORT`` — when set, the server binds to ``0.0.0.0`` on this port
+
+Set variables in your shell::
+
+ $ export SECRET_KEY="your-secret-here"
+ $ export DEBUG=true
+ $ python app.py
+
+Or in a ``.env`` file (don't commit this to git)::
+
+ SECRET_KEY=your-secret-here
+ DEBUG=true
+
+
+Using .env Files
+----------------
+
+For local development, a ``.env`` file is convenient. Install
+``python-dotenv`` and load it at the top of your app::
+
+ $ uv pip install python-dotenv
+
+::
+
+ from dotenv import load_dotenv
+ load_dotenv()
+
+ import os
+ import responder
+
+ api = responder.API(
+ secret_key=os.environ["SECRET_KEY"],
+ )
+
+Add ``.env`` to your ``.gitignore`` — never commit secrets.
+
+
+Configuration Class Pattern
+----------------------------
+
+For larger applications, a configuration class keeps things organized::
+
+ import os
+
+ class Config:
+ SECRET_KEY = os.environ.get("SECRET_KEY", "dev-secret")
+ DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
+ DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///dev.db")
+ CORS_ORIGINS = os.environ.get("CORS_ORIGINS", "").split(",")
+
+ config = Config()
+
+ api = responder.API(
+ debug=config.DEBUG,
+ secret_key=config.SECRET_KEY,
+ cors=bool(config.CORS_ORIGINS[0]),
+ cors_params={"allow_origins": config.CORS_ORIGINS},
+ )
+
+This makes it easy to see all your settings in one place.
+
+
+Secret Key
+----------
+
+The ``secret_key`` is used to sign session cookies. If someone knows your
+secret key, they can forge session data and impersonate any user.
+
+Rules:
+
+- **Never use the default** in production
+- **Generate a random key**: ``python -c "import secrets; print(secrets.token_hex(32))"``
+- **Store it in an environment variable**, not in code
+- **Rotate it** if it's ever compromised (this invalidates all sessions)
+
+::
+
+ api = responder.API(secret_key=os.environ["SECRET_KEY"])
+
+
+Debug Mode
+----------
+
+Debug mode controls error page behavior:
+
+- **On** (``debug=True``): detailed error pages with tracebacks. Never
+ use this in production — it exposes your source code.
+- **Off** (``debug=False``): generic error pages. This is the default.
+
+::
+
+ api = responder.API(debug=True) # development only
+
+A common pattern is to read it from the environment::
+
+ api = responder.API(debug=os.getenv("DEBUG") == "true")
+
+
+Allowed Hosts
+-------------
+
+In production, always set ``allowed_hosts`` to prevent Host header
+attacks. This should match the domain names your application serves::
+
+ api = responder.API(
+ allowed_hosts=["example.com", "www.example.com"],
+ )
+
+In development, you can use ``["*"]`` (the default) or specific local
+addresses::
+
+ api = responder.API(allowed_hosts=["localhost", "127.0.0.1"])
+
+
+Putting It All Together
+-----------------------
+
+A production-ready configuration setup::
+
+ import os
+ from dotenv import load_dotenv
+
+ load_dotenv()
+
+ import responder
+
+ api = responder.API(
+ debug=os.getenv("DEBUG", "false") == "true",
+ secret_key=os.environ["SECRET_KEY"],
+ allowed_hosts=os.getenv("ALLOWED_HOSTS", "*").split(","),
+ cors=bool(os.getenv("CORS_ORIGINS")),
+ cors_params={
+ "allow_origins": os.getenv("CORS_ORIGINS", "").split(","),
+ "allow_methods": ["GET", "POST", "PUT", "DELETE"],
+ },
+ )
+
+With a ``.env`` file for local development::
+
+ SECRET_KEY=dev-secret-do-not-use-in-prod
+ DEBUG=true
+ ALLOWED_HOSTS=localhost,127.0.0.1
+ CORS_ORIGINS=http://localhost:3000
+
+And environment variables set properly in production (via your cloud
+platform's dashboard, Docker secrets, or a secrets manager).
diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt
index 7455866..6b9ba39 100644
--- a/_sources/index.rst.txt
+++ b/_sources/index.rst.txt
@@ -86,7 +86,7 @@ Installation
$ uv pip install responder
-Python 3.9 and above. That's it.
+Python 3.10 and above. That's it.
.. toctree::
@@ -106,7 +106,11 @@ Python 3.9 and above. That's it.
tutorial-rest
tutorial-sqlalchemy
+ tutorial-auth
+ tutorial-websockets
+ tutorial-middleware
tutorial-flask
+ guide-config
.. toctree::
:maxdepth: 1
diff --git a/_sources/quickstart.rst.txt b/_sources/quickstart.rst.txt
index 1781eed..a36cdc7 100644
--- a/_sources/quickstart.rst.txt
+++ b/_sources/quickstart.rst.txt
@@ -330,3 +330,49 @@ for lightweight use cases where you don't need a full message broker.
your application, which makes them fast to start but means CPU-intensive
work will block the event loop. For heavy computation, consider a proper
task queue.
+
+
+Putting It All Together
+-----------------------
+
+Here's a complete, working Responder application that combines everything
+from this guide::
+
+ import responder
+
+ api = responder.API()
+
+ @api.route("/")
+ def index(req, resp):
+ resp.text = "Welcome to the API"
+
+ @api.route("/hello/{name}")
+ def greet(req, resp, *, name):
+ resp.media = {"message": f"hello, {name}!"}
+
+ @api.route("/add/{a:int}/{b:int}")
+ def add(req, resp, *, a, b):
+ resp.media = {"result": a + b}
+
+ @api.route("/echo", methods=["POST"])
+ async def echo(req, resp):
+ data = await req.media()
+ resp.media = {"received": data}
+
+ if __name__ == "__main__":
+ api.run()
+
+Save this as ``app.py``, run it with ``python app.py``, and try::
+
+ $ curl http://localhost:5042/
+ $ curl http://localhost:5042/hello/world
+ $ curl http://localhost:5042/add/3/4
+ $ curl -X POST http://localhost:5042/echo \
+ -H "Content-Type: application/json" -d '{"key": "value"}'
+
+From here, explore the :doc:`tour` for the full range of features, or
+jump into the tutorials:
+
+- :doc:`tutorial-rest` — build a full CRUD API with validation
+- :doc:`tutorial-sqlalchemy` — connect to a database
+- :doc:`tutorial-auth` — add authentication
diff --git a/_sources/tour.rst.txt b/_sources/tour.rst.txt
index 962c720..f041e3f 100644
--- a/_sources/tour.rst.txt
+++ b/_sources/tour.rst.txt
@@ -416,6 +416,22 @@ Requests to ``/flask/`` will be handled by Flask. Everything else goes
through Responder. Both WSGI and ASGI apps are supported — Responder
wraps WSGI apps in an ASGI adapter automatically.
+You can also mount `marimo `_ notebooks as
+interactive dashboards within your API::
+
+ import marimo
+
+ server = (
+ marimo.create_asgi_app()
+ .with_app(path="", root="./notebooks/dashboard.py")
+ .with_app(path="/analysis", root="./notebooks/analysis.py")
+ )
+
+ api.mount("/notebooks", server.build())
+
+Notebooks are served at ``/notebooks/`` and ``/notebooks/analysis``,
+with full interactivity — reactive cells, widgets, plots, and all.
+
Cookies
-------
diff --git a/_sources/tutorial-auth.rst.txt b/_sources/tutorial-auth.rst.txt
new file mode 100644
index 0000000..5e40408
--- /dev/null
+++ b/_sources/tutorial-auth.rst.txt
@@ -0,0 +1,191 @@
+Authentication
+==============
+
+Every API that handles user data needs authentication — a way to verify
+who is making a request. This guide covers the most common patterns:
+API keys, JWT tokens, and how to build reusable auth guards with
+Responder's before-request hooks.
+
+
+API Key Authentication
+----------------------
+
+The simplest approach. The client sends a secret key in a header, and
+your server checks it against a known value. This is common for
+server-to-server communication and simple APIs::
+
+ API_KEYS = {"sk-abc123", "sk-def456"}
+
+ @api.route(before_request=True)
+ def check_api_key(req, resp):
+ key = req.headers.get("X-API-Key")
+ if key not in API_KEYS:
+ resp.status_code = 401
+ resp.media = {"error": "Invalid or missing API key"}
+
+Because the before-request hook sets ``resp.status_code``, the route
+handler is skipped entirely for unauthorized requests. The client never
+reaches your endpoint — the guard catches them first.
+
+The client sends the key like this::
+
+ $ curl -H "X-API-Key: sk-abc123" http://localhost:5042/protected
+
+
+Bearer Token Authentication
+----------------------------
+
+Bearer tokens are the standard for modern APIs. The client sends a token
+in the ``Authorization`` header, and the server validates it. The most
+common format is `JWT `_ (JSON Web Tokens).
+
+Install PyJWT::
+
+ $ uv pip install pyjwt
+
+Create a helper to encode and decode tokens::
+
+ import jwt
+ from datetime import datetime, timedelta
+
+ SECRET = "your-secret-key"
+
+ def create_token(user_id: int) -> str:
+ payload = {
+ "sub": user_id,
+ "exp": datetime.utcnow() + timedelta(hours=24),
+ }
+ return jwt.encode(payload, SECRET, algorithm="HS256")
+
+ def verify_token(token: str) -> dict | None:
+ try:
+ return jwt.decode(token, SECRET, algorithms=["HS256"])
+ except jwt.InvalidTokenError:
+ return None
+
+Add a login endpoint that issues tokens, and a before-request hook that
+verifies them::
+
+ @api.route("/login", methods=["POST"])
+ async def login(req, resp):
+ data = await req.media()
+ # In a real app, check credentials against a database
+ if data.get("username") == "admin" and data.get("password") == "secret":
+ token = create_token(user_id=1)
+ resp.media = {"token": token}
+ else:
+ resp.status_code = 401
+ resp.media = {"error": "Invalid credentials"}
+
+ @api.route(before_request=True)
+ def auth_guard(req, resp):
+ # Skip auth for the login endpoint itself
+ if req.url.path == "/login":
+ return
+
+ auth = req.headers.get("Authorization", "")
+ if not auth.startswith("Bearer "):
+ resp.status_code = 401
+ resp.media = {"error": "Missing bearer token"}
+ return
+
+ token = auth[7:] # Strip "Bearer "
+ payload = verify_token(token)
+ if payload is None:
+ resp.status_code = 401
+ resp.media = {"error": "Invalid or expired token"}
+ return
+
+ # Store the authenticated user on the request state
+ req.state.user_id = payload["sub"]
+
+Now any route can access the authenticated user::
+
+ @api.route("/me")
+ def get_me(req, resp):
+ resp.media = {"user_id": req.state.user_id}
+
+The client flow:
+
+1. ``POST /login`` with credentials → receive a token
+2. Include ``Authorization: Bearer `` on every subsequent request
+3. The token expires after 24 hours — the client must log in again
+
+
+Skipping Auth for Public Routes
+--------------------------------
+
+The example above skips auth for ``/login`` by checking the path. For
+more control, you can use a set of public paths::
+
+ PUBLIC_PATHS = {"/login", "/signup", "/health", "/docs", "/schema.yml"}
+
+ @api.route(before_request=True)
+ def auth_guard(req, resp):
+ if req.url.path in PUBLIC_PATHS:
+ return
+ # ... check token
+
+
+Custom Exception for Auth Errors
+---------------------------------
+
+For cleaner code, define a custom exception and register a handler::
+
+ class AuthError(Exception):
+ def __init__(self, message="Unauthorized", status_code=401):
+ self.message = message
+ self.status_code = status_code
+
+ @api.exception_handler(AuthError)
+ async def handle_auth_error(req, resp, exc):
+ resp.status_code = exc.status_code
+ resp.media = {"error": exc.message}
+
+Now your auth guard can simply raise::
+
+ @api.route(before_request=True)
+ def auth_guard(req, resp):
+ if req.url.path in PUBLIC_PATHS:
+ return
+ if "Authorization" not in req.headers:
+ raise AuthError("Missing authorization header")
+
+
+Using Sessions for Web Apps
+----------------------------
+
+For traditional web applications (with HTML pages and forms), cookie-based
+sessions are simpler than tokens. The browser handles cookies automatically
+— no client-side token management needed::
+
+ @api.route("/login", methods=["POST"])
+ async def login(req, resp):
+ data = await req.media("form")
+ if data["username"] == "admin" and data["password"] == "secret":
+ resp.session["user"] = data["username"]
+ api.redirect(resp, location="/dashboard")
+ else:
+ resp.status_code = 401
+ resp.html = "
Invalid credentials
"
+
+ @api.route("/dashboard")
+ def dashboard(req, resp):
+ user = req.session.get("user")
+ if not user:
+ api.redirect(resp, location="/login")
+ return
+ resp.html = f"
Welcome, {user}!
"
+
+ @api.route("/logout")
+ def logout(req, resp):
+ resp.session.clear()
+ api.redirect(resp, location="/login")
+
+Remember to set a proper secret key::
+
+ api = responder.API(secret_key="your-production-secret-key")
+
+The session data is signed (not encrypted) — users can read it but
+can't tamper with it. Don't store sensitive data like passwords in
+sessions.
diff --git a/_sources/tutorial-middleware.rst.txt b/_sources/tutorial-middleware.rst.txt
new file mode 100644
index 0000000..5679d35
--- /dev/null
+++ b/_sources/tutorial-middleware.rst.txt
@@ -0,0 +1,129 @@
+Writing Middleware
+==================
+
+Middleware sits between the server and your route handlers, processing
+every request and response that flows through your application. It's the
+right tool for cross-cutting concerns — things that apply to *all*
+requests, not just specific routes.
+
+Common middleware use cases:
+
+- Request logging and timing
+- Authentication and authorization
+- Adding security headers
+- Request ID generation
+- Rate limiting
+- Response compression (built-in)
+
+
+Hooks vs. Middleware
+--------------------
+
+Responder gives you two levels of request processing:
+
+**Hooks** (``before_request`` / ``after_request``) run inside Responder's
+routing layer. They receive Responder's ``req`` and ``resp`` objects and
+are the simplest way to add behavior::
+
+ @api.route(before_request=True)
+ def add_header(req, resp):
+ resp.headers["X-Powered-By"] = "Responder"
+
+ @api.after_request()
+ def log_request(req, resp):
+ print(f"{req.method} {req.url.path} -> {resp.status_code}")
+
+**Middleware** runs at the ASGI level, wrapping the entire application.
+It's more powerful but more complex — you work with raw ASGI scopes
+instead of Responder objects. Use middleware when you need to process
+requests *before* they reach Responder's routing, or when you need to
+integrate with Starlette middleware.
+
+
+Using Starlette Middleware
+--------------------------
+
+Responder is built on Starlette, so any Starlette middleware works
+out of the box::
+
+ from starlette.middleware.base import BaseHTTPMiddleware
+
+ class TimingMiddleware(BaseHTTPMiddleware):
+ async def dispatch(self, request, call_next):
+ import time
+ start = time.time()
+ response = await call_next(request)
+ duration = time.time() - start
+ response.headers["X-Response-Time"] = f"{duration:.3f}s"
+ return response
+
+ api.add_middleware(TimingMiddleware)
+
+The ``dispatch`` method receives a Starlette ``Request`` and a
+``call_next`` function. Call ``call_next(request)`` to pass the request
+to the next middleware (or to your route handler). The return value is
+a Starlette ``Response`` that you can modify before it's sent.
+
+
+Built-in Middleware
+-------------------
+
+Responder configures several middleware components automatically:
+
+- **GZipMiddleware** — compresses responses larger than 500 bytes
+- **TrustedHostMiddleware** — validates the ``Host`` header
+- **ServerErrorMiddleware** — catches unhandled exceptions
+- **ExceptionMiddleware** — routes exceptions to your handlers
+- **SessionMiddleware** — manages signed cookie sessions
+
+Optional middleware you can enable:
+
+- **CORSMiddleware** — ``api = responder.API(cors=True)``
+- **HTTPSRedirectMiddleware** — ``api = responder.API(enable_hsts=True)``
+
+
+Adding Third-Party Middleware
+-----------------------------
+
+Any ASGI middleware can be added with ``api.add_middleware()``::
+
+ from some_package import SomeMiddleware
+
+ api.add_middleware(SomeMiddleware, option1="value", option2=True)
+
+Keyword arguments are passed to the middleware's constructor.
+
+
+Middleware Order
+----------------
+
+Middleware wraps your application like layers of an onion. The *last*
+middleware added is the *outermost* layer — it sees the request first
+and the response last.
+
+Responder's built-in middleware stack (from outermost to innermost):
+
+1. SessionMiddleware
+2. ServerErrorMiddleware
+3. CORSMiddleware (if enabled)
+4. TrustedHostMiddleware
+5. HTTPSRedirectMiddleware (if enabled)
+6. GZipMiddleware
+7. ExceptionMiddleware
+8. Your routes
+
+When you call ``api.add_middleware()``, your middleware is added *outside*
+the existing stack. Keep this in mind for ordering dependencies — if
+middleware A depends on middleware B having run first, add B before A.
+
+
+When to Use What
+-----------------
+
+- **Simple header additions, logging, auth checks** → use hooks
+- **Response transformation, timing, third-party integrations** → use middleware
+- **Rate limiting** → use the built-in ``RateLimiter`` (it uses hooks internally)
+- **Request ID** → use ``api = responder.API(request_id=True)``
+
+Start with hooks. They're simpler and cover most cases. Graduate to
+middleware when hooks aren't enough.
diff --git a/_sources/tutorial-websockets.rst.txt b/_sources/tutorial-websockets.rst.txt
new file mode 100644
index 0000000..62f17ae
--- /dev/null
+++ b/_sources/tutorial-websockets.rst.txt
@@ -0,0 +1,171 @@
+WebSocket Tutorial
+==================
+
+HTTP is request-response — the client asks, the server answers, and the
+connection closes. WebSockets upgrade that into a persistent, bidirectional
+channel where both sides can send messages at any time. This is what powers
+chat apps, live dashboards, multiplayer games, and collaborative editors.
+
+This tutorial builds a simple chat room to show how WebSockets work in
+Responder.
+
+
+How WebSockets Work
+-------------------
+
+1. The client sends a normal HTTP request with an ``Upgrade: websocket``
+ header.
+2. The server accepts the upgrade and the connection switches protocols.
+3. Both sides can now send messages freely — no more request/response.
+4. Either side can close the connection at any time.
+
+In Responder, WebSocket routes receive a ``ws`` object instead of
+``req`` and ``resp``. The ``ws`` object has methods for accepting the
+connection, sending and receiving data, and closing.
+
+
+Echo Server
+-----------
+
+The simplest WebSocket — echoes everything back::
+
+ @api.route("/ws", websocket=True)
+ async def echo(ws):
+ await ws.accept()
+ while True:
+ data = await ws.receive_text()
+ await ws.send_text(f"Echo: {data}")
+
+The ``await ws.accept()`` call completes the WebSocket handshake. After
+that, you're in a loop — receive a message, send a response.
+
+Test it with a WebSocket client::
+
+ $ pip install websocket-client
+ $ python -c "
+ import websocket
+ ws = websocket.create_connection('ws://localhost:5042/ws')
+ ws.send('hello')
+ print(ws.recv()) # Echo: hello
+ ws.close()
+ "
+
+
+Chat Room
+---------
+
+A chat room needs to broadcast messages to all connected clients. We keep
+a set of active connections and iterate through them when someone sends
+a message::
+
+ connected = set()
+
+ @api.route("/chat", websocket=True)
+ async def chat(ws):
+ await ws.accept()
+ connected.add(ws)
+ try:
+ while True:
+ message = await ws.receive_text()
+ # Broadcast to all connected clients
+ for client in connected:
+ await client.send_text(message)
+ except Exception:
+ pass
+ finally:
+ connected.discard(ws)
+
+The ``try/finally`` block ensures we remove disconnected clients from
+the set, even if the connection drops unexpectedly.
+
+
+Data Formats
+------------
+
+WebSockets support three data formats:
+
+**Text** — plain strings::
+
+ await ws.send_text("hello")
+ message = await ws.receive_text()
+
+**JSON** — auto-serialized Python objects::
+
+ await ws.send_json({"type": "update", "data": [1, 2, 3]})
+ message = await ws.receive_json()
+
+**Binary** — raw bytes, useful for images, audio, or custom protocols::
+
+ await ws.send_bytes(b"\x00\x01\x02")
+ data = await ws.receive_bytes()
+
+
+HTML Client
+-----------
+
+Here's a minimal HTML page that connects to the chat room. The browser's
+built-in ``WebSocket`` API handles everything — no libraries needed:
+
+.. code-block:: html
+
+
+
+
+
+
+
+
+
+
+Save this as ``static/index.html`` and serve it with Responder's
+built-in static file support.
+
+
+Before-Request Hooks for WebSockets
+------------------------------------
+
+You can run code before a WebSocket connection is established, just like
+HTTP before-request hooks. This is useful for authentication::
+
+ @api.before_request(websocket=True)
+ async def ws_auth(ws):
+ # Check for a token in the query string
+ # (WebSocket headers are limited in browsers)
+ await ws.accept()
+
+WebSocket before-request hooks receive the ``ws`` object and must call
+``await ws.accept()`` if they want the connection to proceed.
+
+
+Testing WebSockets
+------------------
+
+Use Starlette's ``TestClient`` for WebSocket tests::
+
+ from starlette.testclient import TestClient
+
+ def test_echo():
+ client = TestClient(api)
+ with client.websocket_connect("/ws") as ws:
+ ws.send_text("hello")
+ assert ws.receive_text() == "Echo: hello"
+
+The ``websocket_connect`` context manager handles the connection
+lifecycle — it connects on enter and disconnects on exit.
diff --git a/_static/documentation_options.js b/_static/documentation_options.js
index b1611d5..3d20959 100644
--- a/_static/documentation_options.js
+++ b/_static/documentation_options.js
@@ -1,5 +1,5 @@
const DOCUMENTATION_OPTIONS = {
- VERSION: '3.2.0',
+ VERSION: '3.4.1',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
diff --git a/api.html b/api.html
index 45e147e..8b23b4d 100644
--- a/api.html
+++ b/api.html
@@ -5,13 +5,13 @@
- API Reference — responder 3.2.0 documentation
+ API Reference — responder 3.4.1 documentation
-
+
@@ -78,6 +78,28 @@ module and use it to define your entire web service.
+
Middleware wraps the entire application and can inspect or modify
+every request and response. Middleware is applied in reverse order —
+the last middleware added runs first.
+
+
Parameters:
+
+
middleware_cls – A Starlette-compatible middleware class.
+
middleware_config – Keyword arguments passed to the middleware constructor.
An HTTP request, passed to each view as the first argument.
+
Provides access to headers, cookies, query parameters, the request body,
+session data, and more. Most properties are synchronous; reading the body
+(via content, text, or media()) requires await.
An HTTP response, passed to each view as the second argument.
+
Mutate this object to control what gets sent back to the client. Set
+text, html, media, or content to define
+the body. Use headers and set_cookie() to control metadata.
+
+
Variables:
+
+
text – Set the response body as plain text (sets Content-Type:text/plain).
+
html – Set the response body as HTML (sets Content-Type:text/html).
+
media – Set a Python object (dict, list) to be serialized as JSON (or negotiated format).
+
content – Set the raw response body as bytes.
+
status_code – The HTTP status code (e.g. 200, 404). Defaults to 200 if not set.
+
headers – A dict of response headers.
+
cookies – A SimpleCookie holding cookies to set on the response.
+
session – A dict of session data. Changes are persisted in a signed cookie.
Uses a ThreadPoolExecutor sized to the number of CPUs. Access it
+via api.background.
+
Usage:
+
# As a decorator — fire and forget
+@api.background.task
+defsend_email(to,subject):
+ ...
+
+send_email("user@example.com","Hello")
+
+# Direct submission
+future=api.background.run(send_email,"user@example.com","Hello")
+
+# As a callable (supports async functions)
+awaitapi.background(send_email,"user@example.com","Hello")
+
Every application needs different settings for different environments —
+debug mode in development, real secrets in production, different database
+URLs for testing. This guide covers how to manage configuration cleanly.
The simplest and most universal approach. Environment variables work
+everywhere — locally, in Docker, on cloud platforms — and keep secrets
+out of your source code:
diff --git a/sandbox.html b/sandbox.html
index 9dd4113..e71e9a8 100644
--- a/sandbox.html
+++ b/sandbox.html
@@ -5,13 +5,13 @@
- Development Sandbox — responder 3.2.0 documentation
+ Development Sandbox — responder 3.4.1 documentation
-
+
diff --git a/search.html b/search.html
index 48d3503..6b397a9 100644
--- a/search.html
+++ b/search.html
@@ -4,14 +4,14 @@
- Search — responder 3.2.0 documentation
+ Search — responder 3.4.1 documentation
-
+
diff --git a/searchindex.js b/searchindex.js
index 1aad1cb..7c4cb80 100644
--- a/searchindex.js
+++ b/searchindex.js
@@ -1 +1 @@
-Search.setIndex({"alltitles":{"API Reference":[[0,null]],"Added":[[2,"added"],[2,"id1"],[2,"id7"],[2,"id8"],[2,"id11"],[2,"id14"],[2,"id19"],[2,"id26"],[2,"id32"],[2,"id33"],[2,"id34"],[2,"id35"],[2,"id36"],[2,"id39"],[2,"id40"],[2,"id42"],[2,"id45"],[2,"id48"],[2,"id49"],[2,"id50"],[2,"id54"]],"After-Request Hooks":[[9,"after-request-hooks"]],"Background Tasks":[[6,"background-tasks"]],"Backlog":[[1,null]],"Before-Request Hooks":[[9,"before-request-hooks"]],"Blueprints \u2192 Route Groups":[[10,"blueprints-route-groups"]],"Building Frontend Assets":[[3,"building-frontend-assets"]],"Building a REST API":[[11,null]],"CORS":[[9,"cors"]],"CRUD Endpoints":[[12,"crud-endpoints"]],"Changed":[[2,"changed"],[2,"id6"],[2,"id9"],[2,"id12"],[2,"id15"],[2,"id18"],[2,"id23"],[2,"id24"],[2,"id25"],[2,"id27"],[2,"id28"],[2,"id30"],[2,"id31"],[2,"id37"],[2,"id41"],[2,"id46"],[2,"id47"],[2,"id53"]],"Changelog":[[2,null]],"Class-Based Views":[[9,"class-based-views"]],"Cloud Platforms":[[4,"cloud-platforms"]],"Command Line Interface":[[3,null]],"Cookie-Based Sessions":[[9,"cookie-based-sessions"]],"Cookies":[[9,"cookies"]],"Create a Book":[[11,"create-a-book"]],"Create a Web Service":[[6,"create-a-web-service"]],"Custom Error Handling":[[9,"custom-error-handling"]],"Custom Instance Names":[[3,"custom-instance-names"]],"Database Setup":[[12,"database-setup"]],"Define Your Models":[[11,"define-your-models"],[12,"define-your-models"]],"Delete a Book":[[11,"delete-a-book"]],"Deployment":[[4,null]],"Deprecated":[[2,"deprecated"]],"Development Sandbox":[[7,null]],"Docker":[[4,"docker"]],"Error Handling":[[11,"error-handling"]],"Feature Tour":[[9,null]],"Fixed":[[2,"fixed"],[2,"id2"],[2,"id3"],[2,"id4"],[2,"id5"],[2,"id10"],[2,"id13"],[2,"id16"],[2,"id17"],[2,"id20"],[2,"id21"],[2,"id22"],[2,"id29"],[2,"id38"],[2,"id43"],[2,"id44"],[2,"id51"],[2,"id52"]],"Future Ideas":[[1,"future-ideas"]],"Get a Single Book":[[11,"get-a-single-book"]],"Getting Started":[[8,"getting-started"]],"Gradual Migration":[[10,"gradual-migration"]],"GraphQL":[[9,"graphql"]],"HSTS":[[9,"hsts"]],"Hello World":[[6,"hello-world"]],"In-Memory Storage":[[11,"in-memory-storage"]],"Installation":[[5,"installation"],[12,"installation"]],"JSON APIs":[[10,"json-apis"]],"Launching from a File":[[3,"launching-from-a-file"]],"Launching from a Module":[[3,"launching-from-a-module"]],"Launching from a URL":[[3,"launching-from-a-url"]],"Lifespan Events":[[9,"lifespan-events"]],"Lifespan for Startup and Shutdown":[[12,"lifespan-for-startup-and-shutdown"]],"List All Books":[[11,"list-all-books"]],"MessagePack":[[9,"messagepack"]],"Method Filtering":[[9,"method-filtering"]],"Migrating from Flask":[[10,null]],"Mounting Other Apps":[[9,"mounting-other-apps"]],"OpenAPI Documentation":[[9,"openapi-documentation"]],"Operations":[[7,"operations"]],"Project":[[5,null]],"Project Setup":[[11,"project-setup"]],"Quick Reference":[[10,"quick-reference"]],"Quick Start":[[6,null]],"Rate Limiting":[[9,"rate-limiting"]],"Reading Requests":[[6,"reading-requests"]],"Removed":[[2,"removed"]],"Rendering Templates":[[6,"rendering-templates"]],"Request":[[0,"request"]],"Request ID":[[9,"request-id"]],"Responder":[[5,null]],"Response":[[0,"response"]],"Reverse Proxy":[[4,"reverse-proxy"]],"Route Groups":[[9,"route-groups"]],"Route Parameters":[[6,"route-parameters"],[10,"route-parameters"]],"Run It":[[11,"run-it"],[12,"run-it"]],"Run the Server":[[6,"run-the-server"]],"Running Locally":[[4,"running-locally"]],"Sending Responses":[[6,"sending-responses"]],"Server-Sent Events (SSE)":[[9,"server-sent-events-sse"]],"Serving Files":[[9,"serving-files"]],"Setup":[[7,"setup"]],"Static Files":[[9,"static-files"]],"Status Code Helpers":[[0,"status-code-helpers"]],"Templates":[[10,"templates"]],"Testing":[[8,null]],"Testing Before and After Hooks":[[8,"testing-before-and-after-hooks"]],"Testing Error Handling":[[8,"testing-error-handling"]],"Testing File Uploads":[[8,"testing-file-uploads"]],"Testing Headers and Cookies":[[8,"testing-headers-and-cookies"]],"Testing JSON APIs":[[8,"testing-json-apis"]],"Testing Lifespan Events":[[8,"testing-lifespan-events"]],"Testing Request Validation":[[8,"testing-request-validation"]],"Testing WebSockets":[[8,"testing-websockets"]],"The API Class":[[0,"the-api-class"]],"The Big Differences":[[10,"the-big-differences"]],"The Idea":[[5,"the-idea"]],"Tips":[[8,"tips"],[12,"tips"]],"Trusted Hosts":[[9,"trusted-hosts"]],"Try It Out":[[11,"try-it-out"]],"Tutorials":[[5,null]],"Type Convertors":[[6,"type-convertors"]],"Unreleased":[[2,"unreleased"]],"Update a Book":[[11,"update-a-book"]],"User Guide":[[5,null]],"Using Fixtures":[[8,"using-fixtures"]],"Using PostgreSQL":[[12,"using-postgresql"]],"Using SQLAlchemy":[[12,null]],"Uvicorn Directly":[[4,"uvicorn-directly"]],"WebSocket Support":[[9,"websocket-support"]],"What You Get":[[5,"what-you-get"]],"What\u2019s Next":[[11,"what-s-next"]],"v0.0.1 - 2018-10-12":[[2,"v0-0-1-2018-10-12"]],"v0.0.10 - 2018-10-17":[[2,"v0-0-10-2018-10-17"]],"v0.0.2 - 2018-10-13":[[2,"v0-0-2-2018-10-13"]],"v0.0.3 - 2018-10-13":[[2,"v0-0-3-2018-10-13"]],"v0.0.4 - 2018-10-15":[[2,"v0-0-4-2018-10-15"]],"v0.0.5 - 2018-10-15":[[2,"v0-0-5-2018-10-15"]],"v0.0.6 - 2018-10-16":[[2,"v0-0-6-2018-10-16"]],"v0.0.7 - 2018-10-16":[[2,"v0-0-7-2018-10-16"]],"v0.0.8 - 2018-10-17":[[2,"v0-0-8-2018-10-17"]],"v0.0.9 - 2018-10-17":[[2,"v0-0-9-2018-10-17"]],"v0.1.0 - 2018-10-17":[[2,"v0-1-0-2018-10-17"]],"v0.1.1 - 2018-10-17":[[2,"v0-1-1-2018-10-17"]],"v0.1.2 - 2018-10-18":[[2,"v0-1-2-2018-10-18"]],"v0.1.3 - 2018-10-18":[[2,"v0-1-3-2018-10-18"]],"v0.1.4 - 2018-10-19":[[2,"v0-1-4-2018-10-19"]],"v0.1.5 - 2018-10-20":[[2,"v0-1-5-2018-10-20"]],"v0.1.6 - 2018-10-20":[[2,"v0-1-6-2018-10-20"]],"v0.2.0 - 2018-10-22":[[2,"v0-2-0-2018-10-22"]],"v0.2.1 - 2018-10-23":[[2,"v0-2-1-2018-10-23"]],"v0.2.2 - 2018-10-23":[[2,"v0-2-2-2018-10-23"]],"v0.2.3 - 2018-10-24":[[2,"v0-2-3-2018-10-24"]],"v0.3.0 - 2018-10-24":[[2,"v0-3-0-2018-10-24"]],"v0.3.1 - 2018-10-24":[[2,"v0-3-1-2018-10-24"]],"v0.3.2 - 2018-10-25":[[2,"v0-3-2-2018-10-25"]],"v0.3.3 - 2018-10-25":[[2,"v0-3-3-2018-10-25"]],"v1.0.0 - 2018-10-26":[[2,"v1-0-0-2018-10-26"]],"v1.0.1 - 2018-10-26":[[2,"v1-0-1-2018-10-26"]],"v1.0.2 - 2018-10-27":[[2,"v1-0-2-2018-10-27"]],"v1.0.3 - 2018-10-27":[[2,"v1-0-3-2018-10-27"]],"v1.0.4 - 2018-10-27":[[2,"v1-0-4-2018-10-27"]],"v1.0.5- 2018-10-27":[[2,"v1-0-5-2018-10-27"]],"v1.1.0 - 2018-10-27":[[2,"v1-1-0-2018-10-27"]],"v1.1.1 - 2018-10-29":[[2,"v1-1-1-2018-10-29"]],"v1.1.2 - 2018-11-11":[[2,"v1-1-2-2018-11-11"]],"v1.1.3 - 2019-01-12":[[2,"v1-1-3-2019-01-12"]],"v1.2.0 - 2018-12-29":[[2,"v1-2-0-2018-12-29"]],"v1.3.0 - 2019-02-22":[[2,"v1-3-0-2019-02-22"]],"v1.3.1 - 2019-04-28":[[2,"v1-3-1-2019-04-28"]],"v1.3.2 - 2019-08-15":[[2,"v1-3-2-2019-08-15"]],"v2.0.0 - 2019-09-19":[[2,"v2-0-0-2019-09-19"]],"v2.0.1 - 2019-09-20":[[2,"v2-0-1-2019-09-20"]],"v2.0.2 - 2019-09-20":[[2,"v2-0-2-2019-09-20"]],"v2.0.3 - 2019-09-20":[[2,"v2-0-3-2019-09-20"]],"v2.0.4 - 2019-11-19":[[2,"v2-0-4-2019-11-19"]],"v2.0.5 - 2019-12-15":[[2,"v2-0-5-2019-12-15"]],"v3.0.0 - 2026-03-22":[[2,"v3-0-0-2026-03-22"]]},"docnames":["api","backlog","changes","cli","deployment","index","quickstart","sandbox","testing","tour","tutorial-flask","tutorial-rest","tutorial-sqlalchemy"],"envversion":{"sphinx":65,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1},"filenames":["api.rst","backlog.md","changes.md","cli.rst","deployment.rst","index.rst","quickstart.rst","sandbox.md","testing.rst","tour.rst","tutorial-flask.rst","tutorial-rest.rst","tutorial-sqlalchemy.rst"],"indexentries":{"accepts() (responder.request method)":[[0,"responder.Request.accepts",false]],"add_event_handler() (responder.api method)":[[0,"responder.API.add_event_handler",false]],"add_route() (responder.api method)":[[0,"responder.API.add_route",false]],"after_request() (responder.api method)":[[0,"responder.API.after_request",false]],"api (class in responder)":[[0,"responder.API",false]],"apparent_encoding (responder.request property)":[[0,"responder.Request.apparent_encoding",false]],"client (responder.request property)":[[0,"responder.Request.client",false]],"content (responder.request property)":[[0,"responder.Request.content",false]],"content (responder.response attribute)":[[0,"responder.Response.content",false]],"cookies (responder.request property)":[[0,"responder.Request.cookies",false]],"cookies (responder.response attribute)":[[0,"responder.Response.cookies",false]],"encoding (responder.request property)":[[0,"responder.Request.encoding",false]],"exception_handler() (responder.api method)":[[0,"responder.API.exception_handler",false]],"file() (responder.response method)":[[0,"responder.Response.file",false]],"formats (responder.response attribute)":[[0,"responder.Response.formats",false]],"full_url (responder.request property)":[[0,"responder.Request.full_url",false]],"graphql() (responder.api method)":[[0,"responder.API.graphql",false]],"group() (responder.api method)":[[0,"responder.API.group",false]],"headers (responder.request property)":[[0,"responder.Request.headers",false]],"headers (responder.response attribute)":[[0,"responder.Response.headers",false]],"is_100() (in module responder.status_codes)":[[0,"responder.status_codes.is_100",false]],"is_200() (in module responder.status_codes)":[[0,"responder.status_codes.is_200",false]],"is_300() (in module responder.status_codes)":[[0,"responder.status_codes.is_300",false]],"is_400() (in module responder.status_codes)":[[0,"responder.status_codes.is_400",false]],"is_500() (in module responder.status_codes)":[[0,"responder.status_codes.is_500",false]],"is_json (responder.request property)":[[0,"responder.Request.is_json",false]],"media (responder.response attribute)":[[0,"responder.Response.media",false]],"media() (responder.request method)":[[0,"responder.Request.media",false]],"method (responder.request property)":[[0,"responder.Request.method",false]],"module":[[0,"module-responder",false]],"mount() (responder.api method)":[[0,"responder.API.mount",false]],"on_event() (responder.api method)":[[0,"responder.API.on_event",false]],"params (responder.request property)":[[0,"responder.Request.params",false]],"path_matches_route() (responder.api method)":[[0,"responder.API.path_matches_route",false]],"path_params (responder.request property)":[[0,"responder.Request.path_params",false]],"redirect() (responder.api method)":[[0,"responder.API.redirect",false]],"request (class in responder)":[[0,"responder.Request",false]],"requests (responder.api property)":[[0,"responder.API.requests",false]],"responder":[[0,"module-responder",false]],"response (class in responder)":[[0,"responder.Response",false]],"route() (responder.api method)":[[0,"responder.API.route",false]],"schema() (responder.api method)":[[0,"responder.API.schema",false]],"serve() (responder.api method)":[[0,"responder.API.serve",false]],"session (responder.request property)":[[0,"responder.Request.session",false]],"session (responder.response attribute)":[[0,"responder.Response.session",false]],"session() (responder.api method)":[[0,"responder.API.session",false]],"sse() (responder.response method)":[[0,"responder.Response.sse",false]],"state (responder.request property)":[[0,"responder.Request.state",false]],"status_code (responder.response attribute)":[[0,"responder.Response.status_code",false]],"stream_file() (responder.response method)":[[0,"responder.Response.stream_file",false]],"template() (responder.api method)":[[0,"responder.API.template",false]],"template_string() (responder.api method)":[[0,"responder.API.template_string",false]],"text (responder.request property)":[[0,"responder.Request.text",false]],"url (responder.request property)":[[0,"responder.Request.url",false]],"url_for() (responder.api method)":[[0,"responder.API.url_for",false]]},"objects":{"":[[0,0,0,"-","responder"]],"responder":[[0,1,1,"","API"],[0,1,1,"","Request"],[0,1,1,"","Response"]],"responder.API":[[0,2,1,"","add_event_handler"],[0,2,1,"","add_route"],[0,2,1,"","after_request"],[0,2,1,"","exception_handler"],[0,2,1,"","graphql"],[0,2,1,"","group"],[0,2,1,"","mount"],[0,2,1,"","on_event"],[0,2,1,"","path_matches_route"],[0,2,1,"","redirect"],[0,3,1,"","requests"],[0,2,1,"","route"],[0,2,1,"","schema"],[0,2,1,"","serve"],[0,2,1,"","session"],[0,2,1,"","template"],[0,2,1,"","template_string"],[0,2,1,"","url_for"]],"responder.Request":[[0,2,1,"","accepts"],[0,3,1,"","apparent_encoding"],[0,3,1,"","client"],[0,3,1,"","content"],[0,3,1,"","cookies"],[0,3,1,"","encoding"],[0,3,1,"","full_url"],[0,3,1,"","headers"],[0,3,1,"","is_json"],[0,2,1,"","media"],[0,3,1,"","method"],[0,3,1,"","params"],[0,3,1,"","path_params"],[0,3,1,"","session"],[0,3,1,"","state"],[0,3,1,"","text"],[0,3,1,"","url"]],"responder.Response":[[0,4,1,"","content"],[0,4,1,"","cookies"],[0,2,1,"","file"],[0,4,1,"","formats"],[0,4,1,"","headers"],[0,4,1,"","media"],[0,4,1,"","session"],[0,2,1,"","sse"],[0,4,1,"","status_code"],[0,2,1,"","stream_file"]],"responder.status_codes":[[0,5,1,"","is_100"],[0,5,1,"","is_200"],[0,5,1,"","is_300"],[0,5,1,"","is_400"],[0,5,1,"","is_500"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:attribute","5":"py:function"},"terms":{"":[0,2,3,4,5,6,8,9,10,12],"0":[3,4,6,9,11,12],"0441172719":11,"1":[0,3,4,6,9,10,11],"10":[0,6,9],"100":9,"127":[3,4],"13":4,"150mb":4,"1965":11,"2":[4,6,8,9,11,12],"200":[6,8,9,11],"201":[6,8,10,11,12],"204":[11,12],"3":[4,5,6,8,9,11],"301":[0,6],"308":2,"3600":9,"4":4,"40":2,"400":[0,8,9,11],"401":9,"404":[6,10,11,12],"41d4":6,"422":[8,9,11],"429":9,"446655440000":6,"500":[2,6,8,9,11],"5000":9,"5042":[3,4,6,11],"550e8400":6,"60":9,"600":[0,9],"8":0,"80":4,"8000":[4,6],"8192":0,"9":[5,8],"978":11,"99":8,"A":[0,4,5,6,9],"And":[5,6],"As":9,"But":[6,8,9],"By":[3,6,8,9,11],"For":[0,3,4,6,8,9,11,12],"If":[0,3,5,6,8,9,10,12],"In":[5,6,9,10,12],"It":[0,3,5,6,8,9],"No":[5,8,9,10,11],"Not":6,"On":9,"One":[5,6,8],"That":[4,5,6,8],"The":[2,3,4,6,8,9,11,12],"There":[8,9],"These":6,"To":[11,12],"Will":0,"With":0,"__main__":[4,5,8,11,12],"__name__":[4,5,8,9,10,11,12],"__tablename__":12,"_route_for":2,"a2wsgi":2,"a716":6,"abc":11,"abc123":[8,9],"abil":2,"abl":0,"abort":10,"about":[4,6,8,10],"abov":5,"abstract":12,"accept":[0,2,6,8,9],"access":[0,4,9,12],"acm":3,"acquir":7,"across":[4,9,12],"activ":7,"actual":[0,6,9],"ad":[1,9],"adapt":9,"add":[0,1,2,6,11,12],"add_event_handl":0,"add_head":9,"add_request_id":0,"add_rout":[0,9],"add_tim":[8,9],"add_vers":8,"addit":0,"address":[0,6,9],"adher":2,"affect":9,"after":[0,5,6,12],"after_request":[0,1,8,9],"ag":9,"against":[0,4,9],"agre":6,"ai":9,"aiosqlit":12,"alemb":12,"alias":2,"alic":9,"all":[0,2,3,4,5,6,7,8,9,10,12],"alloc":8,"allow":9,"allow_credenti":[0,9],"allow_head":[0,9],"allow_method":[0,9],"allow_origin":[0,9],"allow_origin_regex":0,"allowed_host":[0,9],"alongsid":9,"alpin":4,"alreadi":[0,8,9],"also":[0,2,3,6,8,9],"altern":[0,9],"alwai":[4,5,6,9],"an":[0,3,6,9,10,11,12],"angle_bracket":10,"angular":9,"ani":[0,3,4,5,6,8,9,11,12],"annot":[5,6,9],"annual":[6,9],"answer":9,"anyon":5,"anyth":6,"anywher":[4,9],"api":[2,3,4,5,6,9,12],"api_v1":10,"apispec":2,"app":[0,2,3,4,5,8,10,11,12],"appar":0,"apparent_encod":0,"appear":9,"applic":[0,2,3,4,6,8,9,11,12],"approach":[9,10],"appropri":[9,12],"ar":[0,2,3,4,5,6,8,9,10,12],"architectur":9,"aren":6,"arg":[0,10],"argument":[0,2,6,10],"around":[0,5],"arriv":[6,10],"asgi":[0,2,4,5,6,9,10],"ask":[5,9],"assert":8,"asset":[4,5,9],"assign":11,"async":[0,1,2,4,5,6,8,9,10,11,12],"async_sess":12,"async_sessionmak":12,"asynccontextmanag":[9,12],"asynchron":[2,4],"asyncio":12,"asyncpg":12,"attach":9,"attack":[4,9],"attribut":[0,3,12],"auth":[5,9],"auth_check":9,"authent":[6,9],"author":[6,7,9,11,12],"auto":9,"auto_escap":0,"autobuild":7,"autoincr":12,"automat":[0,4,5,6,8,9,10,11,12],"avail":6,"avoid":[8,9],"aw":4,"await":[0,6,8,9,10,11,12],"azur":[3,4],"b":[6,8,12],"back":[0,6,9,12],"background":[2,5],"backlog":5,"bad":[8,9,11],"balanc":4,"bandwidth":9,"bar":9,"base":[0,2,5,12],"base_url":0,"basemodel":[0,8,9,11,12],"basic":6,"batteri":5,"battl":5,"becaus":[0,5,6,10,11],"becom":[0,12],"befor":[5,6,12],"before_request":[0,1,2,8,9,10],"begin":12,"behind":[4,9],"being":0,"best":5,"better":5,"bidirect":9,"big":5,"biggest":10,"bin":7,"binari":[6,9],"bind":[0,4,6],"blob":3,"block":[6,8,9,10],"blueprint":5,"bodi":[0,6,8,9,10,11],"boilerpl":5,"book":[5,12],"book_id":[11,12],"bookin":[11,12],"bookout":12,"books_db":11,"born":5,"both":[5,6,9,10],"bottom":11,"bp":10,"break":8,"bring":5,"broke":[6,8],"broker":6,"browser":[6,7,9,11],"bucket":9,"buffer":6,"bufix":2,"bug":[2,8],"bugfix":2,"build":[2,4,5,6,7,12],"builder":12,"built":[2,4,5,6,8,9],"bump":2,"bypass":4,"byte":[0,5,6],"cach":[6,8,9],"caddi":4,"call":[3,6,8,9,11],"callabl":0,"came":6,"can":[0,3,4,5,6,8,9,10,11,12],"carri":6,"case":[0,5,6],"cat":9,"catalog":11,"catch":9,"categori":0,"cd":7,"ceas":2,"celeri":6,"central":[0,6],"certain":0,"certif":4,"chang":12,"changelog":5,"channel":9,"chardet":0,"chat":9,"check":[0,7,8,9],"check_exist":[0,9,11,12],"choic":[6,9],"choos":9,"chunk":[0,9],"chunk_siz":0,"ci":2,"circuit":[1,5,9],"class":[2,5,6,8,11,12],"clean":[5,9,11],"cleaner":9,"cleanup":9,"cli":2,"click":5,"client":[0,5,6,8,9,11],"clone":7,"close":[8,9,12],"close_database_connection_pool":0,"cloud":[3,5,6],"cmd":4,"code":[2,3,4,5,6,7,8,9,11,12],"collabor":9,"colon":3,"column":12,"com":[3,6,7,9],"come":[6,9,10],"command":[4,5],"commit":12,"common":[3,4,6,9,10,11],"commonli":6,"commun":9,"compact":9,"complement":[1,9],"complet":[9,11],"complex":12,"compos":12,"compress":[5,6],"comput":6,"concept":[2,9,10],"concern":6,"concurr":4,"condit":[8,9],"config":12,"configur":[0,2,4,6,8,9],"conflict":2,"confus":8,"conn":12,"connect":[0,4,8,9,12],"consid":[1,6,12],"consist":[6,9],"consol":[9,12],"const":9,"consum":9,"contact":0,"contain":[0,4,6],"container":4,"content":[0,2,5,6,8,9,11],"content_typ":[0,8,9],"context":[5,8,9,12],"contextlib":[9,12],"continu":6,"contract":8,"control":[0,4,6,9,12],"conveni":[0,12],"convent":[4,11],"convert":[2,6],"convertor":[5,10],"cooki":[0,2,5,6,10],"copi":4,"cor":[0,2,5,8],"core":[2,4],"coroutin":0,"correctli":4,"correspond":12,"cors_param":[0,9],"count":2,"cpu":[4,6],"creat":[0,5,7,8,9,10,12],"create_al":12,"create_async_engin":12,"create_book":[11,12],"create_item":[0,9,10],"create_pet":9,"creation":12,"cross":9,"crud":[5,11],"css":9,"csv":9,"curl":[3,6,11],"curly_brac":10,"custom":[0,5,6,8,11],"cycl":6,"d":[8,11],"dashboard":9,"data":[0,2,6,8,9,10,11,12],"databas":[5,6,8,9,11],"database_url":12,"db":12,"debug":[0,2,9,10],"decim":6,"declar":[2,6],"declarativebas":12,"decod":[2,9],"decor":[0,6,9],"deep":5,"def":[0,5,6,8,9,10,11,12],"default":[0,1,2,3,6,8,9,12],"default_valu":[0,9],"defend":9,"defin":[0,5,6,9],"definit":0,"del":11,"delet":[5,6,9,12],"delete_book":[11,12],"deliber":6,"demo":3,"denpend":2,"depend":[2,4,8],"deploi":[4,5],"deploy":5,"depth":9,"describ":9,"descript":[0,9],"design":[6,9,11],"detail":[4,9,11],"detect":[0,5,9],"dev":2,"develop":[3,4,9,12],"devic":9,"dict":[0,5,6,9,11,12],"dictionari":0,"differ":[3,5,8,9,12],"digit":6,"direct":[2,9],"directli":[3,5,6,8,11,12],"directori":[0,6,9],"disabl":[8,12],"disk":[0,6,9],"dispatch":[2,9],"dispos":12,"distribut":[4,9],"django":[5,9],"do":[4,6,9],"doc":[7,8,9,11],"docker":5,"dockerfil":4,"docs_rout":[0,9,11],"docstr":9,"document":[0,2,4,5,7,11],"doe":12,"doesn":[0,9,11,12],"domain":9,"don":[6,8,9,10,12],"done":4,"dot":3,"doubl":11,"down":[9,12],"download":9,"dramat":2,"driver":12,"drop":2,"dump":2,"dune":11,"duplex":9,"duplic":9,"dure":[3,4,8,12],"duti":11,"dynam":6,"e":0,"e29b":6,"each":[0,4,8,9,12],"easi":[3,6],"echo":12,"ecosystem":[5,6],"edit":7,"editor":9,"effect":[8,11],"effici":4,"either":0,"element":[0,9],"els":[6,9,10,12],"email":6,"emb":9,"empti":11,"enabl":[2,9,11],"enable_async":6,"enable_hst":[0,9],"encod":0,"end":[6,11],"endpoint":[0,2,5,8,9,10,11],"enforc":9,"engin":[6,12],"enough":4,"ensur":[9,11,12],"enter":8,"entir":[0,9,11],"entri":11,"env":4,"environ":[0,3,4,6],"eol":2,"equival":10,"error":[0,5,12],"errorhandl":10,"escap":0,"essenti":9,"etc":[6,8,9],"even":[3,4,9],"event":[0,2,5,6,10],"event_typ":0,"eventsourc":9,"ever":5,"everi":[0,4,5,6,8,9,11,12],"everyth":[0,4,9,10,11,12],"everywher":3,"evolv":12,"exactli":[8,9],"exampl":[0,3,6,9],"exc":[0,8,9,11],"exceed":9,"except":[0,2,5,8,9],"exception_cl":0,"exception_handl":[0,8,9,10,11],"execut":12,"exist":[0,2,5,10],"exit":[8,9],"expect":6,"expir":9,"expire_on_commit":12,"explain":9,"explan":0,"explicit":[6,9,10],"explicitli":[9,10],"explor":[1,9],"export":9,"expos":4,"expose_head":0,"express":[5,6],"ext":[2,9,12],"extens":[2,6],"extern":6,"extra":[2,4,9],"extract":0,"extrem":6,"f":[0,5,6,8,9,11],"factori":12,"fail":[8,9,12],"failur":8,"falcon":[5,9],"fall":0,"fals":[0,8,9,11,12],"familiar":[5,10],"fast":[4,5,6,8],"fastapi":5,"faster":9,"featur":[0,5,6],"feed":9,"feel":5,"fetch":3,"few":10,"fido":9,"field":[0,8,9,11],"figur":6,"file":[0,2,4,5,6,11,12],"filenam":[0,8],"filepath":6,"filesystem":[2,3],"fill":6,"filter":[5,6],"find":[3,9,10],"fine":[4,9,12],"fire":8,"first":[0,9,11,12],"fit":5,"fix":7,"fixtur":5,"flag":[4,9,12],"flask":[5,9],"flask_app":[9,10],"float":[0,6,8],"fly":4,"follow":6,"font":9,"forg":9,"form":[0,6,10],"format":[0,2,6,7,9],"formerli":9,"forward":[4,9],"found":[2,6,11,12],"foundat":9,"fragment":3,"framework":[5,9],"franca":6,"frank":11,"fresh":8,"from":[0,2,4,5,6,8,9,11,12],"from_attribut":12,"front":4,"frontend":[5,9],"fsspec":3,"full":[0,2,3,6,9],"full_url":[0,6,9],"fulli":6,"fun":5,"func":0,"function":[0,6,9,10],"futur":5,"g":0,"game":9,"gatewai":4,"gener":[0,5,8,9,11],"get":[0,6,9,12],"get_book":[11,12],"get_us":[0,9,10],"git":7,"github":[3,7],"give":[0,8,9,12],"given":[0,8],"global":[10,11],"go":[6,10],"goe":[9,10],"good":[4,5,9],"googl":[3,4],"grace":4,"grade":6,"gradual":[5,9],"grain":12,"graphen":[0,2,5,9],"graphiql":[2,5,9],"graphql":[0,2,5],"great":[3,5,9],"greet":[5,9],"greet_world":5,"greetingresourc":9,"group":[0,5],"grow":9,"guard":5,"guid":[6,10,12],"guido":6,"gzip":[5,6],"h":11,"h1":6,"ha":[6,8,10,11],"half":6,"halv":9,"hand":0,"handl":[4,5,6],"handle_value_error":[0,9,11],"handler":[0,5,8,9,11],"happen":[5,6],"hard":8,"have":[0,2,3,6,8,10,11],"hdf":3,"head":3,"header":[0,4,5,6,9,10],"heavi":6,"hello":[0,3,5,8,9,10],"hello_html":6,"hello_json":6,"hello_to":6,"hello_world":6,"helloworld":3,"help":[0,12],"helper":5,"herbert":11,"here":[4,6,9,10],"high":[4,9],"hold":[0,5,6],"home":5,"honor":[4,6],"hood":9,"hook":[0,1,5,6],"host":[0,2,4,5,6],"hostnam":9,"hour":9,"how":[5,6,9,10,11,12],"hst":5,"html":[0,2,5,6,9,10],"http":[0,2,3,4,5,6,7,8,9,11],"httponli":9,"httpsredirectmiddlewar":4,"httpx":[2,8],"i":[0,2,3,4,5,6,8,9,10,11,12],"id":[0,5,6,10,11,12],"idea":10,"idempot":11,"ident":10,"identifi":9,"imag":[4,6,9],"immedi":[5,6,9],"immut":2,"implement":8,"import":[0,2,3,5,6,8,9,10,11,12],"improv":2,"includ":[3,4,5,6,7,8,9,11],"incom":[0,6,11],"incredibli":9,"independ":4,"index":[0,9],"individu":9,"industri":9,"info":[0,2,9],"inform":[0,11],"infrastructur":[4,5],"inherit":9,"initi":[0,8],"inject":9,"input":[8,9,11],"insensit":[0,5,6],"instal":[0,3,4,7,9],"instanc":[0,4,5,6,8],"instant":6,"instead":[6,8,9,11,12],"int":[0,6,9,10,11,12],"integ":[0,6,11,12],"integr":[11,12],"intens":6,"intent":5,"intention":9,"interact":[2,9,11],"interfac":[4,5,6,10],"intern":[8,9],"invalid":[8,9],"involv":0,"io":4,"iot":9,"ip":[6,9],"is_100":0,"is_200":0,"is_300":0,"is_400":0,"is_500":0,"is_class_bas":2,"is_json":[0,6],"is_secur":6,"isbn":[11,12],"isn":[6,9,10],"issu":2,"item":[0,8,9,10,12],"itemin":0,"itemout":0,"its":[0,2,4,11,12],"j":9,"javascript":[3,9],"jinja2":[0,6,10],"jpeg":9,"jpg":9,"json":[0,3,5,6,9,11],"jsonifi":10,"just":[4,8,9,12],"keep":[2,6,8,9,12],"kei":[0,8,9,10,12],"kennethreitz":[3,7],"keyword":[0,6,10],"know":6,"known":0,"kwarg":0,"languag":9,"larg":9,"larger":8,"later":[6,8],"launch":[4,5],"lazili":0,"lead":8,"learn":9,"legaci":10,"less":9,"let":[3,4,6,9,11,12],"level":4,"librari":[8,9],"licens":0,"lifespan":[0,2,5,11],"lift":6,"lightn":4,"lightweight":6,"like":[4,6,8,9,11],"limit":[1,4,5,12],"line":[5,6],"lingua":6,"list":[0,5,6,8,9,12],"list_book":[11,12],"list_item":9,"list_pet":9,"list_us":[0,9,10],"list_users_v2":9,"live":9,"ll":[5,6,9,10,11,12],"load":[0,2,3,4,6,9],"local":[3,5,10],"localhost":[6,11,12],"locat":[0,6,10],"log":[4,9],"log_level":2,"log_respons":9,"logic":[5,6,9],"login":9,"long":6,"look":[3,5,6],"loop":[6,10],"lose":12,"lower":0,"lowercas":6,"machin":9,"made":5,"magic":10,"mai":4,"main":3,"make":[4,6,8,9,10],"malici":9,"manag":[5,8,9,12],"mani":[4,6,9],"manual":0,"map":[2,6,9,10,12],"mapper":12,"mark":[2,9],"marshmallow":[0,2,9],"match":[0,6,9,11],"matter":9,"max_ag":[0,9],"mean":[4,6,10],"mechan":9,"media":[0,2,5,6,8,9,10,11,12],"memori":[0,5,6,9],"messag":[0,6,9],"messagepack":5,"metadata":[6,12],"method":[0,5,6,8,10,11,12],"microservic":9,"middelwar":0,"middlewar":[0,1,2,6,9],"might":6,"migrat":[2,5,9,12],"mime":0,"mimetyp":[6,9],"min":9,"minim":4,"minimum":2,"minor":2,"miss":[8,9],"mix":9,"ml":9,"mode":[0,7],"model":[0,5,8,9],"model_dump":12,"model_valid":12,"modern":[4,9,10,12],"modifi":[9,11,12],"modul":[0,2,5,6,9],"more":[0,3,4,6,9,10,11],"most":[0,3,4,6,8,9,12],"mount":[0,2,5,10],"move":[2,6,9,10],"msgpack":9,"much":[6,9],"multipart":[2,6],"multipl":[2,4,9,11],"multiplay":9,"must":[0,6,9,11],"mutabl":5,"mutat":[0,6,10],"my_templ":6,"myapi":4,"myapp":3,"mydb":12,"mysql":12,"n":6,"name":[0,5,6,8,9,10],"namespac":2,"nativ":10,"natur":[8,12],"nearli":10,"need":[4,5,6,8,9,10,12],"negoti":[0,5,6,8,9],"nervou":6,"network":8,"never":[9,11],"new":[0,6,8,9,10,11],"new_endpoint":10,"next":5,"next_id":11,"nginx":4,"non":6,"none":[0,2,11,12],"normal":9,"notabl":2,"notasecret":0,"note":[6,9,10,11],"notic":6,"notif":9,"now":[2,6,9,12],"npm":3,"nullabl":12,"number":6,"o":[0,6,10],"object":[0,2,6,9,10,12],"objecttyp":[0,9],"obviou":8,"off":6,"offload":4,"offset":12,"often":[3,6,9],"ok":[6,8,11],"old":10,"on_delet":9,"on_ev":[0,8,9],"on_get":[5,9],"on_post":[5,9],"on_put":9,"on_request":[5,9],"on_startup":8,"onc":[9,10,11],"one":[0,5,6,9,10,12],"ones":[5,6,9],"onli":[6,9,10,11],"onmessag":9,"open":[2,6,7,12],"open_database_connection_pool":0,"openapi":[0,2,5,11],"openapi_rout":0,"openapi_them":0,"oper":[6,9,10,11],"opt":9,"option":[0,2,4,5,6],"order_bi":12,"organ":[8,9,10],"origin":9,"orm":12,"other":[0,2,5,6,12],"otherwis":[0,9],"our":11,"out":[5,6],"outgo":6,"output":11,"over":[0,5,6,9,10,12],"overal":2,"overhead":8,"overrid":[0,9],"overwhelm":9,"own":[4,9,12],"p":4,"pace":9,"packag":[2,3,4],"page":[0,2,6,9],"pagin":6,"painless":8,"param":[0,2,5,6,10],"paramet":[0,5,8,9,11],"parameter":0,"pars":[0,2,6,8,9],"part":[5,6,9],"pass":[0,6,8,10,12],"passion":5,"patch":[6,9],"path":[0,2,3,5,6,8,9],"path_matches_rout":0,"path_param":[0,6],"pattern":[4,5,6,9,10,11,12],"pdf":[6,8,9],"per":[4,8,9],"perfect":9,"period":9,"perman":6,"permit":9,"persist":9,"person":5,"pet":[0,9],"petin":9,"petout":9,"petschema":[0,9],"photo":9,"piec":9,"pin":2,"pip":[3,4,5,7,12],"place":9,"placehold":6,"plain":[6,9],"platform":[2,5,6],"pleasant":5,"plu":12,"plugin":2,"point":[3,4,11],"polici":9,"pool":[5,6,9,10,12],"popular":[6,9,12],"port":[0,4,6,8],"portion":0,"possibl":6,"post":[0,6,8,9,10,11,12],"postgresql":5,"potenti":2,"power":[5,8,9,12],"practic":9,"prefer":9,"prefix":[0,9],"present":6,"prevent":9,"price":[0,8],"primari":0,"primary_kei":12,"print":[9,12],"privat":9,"process":[4,5,6,8,9],"process_data":6,"produc":6,"product":[3,4,5,6,8,9,12],"profil":9,"programmat":9,"progress":9,"project":[2,3,7],"propag":8,"proper":[6,12],"properli":[8,12],"properti":[0,6,8],"protect":4,"proto":4,"protocol":[3,6,8,9],"prototyp":[2,3,5],"provid":0,"proxi":5,"public":[0,9],"push":[4,5,9],"put":[6,9,11,12],"py":[2,3,4,8,11,12],"pydant":[0,8,9,11,12],"pyproject":2,"pytest":[5,7,8],"python":[0,2,3,4,5,6,9,11,12],"pythonpath":3,"q":[6,10],"queri":[0,5,6,9,12],"queue":6,"quick":[0,3,5],"r":[6,8],"race":8,"railwai":4,"rais":[2,8,9],"raise_server_except":8,"random":0,"rang":[0,9],"rapidoc":[0,9],"rare":6,"rate":[1,4,5],"ratelimit":9,"rather":8,"raw":[3,5,6,9,12],"re":[0,4,6,8,9,10,11],"react":[2,9],"read":[0,2,5,9,10,11,12],"readabl":9,"readi":[4,5,6],"real":[9,11,12],"realli":[8,9],"receiv":[0,5,6,8,9],"receive_byt":9,"receive_incom":6,"receive_json":9,"receive_text":[8,9],"recommend":9,"reconnect":9,"redirect":[0,2,6,10],"redoc":[0,9],"reduc":2,"ref":3,"refactor":[2,8],"refer":[5,8],"refresh":12,"refus":9,"regist":[0,8,9],"register_blueprint":10,"reject":[8,9],"relat":[9,12],"releas":7,"reliabl":8,"remain":9,"remov":[9,10,11],"renam":8,"render":[0,1,4,5,9],"render_async":6,"render_templ":10,"replac":[2,6,9,11],"report":[6,8,9],"repres":0,"represent":0,"req":[0,5,6,8,9,10,11,12],"request":[2,3,4,5,10,11,12],"request_id":[0,9],"request_model":[0,8,9,11,12],"requir":[0,2,8,9],"research":5,"resolv":[2,9],"resolve_hello":[0,9],"resourc":[6,9,11],"resp":[0,2,5,6,8,9,10,11,12],"respect":3,"respond":[0,2,3,4,6,7,8,9,10,11,12],"respons":[5,8,9,10,11,12],"response_model":[0,9,11,12],"rest":[4,5,9],"restrict":9,"result":[9,12],"resum":2,"resume_incomplet":2,"retri":[0,9],"retriev":[9,11],"return":[0,2,5,6,8,9,10,11,12],"revers":5,"rfc3986":2,"right":[5,6,9,10],"roll":12,"rout":[0,2,5,8,11,12],"router":2,"ruff":7,"run":[0,2,3,5,7,8,9,10],"run_sync":12,"runner":4,"s3":3,"safe":[2,11],"sai":6,"said":4,"same":[3,4,5,6,9,10,11,12],"sandbox":5,"scalar":12,"scale":5,"schema":[0,2,5,9,11,12],"scope":[0,9],"scratch":[5,11],"search":[0,2,6,9],"second":0,"secret":9,"secret_kei":[0,9],"section":9,"secur":9,"see":[0,4,6,8,9,11,12],"select":[0,2,12],"self":[0,9],"semant":2,"send":[0,2,5,8,9,11],"send_byt":9,"send_json":9,"send_text":[8,9],"sent":[0,5,6],"separ":[6,8,11],"sequenti":2,"serial":[6,9],"serializ":6,"serv":[0,2,3,4,5,6,8,11],"server":[0,2,3,4,5,8,11,12],"servestat":2,"servic":[0,3,5,8,9,11],"session":[0,2,5,6,8,10,12],"session_id":9,"set":[0,2,4,5,6,7,8,9,12],"set_cooki":[2,9],"set_text":0,"setattr":12,"setup":[2,5,8],"sever":9,"sftp":3,"share":[0,3,6,8,9,12],"short":[1,5,9],"should":[11,12],"shouldn":0,"show":[2,9,10,12],"shut":[9,12],"shutdown":[0,4,5,8,9],"shutdwown":2,"sibl":5,"side":[6,9],"sign":[5,6,9],"signatur":[6,9,10],"simpl":[3,8,9,11],"simplecooki":0,"simpler":[6,9],"simplest":6,"simplic":[5,12],"simul":6,"sinc":[6,8],"singl":[2,3,5,6,9,10,12],"size":[0,12],"skip":9,"slash":6,"sleep":[6,8],"slim":4,"slow":6,"slowlori":4,"slug":6,"small":[4,5,8,9],"smaller":4,"so":[8,9,10,11,12],"solv":9,"some":[4,9],"someon":[6,9],"someth":[5,6,8,9],"sometim":[6,9],"somewher":6,"sourc":[0,7,9],"spawn":4,"spec":[6,9],"special":[5,9],"specif":[0,8,9,11],"specifi":3,"sphinx":7,"spin":6,"sprawl":5,"sql":12,"sqlalchemi":[5,11],"sqlite":12,"sse":[0,5],"ssl":4,"stabil":2,"stai":[10,12],"standard":[3,4,9,11],"starlett":[0,2,5,8],"start":[0,3,4,5,9,11,12],"startup":[0,2,5,8,9],"state":[0,2,8,9],"statement":5,"static":[0,2,4,5,6],"static_dir":[0,9],"static_rout":[0,2,9],"statu":[2,5,6,8,9,11],"status_cod":[0,5,6,8,9,10,11,12],"stdlib":2,"still":[9,10],"stop":9,"storag":[3,5],"store":[0,9,11],"str":[0,6,8,9,11,12],"straightforward":9,"stranger":[0,9],"stream":[0,2,9],"stream_fil":[0,9],"strict":9,"string":[0,5,6,9,12],"strip":9,"structur":9,"style":9,"stylesheet":9,"subcommand":3,"subrout":[5,9,10],"subsequ":9,"subtl":2,"success":[6,11],"successfulli":11,"successor":4,"suit":8,"suppli":2,"support":[0,1,2,3,4,5,6,10,12],"swagger":[5,9,11],"swagger_ui":[0,9],"switch":[2,12],"sync":[2,5,6,10],"synchron":[0,10],"syntax":[5,6,10],"system":[3,6,9],"t":[0,4,6,8,9,10,11,12],"tabl":12,"take":[5,6],"tamper":9,"target":2,"task":[2,5],"teach":5,"tear":9,"tell":[6,9,11,12],"templat":[0,1,2,5],"template_str":[0,6],"templates_dir":0,"termin":[3,4],"terms_of_servic":0,"test":[0,2,3,5,7,11],"test_500":[2,8],"test_api":8,"test_create_item":8,"test_custom_error":8,"test_head":8,"test_hello":8,"test_hook":8,"test_json":8,"test_upload":8,"test_valid":8,"test_websocket":8,"test_with_lifespan":8,"testclient":[0,2,8],"text":[0,5,6,8,9,10],"than":[8,9,10,11],"thei":[6,9,10,11],"them":[5,6,8,9,10,11],"theme":[0,9],"themselv":9,"thi":[0,2,3,4,5,6,8,9,10,11,12],"thing":[5,6,8],"think":6,"though":4,"thousand":4,"thread":[5,6,10],"threadpoolexecutor":2,"three":[6,9],"through":[6,9,11],"throughput":9,"time":[0,5,6,8,9,10,11,12],"time_start":0,"timeout":4,"tip":5,"titl":[0,6,9,11,12],"tl":4,"togeth":[5,9],"token":[6,9],"toml":2,"too":[3,4,8,9],"tool":7,"toolbelt":2,"toolkit":12,"top":0,"total":9,"tour":[0,5],"trace":9,"traceback":[2,9],"track":12,"tradit":9,"traffic":4,"transact":12,"translat":10,"transport":9,"treat":[0,9],"trick":9,"trigger":8,"true":[0,6,8,9,10,12],"trust":5,"trustedhostmiddlewar":4,"try":5,"tupl":[0,6,8],"tutori":11,"two":[6,9],"type":[0,2,5,9,10,11],"typic":[6,9],"typo":[2,9],"ui":[2,5,9,11],"unauthor":9,"under":[6,9,10],"understand":[5,6],"unicod":0,"uniqu":9,"unit":12,"unknown":0,"unless":9,"unlik":9,"unmaintain":2,"unmatch":9,"unpin":2,"unrecogn":9,"until":6,"up":[0,2,6,7,9],"updat":[2,5,6,9,12],"update_book":[11,12],"upgrad":[2,7,9],"upload":[2,5,6],"url":[0,2,5,6,8,9,10,12],"url_for":[0,8],"url_prefix":10,"urllib":2,"us":[0,2,3,4,5,6,9,10,11],"usag":[0,2],"user":[0,6,9,10,12],"user_id":[6,9,10],"usernam":9,"usual":8,"utf":0,"uuid":[0,6,9],"uuid4":0,"uv":[3,5,7,12],"uvicorn":[0,2,5,6],"uvloop":4,"v1":[0,9],"v2":9,"valid":[5,6,9,11,12],"valu":[0,5,6,8,9,10,11,12],"valueerror":[0,8,9,10,11],"variabl":[0,4,6],"ve":[5,8,10],"venv":7,"verb":9,"veri":[0,6,12],"verifi":8,"version":[0,2,8,9,11],"via":[2,9,10],"view":[0,2,5,6,8,10],"virtual":[3,4],"virtualenv":7,"visit":[9,11],"vue":9,"w":[8,9],"wa":[0,2,6,11],"wai":[3,4,5,6,9,10,12],"wait":10,"walk":[6,9,11],"want":[0,3,4,5,6,8,9,11,12],"watch":7,"we":[11,12],"web":[0,4,5,9,12],"websit":9,"websocket":[0,1,2,4,5],"websocket_connect":8,"welcom":5,"well":5,"went":5,"what":[0,6,8,9],"whatev":6,"when":[2,4,6,8,9,10,11,12],"where":[4,6,9,12],"whether":[0,6],"which":[0,2,6,8,9,10,12],"whichev":5,"while":[6,9,10],"whitenois":2,"who":[5,6],"widget":8,"wildcard":9,"window":2,"wire":6,"within":[2,12],"without":[0,4,6,8,9,12],"won":[6,8,9,12],"work":[3,4,5,6,9,11,12],"workdir":4,"worker":4,"world":[0,3,5,8,9,10],"worri":8,"would":8,"wrap":[6,8,9],"write":[5,6,9,10],"wrong":9,"wsgi":[0,2,4,5,9,10],"x":[0,4,6,8,9,10,11],"x89png":6,"xml":0,"xss":9,"yaml":[0,2,5,6,8,9],"year":[11,12],"yield":[0,9,12],"yml":[0,9],"you":[0,3,4,6,8,9,10,11,12],"your":[0,3,4,5,6,8,9,10],"yourself":6,"zero":4},"titles":["API Reference","Backlog","Changelog","Command Line Interface","Deployment","Responder","Quick Start","Development Sandbox","Testing","Feature Tour","Migrating from Flask","Building a REST API","Using SQLAlchemy"],"titleterms":{"":11,"0":2,"01":2,"02":2,"03":2,"04":2,"08":2,"09":2,"1":2,"10":2,"11":2,"12":2,"13":2,"15":2,"16":2,"17":2,"18":2,"19":2,"2":2,"20":2,"2018":2,"2019":2,"2026":2,"22":2,"23":2,"24":2,"25":2,"26":2,"27":2,"28":2,"29":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":2,"9":2,"In":11,"It":[11,12],"The":[0,5,10],"ad":2,"after":[8,9],"all":11,"api":[0,8,10,11],"app":9,"asset":3,"background":6,"backlog":1,"base":9,"befor":[8,9],"big":10,"blueprint":10,"book":11,"build":[3,11],"chang":2,"changelog":2,"class":[0,9],"cloud":4,"code":0,"command":3,"convertor":6,"cooki":[8,9],"cor":9,"creat":[6,11],"crud":12,"custom":[3,9],"databas":12,"defin":[11,12],"delet":11,"deploy":4,"deprec":2,"develop":7,"differ":10,"directli":4,"docker":4,"document":9,"endpoint":12,"error":[8,9,11],"event":[8,9],"featur":9,"file":[3,8,9],"filter":9,"fix":2,"fixtur":8,"flask":10,"from":[3,10],"frontend":3,"futur":1,"get":[5,8,11],"gradual":10,"graphql":9,"group":[9,10],"guid":5,"handl":[8,9,11],"header":8,"hello":6,"helper":0,"hook":[8,9],"host":9,"hst":9,"id":9,"idea":[1,5],"instal":[5,12],"instanc":3,"interfac":3,"json":[8,10],"launch":3,"lifespan":[8,9,12],"limit":9,"line":3,"list":11,"local":4,"memori":11,"messagepack":9,"method":9,"migrat":10,"model":[11,12],"modul":3,"mount":9,"name":3,"next":11,"openapi":9,"oper":7,"other":9,"out":11,"paramet":[6,10],"platform":4,"postgresql":12,"project":[5,11],"proxi":4,"quick":[6,10],"rate":9,"read":6,"refer":[0,10],"remov":2,"render":6,"request":[0,6,8,9],"respond":5,"respons":[0,6],"rest":11,"revers":4,"rout":[6,9,10],"run":[4,6,11,12],"sandbox":7,"send":6,"sent":9,"serv":9,"server":[6,9],"servic":6,"session":9,"setup":[7,11,12],"shutdown":12,"singl":11,"sqlalchemi":12,"sse":9,"start":[6,8],"startup":12,"static":9,"statu":0,"storag":11,"support":9,"task":6,"templat":[6,10],"test":8,"tip":[8,12],"tour":9,"trust":9,"try":11,"tutori":5,"type":6,"unreleas":2,"updat":11,"upload":8,"url":3,"us":[8,12],"user":5,"uvicorn":4,"v0":2,"v1":2,"v2":2,"v3":2,"valid":8,"view":9,"web":6,"websocket":[8,9],"what":[5,11],"world":6,"you":5,"your":[11,12]}})
\ No newline at end of file
+Search.setIndex({"alltitles":{"API Key Authentication":[[11,"api-key-authentication"]],"API Reference":[[0,null]],"Added":[[2,"added"],[2,"id1"],[2,"id4"],[2,"id10"],[2,"id11"],[2,"id14"],[2,"id17"],[2,"id22"],[2,"id29"],[2,"id35"],[2,"id36"],[2,"id37"],[2,"id38"],[2,"id39"],[2,"id42"],[2,"id43"],[2,"id45"],[2,"id48"],[2,"id51"],[2,"id52"],[2,"id53"],[2,"id57"]],"Adding Third-Party Middleware":[[13,"adding-third-party-middleware"]],"After-Request Hooks":[[10,"after-request-hooks"]],"Allowed Hosts":[[5,"allowed-hosts"]],"Authentication":[[11,null]],"Background Queue":[[0,"background-queue"]],"Background Tasks":[[7,"background-tasks"]],"Backlog":[[1,null]],"Bearer Token Authentication":[[11,"bearer-token-authentication"]],"Before-Request Hooks":[[10,"before-request-hooks"]],"Before-Request Hooks for WebSockets":[[16,"before-request-hooks-for-websockets"]],"Blueprints \u2192 Route Groups":[[12,"blueprints-route-groups"]],"Building Frontend Assets":[[3,"building-frontend-assets"]],"Building a REST API":[[14,null]],"Built-in Middleware":[[13,"built-in-middleware"]],"CORS":[[10,"cors"]],"CRUD Endpoints":[[15,"crud-endpoints"]],"Changed":[[2,"changed"],[2,"id2"],[2,"id9"],[2,"id12"],[2,"id15"],[2,"id18"],[2,"id21"],[2,"id26"],[2,"id27"],[2,"id28"],[2,"id30"],[2,"id31"],[2,"id33"],[2,"id34"],[2,"id40"],[2,"id44"],[2,"id49"],[2,"id50"],[2,"id56"]],"Changelog":[[2,null]],"Chat Room":[[16,"chat-room"]],"Class-Based Views":[[10,"class-based-views"]],"Cloud Platforms":[[4,"cloud-platforms"]],"Command Line Interface":[[3,null]],"Configuration":[[5,null]],"Configuration Class Pattern":[[5,"configuration-class-pattern"]],"Cookie-Based Sessions":[[10,"cookie-based-sessions"]],"Cookies":[[10,"cookies"]],"Create a Book":[[14,"create-a-book"]],"Create a Web Service":[[7,"create-a-web-service"]],"Custom Error Handling":[[10,"custom-error-handling"]],"Custom Exception for Auth Errors":[[11,"custom-exception-for-auth-errors"]],"Custom Instance Names":[[3,"custom-instance-names"]],"Data Formats":[[16,"data-formats"]],"Database Setup":[[15,"database-setup"]],"Debug Mode":[[5,"debug-mode"]],"Define Your Models":[[14,"define-your-models"],[15,"define-your-models"]],"Delete a Book":[[14,"delete-a-book"]],"Deployment":[[4,null]],"Deprecated":[[2,"deprecated"]],"Development Sandbox":[[8,null]],"Docker":[[4,"docker"]],"Echo Server":[[16,"echo-server"]],"Environment Variables":[[5,"environment-variables"]],"Error Handling":[[14,"error-handling"]],"Feature Tour":[[10,null]],"Fixed":[[2,"fixed"],[2,"id3"],[2,"id5"],[2,"id6"],[2,"id7"],[2,"id8"],[2,"id13"],[2,"id16"],[2,"id19"],[2,"id20"],[2,"id23"],[2,"id24"],[2,"id25"],[2,"id32"],[2,"id41"],[2,"id46"],[2,"id47"],[2,"id54"],[2,"id55"]],"Future Ideas":[[1,"future-ideas"]],"Get a Single Book":[[14,"get-a-single-book"]],"Getting Started":[[9,"getting-started"]],"Gradual Migration":[[12,"gradual-migration"]],"GraphQL":[[10,"graphql"]],"HSTS":[[10,"hsts"]],"HTML Client":[[16,"html-client"]],"Hello World":[[7,"hello-world"]],"Hooks vs. Middleware":[[13,"hooks-vs-middleware"]],"How WebSockets Work":[[16,"how-websockets-work"]],"In-Memory Storage":[[14,"in-memory-storage"]],"Installation":[[6,"installation"],[15,"installation"]],"JSON APIs":[[12,"json-apis"]],"Launching from a File":[[3,"launching-from-a-file"]],"Launching from a Module":[[3,"launching-from-a-module"]],"Launching from a URL":[[3,"launching-from-a-url"]],"Lifespan Events":[[10,"lifespan-events"]],"Lifespan for Startup and Shutdown":[[15,"lifespan-for-startup-and-shutdown"]],"List All Books":[[14,"list-all-books"]],"MessagePack":[[10,"messagepack"]],"Method Filtering":[[10,"method-filtering"]],"Middleware Order":[[13,"middleware-order"]],"Migrating from Flask":[[12,null]],"Mounting Other Apps":[[10,"mounting-other-apps"]],"OpenAPI Documentation":[[10,"openapi-documentation"]],"Operations":[[8,"operations"]],"Project":[[6,null]],"Project Setup":[[14,"project-setup"]],"Putting It All Together":[[5,"putting-it-all-together"],[7,"putting-it-all-together"]],"Query Dict":[[0,"query-dict"]],"Quick Reference":[[12,"quick-reference"]],"Quick Start":[[7,null]],"Rate Limiter":[[0,"rate-limiter"]],"Rate Limiting":[[10,"rate-limiting"]],"Reading Requests":[[7,"reading-requests"]],"Removed":[[2,"removed"]],"Rendering Templates":[[7,"rendering-templates"]],"Request":[[0,"request"]],"Request ID":[[10,"request-id"]],"Responder":[[6,null]],"Response":[[0,"response"]],"Reverse Proxy":[[4,"reverse-proxy"]],"Route Groups":[[0,"route-groups"],[10,"route-groups"]],"Route Parameters":[[7,"route-parameters"],[12,"route-parameters"]],"Run It":[[14,"run-it"],[15,"run-it"]],"Run the Server":[[7,"run-the-server"]],"Running Locally":[[4,"running-locally"]],"Secret Key":[[5,"secret-key"]],"Sending Responses":[[7,"sending-responses"]],"Server-Sent Events (SSE)":[[10,"server-sent-events-sse"]],"Serving Files":[[10,"serving-files"]],"Setup":[[8,"setup"]],"Skipping Auth for Public Routes":[[11,"skipping-auth-for-public-routes"]],"Static Files":[[10,"static-files"]],"Status Code Helpers":[[0,"status-code-helpers"]],"Templates":[[12,"templates"]],"Testing":[[9,null]],"Testing Before and After Hooks":[[9,"testing-before-and-after-hooks"]],"Testing Error Handling":[[9,"testing-error-handling"]],"Testing File Uploads":[[9,"testing-file-uploads"]],"Testing Headers and Cookies":[[9,"testing-headers-and-cookies"]],"Testing JSON APIs":[[9,"testing-json-apis"]],"Testing Lifespan Events":[[9,"testing-lifespan-events"]],"Testing Request Validation":[[9,"testing-request-validation"]],"Testing WebSockets":[[9,"testing-websockets"],[16,"testing-websockets"]],"The API Class":[[0,"the-api-class"]],"The Big Differences":[[12,"the-big-differences"]],"The Idea":[[6,"the-idea"]],"Tips":[[9,"tips"],[15,"tips"]],"Trusted Hosts":[[10,"trusted-hosts"]],"Try It Out":[[14,"try-it-out"]],"Tutorials":[[6,null]],"Type Convertors":[[7,"type-convertors"]],"Unreleased":[[2,"unreleased"]],"Update a Book":[[14,"update-a-book"]],"User Guide":[[6,null]],"Using .env Files":[[5,"using-env-files"]],"Using Fixtures":[[9,"using-fixtures"]],"Using PostgreSQL":[[15,"using-postgresql"]],"Using SQLAlchemy":[[15,null]],"Using Sessions for Web Apps":[[11,"using-sessions-for-web-apps"]],"Using Starlette Middleware":[[13,"using-starlette-middleware"]],"Uvicorn Directly":[[4,"uvicorn-directly"]],"WebSocket Support":[[10,"websocket-support"]],"WebSocket Tutorial":[[16,null]],"What You Get":[[6,"what-you-get"]],"What\u2019s Next":[[14,"what-s-next"]],"When to Use What":[[13,"when-to-use-what"]],"Writing Middleware":[[13,null]],"[v3.2.0] - 2026-03-22":[[2,"v3-2-0-2026-03-22"]],"v0.0.1 - 2018-10-12":[[2,"v0-0-1-2018-10-12"]],"v0.0.10 - 2018-10-17":[[2,"v0-0-10-2018-10-17"]],"v0.0.2 - 2018-10-13":[[2,"v0-0-2-2018-10-13"]],"v0.0.3 - 2018-10-13":[[2,"v0-0-3-2018-10-13"]],"v0.0.4 - 2018-10-15":[[2,"v0-0-4-2018-10-15"]],"v0.0.5 - 2018-10-15":[[2,"v0-0-5-2018-10-15"]],"v0.0.6 - 2018-10-16":[[2,"v0-0-6-2018-10-16"]],"v0.0.7 - 2018-10-16":[[2,"v0-0-7-2018-10-16"]],"v0.0.8 - 2018-10-17":[[2,"v0-0-8-2018-10-17"]],"v0.0.9 - 2018-10-17":[[2,"v0-0-9-2018-10-17"]],"v0.1.0 - 2018-10-17":[[2,"v0-1-0-2018-10-17"]],"v0.1.1 - 2018-10-17":[[2,"v0-1-1-2018-10-17"]],"v0.1.2 - 2018-10-18":[[2,"v0-1-2-2018-10-18"]],"v0.1.3 - 2018-10-18":[[2,"v0-1-3-2018-10-18"]],"v0.1.4 - 2018-10-19":[[2,"v0-1-4-2018-10-19"]],"v0.1.5 - 2018-10-20":[[2,"v0-1-5-2018-10-20"]],"v0.1.6 - 2018-10-20":[[2,"v0-1-6-2018-10-20"]],"v0.2.0 - 2018-10-22":[[2,"v0-2-0-2018-10-22"]],"v0.2.1 - 2018-10-23":[[2,"v0-2-1-2018-10-23"]],"v0.2.2 - 2018-10-23":[[2,"v0-2-2-2018-10-23"]],"v0.2.3 - 2018-10-24":[[2,"v0-2-3-2018-10-24"]],"v0.3.0 - 2018-10-24":[[2,"v0-3-0-2018-10-24"]],"v0.3.1 - 2018-10-24":[[2,"v0-3-1-2018-10-24"]],"v0.3.2 - 2018-10-25":[[2,"v0-3-2-2018-10-25"]],"v0.3.3 - 2018-10-25":[[2,"v0-3-3-2018-10-25"]],"v1.0.0 - 2018-10-26":[[2,"v1-0-0-2018-10-26"]],"v1.0.1 - 2018-10-26":[[2,"v1-0-1-2018-10-26"]],"v1.0.2 - 2018-10-27":[[2,"v1-0-2-2018-10-27"]],"v1.0.3 - 2018-10-27":[[2,"v1-0-3-2018-10-27"]],"v1.0.4 - 2018-10-27":[[2,"v1-0-4-2018-10-27"]],"v1.0.5- 2018-10-27":[[2,"v1-0-5-2018-10-27"]],"v1.1.0 - 2018-10-27":[[2,"v1-1-0-2018-10-27"]],"v1.1.1 - 2018-10-29":[[2,"v1-1-1-2018-10-29"]],"v1.1.2 - 2018-11-11":[[2,"v1-1-2-2018-11-11"]],"v1.1.3 - 2019-01-12":[[2,"v1-1-3-2019-01-12"]],"v1.2.0 - 2018-12-29":[[2,"v1-2-0-2018-12-29"]],"v1.3.0 - 2019-02-22":[[2,"v1-3-0-2019-02-22"]],"v1.3.1 - 2019-04-28":[[2,"v1-3-1-2019-04-28"]],"v1.3.2 - 2019-08-15":[[2,"v1-3-2-2019-08-15"]],"v2.0.0 - 2019-09-19":[[2,"v2-0-0-2019-09-19"]],"v2.0.1 - 2019-09-20":[[2,"v2-0-1-2019-09-20"]],"v2.0.2 - 2019-09-20":[[2,"v2-0-2-2019-09-20"]],"v2.0.3 - 2019-09-20":[[2,"v2-0-3-2019-09-20"]],"v2.0.4 - 2019-11-19":[[2,"v2-0-4-2019-11-19"]],"v2.0.5 - 2019-12-15":[[2,"v2-0-5-2019-12-15"]],"v3.0.0 - 2026-03-22":[[2,"v3-0-0-2026-03-22"]]},"docnames":["api","backlog","changes","cli","deployment","guide-config","index","quickstart","sandbox","testing","tour","tutorial-auth","tutorial-flask","tutorial-middleware","tutorial-rest","tutorial-sqlalchemy","tutorial-websockets"],"envversion":{"sphinx":65,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1},"filenames":["api.rst","backlog.md","changes.md","cli.rst","deployment.rst","guide-config.rst","index.rst","quickstart.rst","sandbox.md","testing.rst","tour.rst","tutorial-auth.rst","tutorial-flask.rst","tutorial-middleware.rst","tutorial-rest.rst","tutorial-sqlalchemy.rst","tutorial-websockets.rst"],"indexentries":{"accepts() (responder.request method)":[[0,"responder.Request.accepts",false]],"add_event_handler() (responder.api method)":[[0,"responder.API.add_event_handler",false]],"add_middleware() (responder.api method)":[[0,"responder.API.add_middleware",false]],"add_route() (responder.api method)":[[0,"responder.API.add_route",false]],"after_request() (responder.api method)":[[0,"responder.API.after_request",false]],"api (class in responder)":[[0,"responder.API",false]],"apparent_encoding (responder.request property)":[[0,"responder.Request.apparent_encoding",false]],"backgroundqueue (class in responder.background)":[[0,"responder.background.BackgroundQueue",false]],"before_request() (responder.api method)":[[0,"responder.API.before_request",false]],"check() (responder.ext.ratelimit.ratelimiter method)":[[0,"responder.ext.ratelimit.RateLimiter.check",false]],"client (responder.request property)":[[0,"responder.Request.client",false]],"content (responder.request property)":[[0,"responder.Request.content",false]],"cookies (responder.request property)":[[0,"responder.Request.cookies",false]],"encoding (responder.request property)":[[0,"responder.Request.encoding",false]],"exception_handler() (responder.api method)":[[0,"responder.API.exception_handler",false]],"file() (responder.response method)":[[0,"responder.Response.file",false]],"full_url (responder.request property)":[[0,"responder.Request.full_url",false]],"get() (responder.models.querydict method)":[[0,"responder.models.QueryDict.get",false]],"get_list() (responder.models.querydict method)":[[0,"responder.models.QueryDict.get_list",false]],"graphql() (responder.api method)":[[0,"responder.API.graphql",false]],"group() (responder.api method)":[[0,"responder.API.group",false]],"headers (responder.request property)":[[0,"responder.Request.headers",false]],"install() (responder.ext.ratelimit.ratelimiter method)":[[0,"responder.ext.ratelimit.RateLimiter.install",false]],"is_100() (in module responder.status_codes)":[[0,"responder.status_codes.is_100",false]],"is_200() (in module responder.status_codes)":[[0,"responder.status_codes.is_200",false]],"is_300() (in module responder.status_codes)":[[0,"responder.status_codes.is_300",false]],"is_400() (in module responder.status_codes)":[[0,"responder.status_codes.is_400",false]],"is_500() (in module responder.status_codes)":[[0,"responder.status_codes.is_500",false]],"is_json (responder.request property)":[[0,"responder.Request.is_json",false]],"is_secure (responder.request property)":[[0,"responder.Request.is_secure",false]],"items() (responder.models.querydict method)":[[0,"responder.models.QueryDict.items",false]],"items_list() (responder.models.querydict method)":[[0,"responder.models.QueryDict.items_list",false]],"media() (responder.request method)":[[0,"responder.Request.media",false]],"method (responder.request property)":[[0,"responder.Request.method",false]],"mimetype (responder.request property)":[[0,"responder.Request.mimetype",false]],"module":[[0,"module-responder",false]],"mount() (responder.api method)":[[0,"responder.API.mount",false]],"ok (responder.response property)":[[0,"responder.Response.ok",false]],"on_event() (responder.api method)":[[0,"responder.API.on_event",false]],"params (responder.request property)":[[0,"responder.Request.params",false]],"path_matches_route() (responder.api method)":[[0,"responder.API.path_matches_route",false]],"path_params (responder.request property)":[[0,"responder.Request.path_params",false]],"querydict (class in responder.models)":[[0,"responder.models.QueryDict",false]],"ratelimiter (class in responder.ext.ratelimit)":[[0,"responder.ext.ratelimit.RateLimiter",false]],"redirect() (responder.api method)":[[0,"responder.API.redirect",false]],"redirect() (responder.response method)":[[0,"responder.Response.redirect",false]],"request (class in responder)":[[0,"responder.Request",false]],"requests (responder.api property)":[[0,"responder.API.requests",false]],"responder":[[0,"module-responder",false]],"response (class in responder)":[[0,"responder.Response",false]],"route() (responder.api method)":[[0,"responder.API.route",false]],"routegroup (class in responder.api)":[[0,"responder.api.RouteGroup",false]],"run() (responder.api method)":[[0,"responder.API.run",false]],"run() (responder.background.backgroundqueue method)":[[0,"responder.background.BackgroundQueue.run",false]],"schema() (responder.api method)":[[0,"responder.API.schema",false]],"serve() (responder.api method)":[[0,"responder.API.serve",false]],"session (responder.request property)":[[0,"responder.Request.session",false]],"session() (responder.api method)":[[0,"responder.API.session",false]],"set_cookie() (responder.response method)":[[0,"responder.Response.set_cookie",false]],"sse() (responder.response method)":[[0,"responder.Response.sse",false]],"state (responder.request property)":[[0,"responder.Request.state",false]],"static_app (responder.api property)":[[0,"responder.API.static_app",false]],"status_code_safe (responder.response property)":[[0,"responder.Response.status_code_safe",false]],"stream() (responder.response method)":[[0,"responder.Response.stream",false]],"stream_file() (responder.response method)":[[0,"responder.Response.stream_file",false]],"task() (responder.background.backgroundqueue method)":[[0,"responder.background.BackgroundQueue.task",false]],"template() (responder.api method)":[[0,"responder.API.template",false]],"template_string() (responder.api method)":[[0,"responder.API.template_string",false]],"text (responder.request property)":[[0,"responder.Request.text",false]],"url (responder.request property)":[[0,"responder.Request.url",false]],"url_for() (responder.api method)":[[0,"responder.API.url_for",false]]},"objects":{"":[[0,0,0,"-","responder"]],"responder":[[0,1,1,"","API"],[0,1,1,"","Request"],[0,1,1,"","Response"]],"responder.API":[[0,2,1,"","add_event_handler"],[0,2,1,"","add_middleware"],[0,2,1,"","add_route"],[0,2,1,"","after_request"],[0,2,1,"","before_request"],[0,2,1,"","exception_handler"],[0,2,1,"","graphql"],[0,2,1,"","group"],[0,2,1,"","mount"],[0,2,1,"","on_event"],[0,2,1,"","path_matches_route"],[0,2,1,"","redirect"],[0,3,1,"","requests"],[0,2,1,"","route"],[0,2,1,"","run"],[0,2,1,"","schema"],[0,2,1,"","serve"],[0,2,1,"","session"],[0,3,1,"","static_app"],[0,2,1,"","template"],[0,2,1,"","template_string"],[0,2,1,"","url_for"]],"responder.Request":[[0,2,1,"","accepts"],[0,3,1,"","apparent_encoding"],[0,3,1,"","client"],[0,3,1,"","content"],[0,3,1,"","cookies"],[0,3,1,"","encoding"],[0,3,1,"","full_url"],[0,3,1,"","headers"],[0,3,1,"","is_json"],[0,3,1,"","is_secure"],[0,2,1,"","media"],[0,3,1,"","method"],[0,3,1,"","mimetype"],[0,3,1,"","params"],[0,3,1,"","path_params"],[0,3,1,"","session"],[0,3,1,"","state"],[0,3,1,"","text"],[0,3,1,"","url"]],"responder.Response":[[0,2,1,"","file"],[0,3,1,"","ok"],[0,2,1,"","redirect"],[0,2,1,"","set_cookie"],[0,2,1,"","sse"],[0,3,1,"","status_code_safe"],[0,2,1,"","stream"],[0,2,1,"","stream_file"]],"responder.api":[[0,1,1,"","RouteGroup"]],"responder.background":[[0,1,1,"","BackgroundQueue"]],"responder.background.BackgroundQueue":[[0,2,1,"","run"],[0,2,1,"","task"]],"responder.ext.ratelimit":[[0,1,1,"","RateLimiter"]],"responder.ext.ratelimit.RateLimiter":[[0,2,1,"","check"],[0,2,1,"","install"]],"responder.models":[[0,1,1,"","QueryDict"]],"responder.models.QueryDict":[[0,2,1,"","get"],[0,2,1,"","get_list"],[0,2,1,"","items"],[0,2,1,"","items_list"]],"responder.status_codes":[[0,4,1,"","is_100"],[0,4,1,"","is_200"],[0,4,1,"","is_300"],[0,4,1,"","is_400"],[0,4,1,"","is_500"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","function","Python function"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:function"},"terms":{"":[0,2,3,4,5,6,7,9,10,11,12,13,15,16],"0":[3,4,5,7,10,14,15],"00":0,"01":0,"0441172719":14,"1":[0,3,4,5,7,10,11,12,14,16],"10":[0,6,7,10],"100":[0,2,10],"127":[3,4,5],"13":4,"150mb":4,"1965":14,"2":[4,7,9,10,14,15,16],"200":[0,7,9,10,14],"201":[7,9,12,14,15],"2026":0,"204":[14,15],"24":11,"2xx":0,"3":[4,6,7,9,10,14,16],"3000":5,"301":[0,7],"308":2,"32":5,"3600":[0,10],"3f":13,"4":[4,7],"40":2,"400":[0,9,10,14],"401":[0,10,11],"404":[0,7,12,14,15],"41d4":7,"422":[2,9,10,14],"429":[0,10],"446655440000":7,"500":[2,7,9,10,13,14],"5000":10,"5042":[3,4,7,11,14,16],"550e8400":7,"561":2,"60":[0,2,10],"600":[0,10],"7":11,"8":0,"80":4,"8000":[4,7],"8192":0,"9":9,"978":14,"99":9,"A":[0,4,5,6,7,10,13,16],"And":[5,6,7],"As":[0,10],"But":[7,9,10],"By":[3,7,9,10,13,14],"For":[0,3,4,5,7,9,10,11,14,15],"If":[0,3,5,6,7,9,10,12,15],"In":[0,5,6,7,10,11,12,15,16],"It":[0,3,6,9,10,13],"No":[6,9,10,12,14],"Not":7,"On":[5,10],"One":[6,7,9],"Or":[0,5],"That":[4,6,7,9],"The":[2,3,4,5,7,9,10,11,13,14,15,16],"There":[9,10],"These":7,"To":[14,15],"Will":0,"With":[0,5],"__init__":11,"__main__":[4,6,7,9,14,15],"__name__":[4,6,7,9,10,12,14,15],"__tablename__":15,"_route_for":2,"a2wsgi":2,"a716":7,"abc":14,"abc123":[0,9,10,11],"abil":2,"abl":0,"abort":12,"about":[4,7,9,12],"abov":[6,11],"abstract":15,"accept":[0,2,7,9,10,16],"access":[0,4,10,11,15],"acm":3,"acquir":8,"across":[4,10,15],"activ":[8,16],"actual":[0,7,10],"ad":[0,1,6,10],"adapt":10,"add":[0,1,2,5,7,11,13,14,15,16],"add_event_handl":0,"add_head":[10,13],"add_middlewar":[0,13],"add_request_id":0,"add_rout":[0,10],"add_tim":[9,10],"add_vers":9,"addeventlisten":16,"addit":[0,13],"address":[0,5,7,10],"adher":2,"admin":11,"affect":10,"after":[0,6,7,11,15,16],"after_request":[0,1,2,9,10,13],"ag":[0,10],"again":11,"against":[0,4,10,11],"agre":7,"ai":10,"aiosqlit":15,"alemb":15,"algorithm":11,"alias":2,"alic":10,"all":[0,2,3,4,6,8,9,10,12,13,15,16],"alloc":9,"allow":[6,10],"allow_credenti":[0,10],"allow_head":[0,10],"allow_method":[0,5,10],"allow_origin":[0,5,10],"allow_origin_regex":0,"allowed_host":[0,5,10],"alongsid":10,"alpin":4,"alreadi":[0,9,10],"also":[0,2,3,7,9,10],"altern":[0,10],"alwai":[4,5,6,7,10],"an":[0,3,5,7,10,12,13,14,15,16],"analysi":10,"angle_bracket":12,"angular":10,"ani":[0,3,4,5,6,7,9,10,11,13,14,15,16],"annot":[6,7,10],"annual":[7,10],"answer":[10,16],"anyon":6,"anyth":7,"anywher":[4,10],"api":[2,3,4,5,6,7,10,13,15,16],"api_kei":11,"api_v1":12,"apispec":2,"app":[0,2,3,4,5,6,7,9,12,14,15,16],"appar":0,"apparent_encod":0,"appear":10,"appendchild":16,"appli":[0,13],"applic":[0,2,3,4,5,7,9,10,11,13,14,15],"approach":[5,10,11,12],"appropri":[10,15],"ar":[0,2,3,4,6,7,9,10,11,12,13,15,16],"architectur":10,"aren":[7,13],"arg":[0,12],"argument":[0,2,7,12,13],"around":[0,6],"arriv":[7,12],"asgi":[0,2,4,6,7,10,12,13],"ask":[6,10,16],"assert":[9,16],"asset":[0,4,6,10],"assign":14,"associ":0,"async":[0,1,2,4,6,7,9,10,11,12,13,14,15,16],"async_sess":15,"async_sessionmak":15,"asynccontextmanag":[10,15],"asynchron":[2,4],"asyncio":15,"asyncpg":15,"attach":10,"attack":[4,5,10],"attribut":[0,3,15],"audio":16,"auth":[6,10,13],"auth_check":10,"auth_guard":11,"authent":[6,7,10,13,16],"autherror":11,"author":[0,7,8,10,11,13,14,15],"auto":[2,10,16],"auto_escap":0,"autobuild":8,"autoincr":15,"automat":[0,2,4,5,6,7,9,10,11,12,13,14,15],"avail":[0,7],"avoid":[9,10],"aw":4,"await":[0,2,7,9,10,11,12,13,14,15,16],"azur":[3,4],"b":[7,9,13,15,16],"back":[0,7,10,15,16],"background":[2,6],"backgroundqueu":0,"backlog":6,"bad":[9,10,14],"balanc":4,"bandwidth":10,"bar":10,"base":[0,2,6,11,13,15],"base_url":0,"basehttpmiddlewar":13,"basemodel":[0,9,10,14,15],"basic":7,"batteri":6,"battl":6,"bearer":6,"becaus":[0,6,7,11,12,14],"becom":[0,15],"been":0,"befor":[0,2,6,7,11,13,15],"before_request":[0,1,2,9,10,11,12,13,16],"begin":15,"behavior":[5,13],"behind":[4,10],"being":0,"best":6,"better":6,"between":13,"bidirect":[10,16],"big":6,"biggest":12,"bin":8,"binari":[7,10,16],"bind":[0,4,5,7],"blob":3,"block":[0,7,9,10,12,16],"blueprint":6,"bodi":[0,2,7,9,10,12,14,16],"boilerpl":6,"book":[6,15],"book_id":[14,15],"bookin":[14,15],"bookout":15,"books_db":14,"bool":5,"born":6,"both":[6,7,10,12,16],"bottom":14,"box":13,"bp":12,"break":9,"bring":6,"broadcast":16,"broke":[7,9],"broker":7,"browser":[7,8,10,11,14,16],"bucket":[0,10],"buffer":[0,7],"bufix":2,"bug":[2,9],"bugfix":2,"build":[2,4,6,7,8,10,11,15,16],"builder":15,"built":[2,4,6,7,9,10,16],"bump":2,"bypass":4,"byte":[0,2,6,7,13,16],"c":[5,16],"cach":[7,9,10],"caddi":4,"call":[3,7,9,10,13,14,16],"call_next":13,"callabl":0,"came":7,"can":[0,2,3,4,5,6,7,9,10,11,12,13,14,15,16],"carri":7,"case":[0,6,7,13],"cat":10,"catalog":14,"catch":[10,11,13],"categori":0,"cd":8,"ceas":2,"celeri":7,"cell":10,"central":[0,7],"certain":0,"certif":4,"chang":[0,15],"changelog":6,"channel":[10,16],"chardet":0,"chat":[6,10],"check":[0,8,9,10,11,13,16],"check_api_kei":11,"check_auth":0,"check_exist":[0,10,14,15],"choic":[7,10],"choos":10,"chunk":[0,10],"chunk_siz":0,"ci":2,"circuit":[0,1,2,6,10],"class":[2,6,7,9,11,13,14,15],"clean":[6,10,14],"cleaner":[10,11],"cleanli":5,"cleanup":10,"clear":11,"cli":2,"click":6,"client":[0,2,6,7,9,10,11,14],"clone":8,"close":[9,10,15,16],"close_database_connection_pool":0,"cloud":[3,5,6,7],"cmd":4,"code":[2,3,4,5,6,7,8,9,10,11,14,15,16],"collabor":[10,16],"colon":3,"column":15,"com":[0,3,5,7,8,10],"combin":7,"come":[7,10,12],"command":[4,6],"commit":[5,15],"common":[3,4,5,7,10,11,12,13,14],"commonli":7,"commun":[10,11],"compact":10,"compat":[0,2],"complement":[1,10],"complet":[7,10,14,16],"complex":[13,15],"compon":13,"compos":15,"compress":[6,7,13],"compromis":5,"comput":7,"concept":[2,10,12],"concern":[7,13],"concurr":[0,4],"condit":[9,10],"config":[5,15],"configur":[0,2,4,6,7,9,10,13],"conflict":2,"confus":9,"conn":15,"connect":[0,4,7,9,10,15,16],"consid":[1,7,15],"consist":[7,10],"consol":[10,15],"const":[10,16],"constructor":[0,13],"consum":10,"contact":0,"contain":[0,4,7],"container":4,"content":[0,2,6,7,9,10,14],"content_typ":[0,9,10],"context":[2,6,9,10,15,16],"contextlib":[10,15],"continu":7,"contract":9,"control":[0,4,5,7,10,11,15],"conveni":[0,5,15],"convent":[4,14],"convert":[2,7],"convertor":[2,6,12],"cooki":[0,2,5,6,7,11,12,13],"copi":4,"cor":[0,2,5,6,9,13],"core":[2,4],"coroutin":0,"correctli":4,"correspond":15,"cors_en":5,"cors_origin":5,"cors_param":[0,5,10],"corsmiddlewar":13,"count":2,"cover":[5,11,13],"cpu":[0,4,7],"crash":2,"creat":[0,6,8,9,10,11,12,15],"create_al":15,"create_asgi_app":10,"create_async_engin":15,"create_book":[14,15],"create_connect":16,"create_item":[0,10,12],"create_pet":10,"create_token":11,"createel":16,"creation":15,"credenti":11,"cross":[10,13],"crud":[6,7,14],"css":10,"csv":10,"curl":[3,7,11,14],"curly_brac":12,"custom":[0,2,6,7,9,14,16],"cut":13,"cycl":7,"d":[7,9,14],"dashboard":[5,10,11,16],"data":[0,2,5,6,7,9,10,11,12,14,15],"databas":[5,6,7,9,10,11,14],"database_url":[5,15],"date":0,"datetim":11,"db":[5,15],"debug":[0,2,6,10,12],"decim":7,"declar":[2,7],"declarativebas":15,"decod":[2,10,11],"decor":[0,2,7,10],"deep":6,"def":[0,6,7,9,10,11,12,13,14,15,16],"def456":11,"default":[0,1,2,3,5,7,9,10,15],"default_valu":[0,10],"defend":10,"defin":[0,6,7,10,11],"definit":0,"del":14,"delet":[5,6,7,10,15],"delete_book":[14,15],"deliber":7,"demo":3,"denpend":2,"dep":2,"depend":[2,4,9,13],"deploi":[4,6],"deploy":6,"depth":10,"describ":10,"descript":[0,10],"design":[7,10,14],"detail":[4,5,10,14],"detect":[0,6,10],"dev":[2,5],"develop":[3,4,5,10,15],"devic":10,"dict":[6,7,10,11,14,15],"dictionari":0,"differ":[0,3,5,6,9,10,15],"digit":7,"direct":[0,2,10],"directli":[3,6,7,9,14,15],"directori":[0,7,10],"disabl":[9,15],"discard":16,"disconnect":16,"disk":[0,7,10],"dispatch":[2,10,13],"dispos":15,"distribut":[4,10],"div":16,"django":[6,10],"do":[4,5,7,10],"doc":[8,9,10,11,14],"docker":[5,6],"dockerfil":4,"docs_rout":[0,10,14],"docstr":10,"doctyp":16,"document":[0,2,4,6,8,14,16],"doe":15,"doesn":[0,10,14,15],"domain":[0,5,10],"don":[5,7,9,10,11,12,15],"done":4,"dot":3,"dotenv":5,"doubl":14,"down":[10,15],"download":10,"dramat":2,"driver":15,"drop":[2,16],"dump":2,"dune":14,"duplex":10,"duplic":10,"durat":13,"dure":[3,4,9,15],"duti":14,"dynam":7,"e":[0,16],"e29b":7,"each":[0,4,9,10,15],"easi":[3,5,7],"echo":[6,7,15],"ecosystem":[6,7],"edit":8,"editor":[10,16],"effect":[9,14],"effici":4,"either":[0,16],"element":[0,10],"els":[7,10,11,12,15],"email":7,"emb":10,"empti":[0,14],"enabl":[2,10,13,14],"enable_async":7,"enable_hst":[0,10,13],"encod":[0,2,11],"encrypt":11,"end":[7,14],"endpoint":[0,2,6,9,10,11,12,14],"enforc":10,"engin":[7,15],"enough":[4,13],"ensur":[10,14,15,16],"enter":[9,16],"entir":[0,10,11,13,14],"entri":14,"env":[4,6],"environ":[0,3,4,6,7],"eol":2,"equival":12,"error":[0,2,5,6,15],"errorhandl":12,"escap":0,"essenti":10,"establish":16,"etc":[7,9,10],"even":[3,4,10,16],"event":[0,2,6,7,12,16],"event_typ":0,"eventsourc":10,"ever":[5,6],"everi":[0,4,5,6,7,9,10,11,13,14,15],"everyth":[0,2,4,7,10,12,14,15,16],"everywher":[3,5],"evolv":15,"exactli":[9,10],"exampl":[0,3,5,7,10,11],"exc":[0,9,10,11,14],"exceed":[0,10],"except":[0,2,6,9,10,13,16],"exception_cl":0,"exception_handl":[0,2,9,10,11,12,14],"exceptionmiddlewar":13,"execut":15,"exist":[0,2,6,12,13],"exit":[9,10,16],"exp":11,"expect":7,"expir":[0,10,11],"expire_on_commit":15,"explain":10,"explan":0,"explicit":[7,10,12],"explicitli":[10,12],"explor":[1,7,10],"export":[5,10],"expos":[4,5],"expose_head":0,"express":[6,7],"ext":[0,2,10,15],"extens":[2,7],"extern":7,"extra":[2,4,10],"extract":0,"extrem":7,"f":[0,6,7,9,10,11,13,14,16],"factori":15,"fail":[9,10,15],"failur":[2,9],"falcon":[6,10],"fall":0,"fals":[0,5,9,10,14,15],"familiar":[6,12],"fast":[4,6,7,9],"fastapi":6,"faster":10,"featur":[0,6,7],"feed":10,"feel":6,"fetch":3,"few":12,"fido":10,"field":[0,2,9,10,14],"figur":7,"file":[0,2,4,6,7,14,15,16],"filenam":[0,9],"filepath":7,"filesystem":[2,3],"fill":7,"filter":[6,7],"final":16,"find":[3,10,12],"fine":[4,10,15],"fire":[0,9],"first":[0,10,11,13,14,15],"fit":6,"fix":8,"fixtur":6,"flag":[4,10,15],"flask":[6,10],"flask_app":[10,12],"flatten":2,"float":[0,7,9],"flow":[11,13],"fly":4,"follow":7,"font":10,"forg":[5,10],"forget":0,"form":[0,7,11,12],"format":[0,2,6,7,8,10,11],"formerli":10,"forward":[4,10],"found":[2,7,14,15],"foundat":10,"fragment":3,"framework":[6,10],"franca":7,"frank":14,"freeli":16,"fresh":9,"from":[0,2,4,5,6,7,9,10,11,13,14,15,16],"from_attribut":15,"front":4,"frontend":[6,10],"fsspec":3,"full":[0,2,3,7,10],"full_url":[0,7,10],"fulli":7,"fun":6,"func":0,"function":[0,7,10,12,13],"futur":[0,6],"g":0,"game":[10,16],"gatewai":4,"gener":[0,2,5,6,9,10,13,14],"get":[0,2,5,7,10,11,15],"get_book":[14,15],"get_list":0,"get_m":11,"get_us":[0,10,12],"getelementbyid":16,"getenv":5,"git":[5,8],"github":[3,8],"gitignor":5,"give":[0,9,10,13,15],"given":[0,9],"global":[12,14],"gmt":0,"go":[7,12],"goe":[10,12],"good":[4,6,10],"googl":[3,4],"grace":4,"grade":7,"gradual":[6,10],"graduat":13,"grain":15,"graphen":[0,2,6,10],"graphiql":[2,6,10],"graphql":[0,2,6],"great":[3,6,10],"greet":[6,7,10],"greet_world":6,"greetingresourc":10,"group":[2,6],"grow":10,"guard":[6,11],"guid":[5,7,11,12,15],"guido":7,"gzip":[6,7],"gzipmiddlewar":13,"h":[7,11,14],"h1":[7,11],"ha":[7,9,12,14,16],"half":7,"halv":10,"hand":0,"handl":[0,2,4,5,6,7,11,16],"handle_auth_error":11,"handle_value_error":[0,10,14],"handler":[0,6,9,10,11,13,14],"handshak":16,"happen":[6,7],"hard":9,"hasn":0,"have":[0,2,3,7,9,12,13,14],"hdf":3,"head":3,"header":[0,2,4,5,6,7,10,11,12,13,16],"health":11,"heavi":7,"hello":[0,3,6,9,10,12,16],"hello_html":7,"hello_json":7,"hello_to":7,"hello_world":7,"helloworld":3,"help":[0,15],"helper":[6,11],"herbert":14,"here":[4,5,7,10,12,16],"high":[4,10],"hold":[0,6,7],"home":6,"honor":[4,7],"hood":10,"hook":[0,1,2,6,7,11],"host":[0,2,4,6,7,13],"hostnam":10,"hour":[10,11],"how":[5,6,7,10,11,12,14,15],"hs256":11,"hst":6,"html":[0,2,6,7,10,11,12],"http":[0,2,3,4,5,6,7,8,9,10,11,14,16],"httponli":[0,10],"httpsredirect":0,"httpsredirectmiddlewar":[0,4,13],"httpx":[2,9],"i":[0,2,3,4,5,6,7,9,10,11,12,13,14,15,16],"id":[0,2,6,7,12,13,14,15,16],"idea":12,"idempot":14,"ident":12,"identifi":10,"imag":[4,7,10,16],"immedi":[0,6,7,10],"immut":2,"imperson":5,"implement":9,"import":[0,2,3,5,6,7,9,10,11,12,13,14,15,16],"improv":2,"inaccess":0,"includ":[3,4,6,7,8,9,10,11,14],"incom":[0,7,14],"incredibli":10,"independ":4,"index":[0,7,10,16],"individu":10,"industri":10,"info":[0,2,10],"inform":[0,14],"infrastructur":[4,6],"inherit":[0,10],"initi":[0,9],"inject":10,"innermost":13,"input":[2,9,10,14,16],"insensit":[0,6,7],"insid":13,"inspect":0,"instal":[0,2,3,4,5,8,10,11,16],"instanc":[0,4,6,7,9],"instant":7,"instead":[0,7,9,10,13,14,15,16],"int":[0,2,7,10,11,12,14,15],"integ":[0,7,14,15],"integr":[13,14,15],"intens":7,"intent":6,"intention":10,"interact":[2,10,14],"interfac":[4,6,7,12],"intern":[9,10,13],"invalid":[5,9,10,11],"invalidtokenerror":11,"involv":0,"io":4,"iot":10,"ip":[0,7,10],"is_100":0,"is_200":0,"is_300":0,"is_400":0,"is_500":0,"is_class_bas":2,"is_json":[0,2,7],"is_secur":[0,7],"isbn":[14,15],"isn":[7,10,12],"issu":[2,11],"item":[0,9,10,12,15],"itemin":0,"itemout":0,"items_list":0,"iter":16,"its":[0,2,4,14,15],"itself":11,"j":10,"jan":0,"javascript":[0,3,10],"jinja2":[0,7,12],"jpeg":10,"jpg":10,"json":[0,3,6,7,10,11,14,16],"jsonifi":12,"jump":7,"just":[4,9,10,13,15,16],"jwt":11,"keep":[2,5,7,9,10,13,15,16],"kei":[0,6,7,9,10,12,15,16],"kennethreitz":[3,8],"keypress":16,"keyword":[0,7,12,13],"know":[5,7],"known":[0,11],"kwarg":0,"languag":10,"larg":[0,2,10],"larger":[5,9,13],"last":[0,13],"later":[7,9],"launch":[4,6],"layer":13,"lazi":2,"lazili":0,"lead":9,"learn":10,"legaci":12,"less":10,"let":[3,4,7,10,14,15],"level":[4,13],"librari":[9,10,16],"licens":0,"lifecycl":16,"lifespan":[0,2,6,14],"lift":7,"lightn":4,"lightweight":7,"like":[4,7,9,10,11,13,14,16],"limit":[1,2,4,6,13,15,16],"line":[6,7],"liner":2,"lingua":7,"list":[0,6,7,9,10,15],"list_book":[14,15],"list_item":10,"list_pet":10,"list_us":[0,10,12],"list_users_v2":10,"live":[10,16],"ll":[6,7,10,12,14,15],"load":[0,2,3,4,5,7,10],"load_dotenv":5,"local":[3,5,6,12],"localhost":[5,7,11,14,15,16],"locat":[0,7,11,12],"log":[4,10,11,13],"log_level":2,"log_request":13,"log_respons":10,"logic":[6,7,10],"login":[10,11],"logout":11,"long":7,"look":[3,6,7],"loop":[7,12,16],"lose":[2,15],"lower":[0,5],"lowercas":7,"machin":10,"made":[0,6],"magic":12,"mai":4,"main":3,"make":[4,5,7,9,10,11,12],"malici":10,"manag":[2,5,6,9,10,11,13,15,16],"mani":[0,4,7,10],"manual":0,"map":[2,7,10,12,15],"mapper":15,"marimo":10,"mark":[2,10],"marker":2,"marshmallow":[0,2,10],"match":[0,5,7,10,14],"matter":10,"max_ag":[0,10],"maximum":0,"me":11,"mean":[4,7,12],"mechan":10,"media":[0,2,6,7,9,10,11,12,14,15],"memori":[0,2,6,7,10],"messag":[0,7,10,11,16],"messagepack":[2,6],"metadata":[0,7,15],"method":[0,6,7,9,11,12,13,14,15,16],"microservic":10,"middelwar":0,"middlewar":[0,1,2,6,7,10],"middleware_cl":0,"middleware_config":0,"might":7,"migrat":[2,6,10,15],"mime":0,"mimetyp":[0,7,10],"min":[0,10],"mind":13,"minim":[4,16],"minimum":2,"minor":2,"miss":[2,9,10,11],"mix":10,"ml":10,"mode":[0,6,8],"model":[0,6,9,10],"model_dump":15,"model_valid":15,"modern":[4,10,11,12,15],"modifi":[0,10,13,14,15],"modul":[0,2,6,7,10],"more":[0,3,4,7,10,11,12,13,14,16],"most":[0,3,4,5,7,9,10,11,13,15],"mount":[0,2,6,12],"move":[2,7,10,12],"msgpack":[2,10],"much":[7,10],"multi":0,"multipart":[2,7],"multipl":[2,4,10,14],"multiplay":[10,16],"must":[0,7,10,11,14,16],"mutabl":6,"mutat":[0,7,12],"my_templ":7,"myapi":4,"myapp":3,"mydb":15,"mysql":15,"n":[0,7],"name":[0,5,6,7,9,10,12],"namespac":2,"nativ":12,"natur":[9,15],"nearli":12,"need":[4,5,6,7,9,10,11,12,13,15,16],"negoti":[0,6,7,9,10],"nervou":7,"network":9,"never":[5,10,11,14],"new":[0,7,9,10,12,14,16],"new_endpoint":12,"next":[6,13],"next_id":14,"nginx":4,"non":7,"none":[0,2,11,14,15],"normal":[10,16],"notabl":2,"notasecret":0,"note":[7,10,12,14],"notebook":10,"notic":7,"notif":10,"now":[2,7,10,11,15,16],"npm":3,"nullabl":15,"number":[0,7],"o":[0,5,7,12],"object":[0,2,7,10,12,13,15,16],"objecttyp":[0,10],"obviou":9,"off":[5,7],"offload":4,"offset":15,"often":[3,7,10],"ok":[0,7,9,14],"old":12,"on_delet":10,"on_ev":[0,9,10],"on_get":[6,10],"on_post":[6,10],"on_put":10,"on_request":[6,10],"on_startup":9,"onc":[10,12,14],"one":[0,2,5,6,7,10,12,15],"ones":[6,7,10],"onion":13,"onli":[0,5,7,10,12,14],"onmessag":[10,16],"open":[2,7,8,15],"open_database_connection_pool":0,"openapi":[0,2,6,14],"openapi_rout":0,"openapi_them":0,"oper":[7,10,12,14],"opt":10,"option":[0,2,4,6,7,13],"option1":13,"option2":13,"order":[0,6],"order_bi":15,"organ":[0,5,9,10,12],"origin":10,"orm":15,"other":[0,2,6,7,15],"otherwis":[0,10],"our":14,"out":[5,6,7,13],"outermost":13,"outgo":7,"output":14,"outsid":13,"over":[0,6,7,10,12,15],"overal":2,"overhead":9,"overrid":[0,10],"overwhelm":10,"own":[4,10,15],"p":[4,11,16],"pace":10,"packag":[2,3,4],"page":[0,2,5,7,10,11,16],"pagin":7,"painless":9,"pair":0,"param":[0,2,6,7,12],"paramet":[0,6,9,10,14],"parameter":0,"pars":[0,2,7,9,10],"parser":2,"part":[2,6,7,10],"parti":6,"pass":[0,7,9,12,13,15,16],"passion":6,"password":11,"patch":[7,10],"path":[0,2,3,6,7,9,10,11,13],"path_matches_rout":0,"path_param":[0,2,7],"pattern":[4,6,7,10,11,12,14,15],"payload":11,"pdf":[7,9,10],"pep":2,"per":[0,4,9,10],"perfect":10,"period":[0,2,10],"perman":7,"permit":10,"persist":[0,10,16],"person":6,"pet":[0,10],"petin":10,"petout":10,"petschema":[0,10],"photo":10,"piec":10,"pin":2,"pip":[2,3,4,5,6,8,11,15,16],"place":[5,10],"placehold":[7,16],"plain":[0,7,10,16],"platform":[2,5,6,7],"pleasant":6,"plot":10,"plu":15,"plugin":2,"poethepoet":2,"point":[3,4,14],"polici":10,"pool":[0,6,7,10,12,15],"popular":[7,10,15],"port":[0,4,5,7,9],"portion":0,"possibl":7,"post":[0,5,7,9,10,11,12,14,15],"postgresql":6,"potenti":2,"power":[6,9,10,13,15,16],"practic":10,"prefer":10,"prefix":[0,2,10],"present":7,"prevent":[5,10],"price":[0,9],"primari":0,"primary_kei":15,"print":[0,5,10,13,15,16],"privat":10,"proce":16,"process":[4,6,7,9,10,13],"process_data":7,"prod":5,"produc":[0,7],"product":[2,3,4,5,6,7,9,10,11,15],"profil":10,"programmat":10,"progress":10,"project":[2,3,8],"propag":9,"proper":[7,11,15],"properli":[5,9,15],"properti":[0,2,7,9],"protect":[4,11],"proto":4,"protocol":[3,7,9,10,16],"prototyp":[2,3,6],"provid":0,"proxi":6,"public":[0,6,10],"public_path":11,"push":[4,6,10],"put":[6,10,14,15],"py":[2,3,4,5,7,9,10,14,15],"pydant":[0,2,9,10,14,15],"pyjwt":11,"pyproject":2,"pytest":[6,8,9],"python":[0,2,3,4,5,6,7,10,14,15,16],"pythonpath":3,"q":[7,12],"queri":[2,6,7,10,15,16],"query_str":0,"querydict":0,"queue":[6,7],"quick":[0,3,6],"r":[7,9],"race":9,"railwai":4,"rais":[0,2,9,10,11],"raise_server_except":9,"random":[0,5],"rang":[0,7,10],"rapidoc":[0,10],"rare":7,"rate":[1,2,4,6,13],"rate_limit":0,"ratelimit":[0,2,10,13],"rather":9,"raw":[0,3,6,7,10,13,15,16],"re":[0,4,7,9,10,12,13,14,16],"reach":[11,13],"react":[2,10],"reactiv":10,"read":[0,2,5,6,10,11,12,14,15],"readabl":10,"readi":[4,5,6,7],"real":[2,5,10,11,14,15],"realli":[9,10],"receiv":[0,6,7,9,10,11,13,16],"receive_byt":[10,16],"receive_incom":7,"receive_json":[10,16],"receive_text":[9,10,16],"recommend":10,"reconnect":10,"recv":16,"redirect":[0,2,7,11,12],"redoc":[0,10],"reduc":2,"ref":3,"refactor":[2,9],"refer":[6,9],"refresh":15,"refus":10,"regist":[0,9,10,11],"register_blueprint":12,"reject":[9,10],"relat":[0,10,15],"releas":8,"reliabl":9,"remain":10,"rememb":11,"remov":[10,12,14,16],"renam":9,"render":[0,1,4,6,10],"render_async":7,"render_templ":12,"replac":[2,7,10,14],"report":[7,9,10],"repres":0,"represent":0,"req":[0,2,6,7,9,10,11,12,13,14,15,16],"request":[2,3,4,6,11,12,13,14,15],"request_id":[0,2,10,13],"request_model":[0,2,9,10,14,15],"requir":[0,2,9,10],"research":6,"resolv":[2,10],"resolve_hello":[0,10],"resourc":[7,10,14],"resp":[0,2,6,7,9,10,11,12,13,14,15,16],"respect":3,"respond":[0,2,3,4,5,7,8,9,10,11,12,13,14,15,16],"respons":[2,6,9,10,12,13,14,15,16],"response_model":[0,2,10,14,15],"rest":[4,6,7,10],"restrict":10,"result":[0,7,10,15],"resum":2,"resume_incomplet":2,"retri":[0,10],"retriev":[0,10,14],"return":[0,2,6,7,9,10,11,12,13,14,15],"reusabl":11,"revers":[0,6],"rfc3986":2,"right":[6,7,10,12,13],"roll":15,"room":6,"root":10,"rotat":5,"rout":[2,6,9,13,14,15,16],"routegroup":0,"router":2,"ruff":8,"rule":5,"run":[0,2,3,6,8,9,10,12,13,16],"run_sync":15,"runner":[2,4],"runtimeerror":0,"s3":3,"safe":[2,14],"sai":7,"said":4,"same":[3,4,6,7,10,12,14,15],"sandbox":6,"save":[7,16],"scalar":15,"scale":6,"schema":[0,2,6,10,11,14,15],"scope":[0,10,13],"scratch":[6,14],"script":16,"search":[0,2,7,10],"second":0,"secret":[6,10,11],"secret_kei":[0,5,10,11],"section":10,"secur":[0,10,13],"see":[0,4,5,7,9,10,13,14,15],"select":[0,2,15],"self":[0,10,11,13],"semant":2,"send":[0,2,6,9,10,11,14,16],"send_byt":[10,16],"send_email":0,"send_json":[10,16],"send_text":[9,10,16],"sensit":11,"sent":[0,2,6,7,13],"separ":[7,9,14],"sequenti":2,"serial":[0,2,7,10,16],"serializ":7,"serv":[0,2,3,4,5,6,7,9,14,16],"server":[0,2,3,4,5,6,9,11,13,14,15],"servererrormiddlewar":13,"servestat":2,"servic":[0,3,6,9,10,14],"session":[0,2,5,6,7,9,12,13,15],"session_id":10,"sessionmiddlewar":13,"set":[0,2,4,5,6,7,8,9,10,11,15,16],"set_cooki":[0,2,10],"set_text":0,"setattr":15,"setup":[2,5,6,9],"sever":[10,13],"sftp":3,"share":[0,3,7,9,10,15],"shell":5,"short":[0,1,2,6,10],"shorthand":0,"should":[5,14,15],"shouldn":0,"show":[2,10,12,15,16],"shut":[10,15],"shutdown":[0,4,6,9,10],"shutdwown":2,"sibl":6,"side":[7,10,11,16],"sign":[0,5,6,7,10,11,13],"signatur":[7,10,12],"signup":11,"simpl":[3,9,10,11,13,14,16],"simplecooki":0,"simpler":[7,10,11,13],"simplest":[5,7,11,13,16],"simpli":11,"simplic":[6,15],"simul":7,"sinc":[7,9],"singl":[0,2,3,6,7,10,12,15],"sit":13,"size":[0,15],"sk":11,"skip":[0,6,10],"slash":7,"sleep":[7,9],"slim":4,"slow":7,"slowlori":4,"slug":7,"small":[4,6,9,10],"smaller":4,"so":[9,10,12,13,14,15],"solv":10,"some":[4,5,10],"some_packag":13,"somemiddlewar":13,"someon":[5,7,10,16],"someth":[6,7,9,10],"sometim":[7,10],"somewher":7,"sourc":[0,5,8,10],"spawn":4,"spec":[7,10],"special":[6,10],"specif":[0,5,9,10,13,14],"specifi":3,"sphinx":8,"spin":7,"split":5,"sprawl":6,"sql":15,"sqlalchemi":[6,7,14],"sqlite":[5,15],"sse":[0,2,6],"ssl":4,"stabil":2,"stack":13,"stai":[12,15],"standard":[3,4,10,11,14],"starlett":[0,2,6,9,16],"start":[0,3,4,6,10,13,14,15],"startswith":11,"startup":[0,2,6,9,10],"state":[0,2,9,10,11],"statement":6,"static":[0,2,4,6,7,16],"static_app":0,"static_dir":[0,10],"static_rout":[0,2,10],"staticfil":0,"statu":[2,6,7,9,10,14],"status_cod":[0,6,7,9,10,11,12,13,14,15],"status_code_saf":0,"stderr":0,"stdlib":2,"still":[10,12],"stop":10,"storag":[3,6],"store":[0,5,10,11,14],"str":[0,7,9,10,11,14,15],"straightforward":10,"stranger":[0,10],"stream":[0,2,10],"stream_data":0,"stream_fil":[0,2,10],"strict":10,"string":[0,6,7,10,15,16],"strip":[2,10,11],"structur":10,"style":10,"stylesheet":10,"sub":11,"subclass":0,"subcommand":3,"subject":0,"submiss":0,"submit":0,"subrout":[6,10,12],"subsequ":[10,11],"subtl":2,"success":[0,7,14],"successfulli":14,"successor":4,"suit":9,"suppli":2,"support":[0,1,2,3,4,6,7,12,15,16],"swagger":[6,10,14],"swagger_ui":[0,10],"switch":[2,15,16],"sync":[2,6,7,12],"synchron":[0,12],"syntax":[2,6,7,12],"system":[3,7,10],"t":[0,4,5,7,9,10,11,12,13,14,15],"tabl":15,"take":[6,7],"tamper":[10,11],"target":2,"task":[0,2,6],"teach":6,"tear":10,"tell":[7,10,14,15],"templat":[0,1,2,6],"template_str":[0,7],"templates_dir":0,"termin":[3,4],"terms_of_servic":0,"test":[0,2,3,5,6,8,14],"test_500":[2,9],"test_api":9,"test_create_item":9,"test_custom_error":9,"test_echo":16,"test_head":9,"test_hello":9,"test_hook":9,"test_json":9,"test_upload":9,"test_valid":9,"test_websocket":9,"test_with_lifespan":9,"testclient":[0,2,9,16],"text":[0,2,6,7,9,10,12,16],"textcont":16,"than":[9,10,11,12,13,14],"thei":[0,5,7,10,12,13,14,16],"them":[6,7,9,10,11,12,14,16],"theme":[0,10],"themselv":10,"thi":[0,2,3,4,5,6,7,9,10,11,12,13,14,15,16],"thing":[5,6,7,9,13],"think":7,"third":6,"though":4,"thousand":4,"thread":[0,6,7,12],"threadpoolexecutor":[0,2],"three":[7,10,16],"through":[0,7,10,13,14,16],"throughput":10,"thu":0,"time":[0,2,6,7,9,10,12,13,14,15,16],"time_start":0,"timedelta":11,"timeout":4,"timingmiddlewar":13,"tip":6,"titl":[0,7,10,14,15],"tl":4,"togeth":[6,10],"token":[0,6,7,10,16],"token_hex":5,"toml":2,"too":[0,3,4,9,10],"tool":[8,13],"toolbelt":2,"toolkit":15,"top":[0,5],"total":10,"tour":[0,6,7],"trace":10,"traceback":[0,2,5,10],"track":15,"tradit":[10,11],"traffic":4,"transact":15,"transform":13,"translat":12,"transport":10,"treat":[0,10],"trick":10,"trigger":9,"true":[0,2,5,7,9,10,11,12,13,15,16],"trust":6,"trustedhostmiddlewar":[4,13],"try":[6,7,11,16],"tupl":[0,7,9],"tutori":[7,14],"two":[7,10,13],"type":[0,2,6,10,12,14,16],"typic":[7,10],"typo":[2,10],"ui":[2,6,10,14],"unauthor":[0,10,11],"under":[0,7,10,12],"understand":[6,7],"unexpectedli":16,"unhandl":13,"unicod":0,"union":2,"uniqu":10,"unit":15,"univers":5,"unknown":0,"unless":10,"unlik":10,"unmaintain":2,"unmatch":10,"unpin":2,"unrecogn":10,"until":7,"up":[0,2,7,8,10],"updat":[2,6,7,10,15,16],"update_book":[14,15],"upgrad":[2,8,10,16],"upload":[2,6,7],"url":[0,2,5,6,7,9,10,11,12,13,15],"url_for":[0,2,9],"url_prefix":12,"urllib":2,"us":[0,2,3,4,6,7,10,12,14,16],"usag":[0,2],"user":[0,5,7,10,11,12,15],"user_id":[7,10,11,12],"usernam":[10,11],"usual":9,"utcnow":11,"utf":0,"uuid":[0,2,7,10],"uuid4":0,"uv":[3,5,6,8,11,15],"uvicorn":[0,2,6,7],"uvloop":4,"v":6,"v1":[0,10],"v2":10,"valid":[0,2,6,7,10,11,13,14,15],"valu":[0,6,7,9,10,11,12,13,14,15,16],"valueerror":[0,9,10,12,14],"variabl":[0,4,6,7],"ve":[6,9,12],"venv":8,"verb":10,"veri":[0,7,15],"verifi":[9,11],"verify_token":11,"version":[0,2,9,10,14],"via":[0,2,5,10,12],"view":[0,2,6,7,9,12],"virtual":[3,4],"virtualenv":8,"visit":[10,14],"vue":10,"w":[9,10,16],"wa":[0,2,7,14],"wai":[3,4,6,7,10,11,12,13,15],"wait":12,"walk":[7,10,14],"want":[0,3,4,6,7,9,10,14,15,16],"watch":8,"we":[14,15,16],"web":[0,4,6,10,15],"websit":10,"websocket":[0,1,2,4,6],"websocket_connect":[9,16],"welcom":[6,7,11],"well":6,"went":6,"what":[0,7,9,10,16],"whatev":7,"when":[0,2,4,5,6,7,9,10,12,14,15,16],"where":[0,4,7,10,15,16],"whether":[0,7],"which":[0,2,7,9,10,12,15],"whichev":6,"while":[7,10,12,16],"whitenois":2,"who":[6,7,11],"widget":[9,10],"wildcard":10,"window":2,"wire":7,"with_app":10,"within":[2,10,15],"without":[0,2,4,7,9,10,15],"won":[7,9,10,15],"work":[3,4,5,6,7,10,13,14,15],"workdir":4,"worker":4,"world":[0,3,6,9,10,12],"worri":9,"would":9,"wrap":[0,7,9,10,13],"write":[6,7,10,12],"wrong":10,"ws_auth":16,"wsgi":[0,2,4,6,10,12],"www":5,"x":[0,4,7,9,10,11,12,13,14],"x00":16,"x01":16,"x02":16,"x89png":7,"xml":0,"xss":10,"yaml":[0,2,6,7,9,10],"year":[14,15],"yield":[0,10,15],"yml":[0,10,11],"you":[0,3,4,5,7,9,10,11,12,13,14,15,16],"your":[0,3,4,5,6,7,9,10,11,12,13],"yourself":7,"zero":4},"titles":["API Reference","Backlog","Changelog","Command Line Interface","Deployment","Configuration","Responder","Quick Start","Development Sandbox","Testing","Feature Tour","Authentication","Migrating from Flask","Writing Middleware","Building a REST API","Using SQLAlchemy","WebSocket Tutorial"],"titleterms":{"":14,"0":2,"01":2,"02":2,"03":2,"04":2,"08":2,"09":2,"1":2,"10":2,"11":2,"12":2,"13":2,"15":2,"16":2,"17":2,"18":2,"19":2,"2":2,"20":2,"2018":2,"2019":2,"2026":2,"22":2,"23":2,"24":2,"25":2,"26":2,"27":2,"28":2,"29":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":2,"9":2,"In":14,"It":[5,7,14,15],"The":[0,6,12],"ad":[2,13],"after":[9,10],"all":[5,7,14],"allow":5,"api":[0,9,11,12,14],"app":[10,11],"asset":3,"auth":11,"authent":11,"background":[0,7],"backlog":1,"base":10,"bearer":11,"befor":[9,10,16],"big":12,"blueprint":12,"book":14,"build":[3,14],"built":13,"chang":2,"changelog":2,"chat":16,"class":[0,5,10],"client":16,"cloud":4,"code":0,"command":3,"configur":5,"convertor":7,"cooki":[9,10],"cor":10,"creat":[7,14],"crud":15,"custom":[3,10,11],"data":16,"databas":15,"debug":5,"defin":[14,15],"delet":14,"deploy":4,"deprec":2,"develop":8,"dict":0,"differ":12,"directli":4,"docker":4,"document":10,"echo":16,"endpoint":15,"env":5,"environ":5,"error":[9,10,11,14],"event":[9,10],"except":11,"featur":10,"file":[3,5,9,10],"filter":10,"fix":2,"fixtur":9,"flask":12,"format":16,"from":[3,12],"frontend":3,"futur":1,"get":[6,9,14],"gradual":12,"graphql":10,"group":[0,10,12],"guid":6,"handl":[9,10,14],"header":9,"hello":7,"helper":0,"hook":[9,10,13,16],"host":[5,10],"how":16,"hst":10,"html":16,"id":10,"idea":[1,6],"instal":[6,15],"instanc":3,"interfac":3,"json":[9,12],"kei":[5,11],"launch":3,"lifespan":[9,10,15],"limit":[0,10],"line":3,"list":14,"local":4,"memori":14,"messagepack":10,"method":10,"middlewar":13,"migrat":12,"mode":5,"model":[14,15],"modul":3,"mount":10,"name":3,"next":14,"openapi":10,"oper":8,"order":13,"other":10,"out":14,"paramet":[7,12],"parti":13,"pattern":5,"platform":4,"postgresql":15,"project":[6,14],"proxi":4,"public":11,"put":[5,7],"queri":0,"queue":0,"quick":[7,12],"rate":[0,10],"read":7,"refer":[0,12],"remov":2,"render":7,"request":[0,7,9,10,16],"respond":6,"respons":[0,7],"rest":14,"revers":4,"room":16,"rout":[0,7,10,11,12],"run":[4,7,14,15],"sandbox":8,"secret":5,"send":7,"sent":10,"serv":10,"server":[7,10,16],"servic":7,"session":[10,11],"setup":[8,14,15],"shutdown":15,"singl":14,"skip":11,"sqlalchemi":15,"sse":10,"starlett":13,"start":[7,9],"startup":15,"static":10,"statu":0,"storag":14,"support":10,"task":7,"templat":[7,12],"test":[9,16],"third":13,"tip":[9,15],"togeth":[5,7],"token":11,"tour":10,"trust":10,"try":14,"tutori":[6,16],"type":7,"unreleas":2,"updat":14,"upload":9,"url":3,"us":[5,9,11,13,15],"user":6,"uvicorn":4,"v":13,"v0":2,"v1":2,"v2":2,"v3":2,"valid":9,"variabl":5,"view":10,"web":[7,11],"websocket":[9,10,16],"what":[6,13,14],"when":13,"work":16,"world":7,"write":13,"you":6,"your":[14,15]}})
\ No newline at end of file
diff --git a/testing.html b/testing.html
index 031454d..f68e6b6 100644
--- a/testing.html
+++ b/testing.html
@@ -5,13 +5,13 @@
- Testing — responder 3.2.0 documentation
+ Testing — responder 3.4.1 documentation
-
+
diff --git a/tour.html b/tour.html
index 37d5e51..276e4a5 100644
--- a/tour.html
+++ b/tour.html
@@ -5,13 +5,13 @@
- Feature Tour — responder 3.2.0 documentation
+ Feature Tour — responder 3.4.1 documentation
-
+
@@ -420,6 +420,21 @@ side by side, moving routes over one at a time:
Requests to /flask/ will be handled by Flask. Everything else goes
through Responder. Both WSGI and ASGI apps are supported — Responder
wraps WSGI apps in an ASGI adapter automatically.
+
You can also mount marimo notebooks as
+interactive dashboards within your API:
Every API that handles user data needs authentication — a way to verify
+who is making a request. This guide covers the most common patterns:
+API keys, JWT tokens, and how to build reusable auth guards with
+Responder’s before-request hooks.
The simplest approach. The client sends a secret key in a header, and
+your server checks it against a known value. This is common for
+server-to-server communication and simple APIs:
+
API_KEYS={"sk-abc123","sk-def456"}
+
+@api.route(before_request=True)
+defcheck_api_key(req,resp):
+ key=req.headers.get("X-API-Key")
+ ifkeynotinAPI_KEYS:
+ resp.status_code=401
+ resp.media={"error":"Invalid or missing API key"}
+
+
+
Because the before-request hook sets resp.status_code, the route
+handler is skipped entirely for unauthorized requests. The client never
+reaches your endpoint — the guard catches them first.
Bearer tokens are the standard for modern APIs. The client sends a token
+in the Authorization header, and the server validates it. The most
+common format is JWT (JSON Web Tokens).
For traditional web applications (with HTML pages and forms), cookie-based
+sessions are simpler than tokens. The browser handles cookies automatically
+— no client-side token management needed:
Middleware sits between the server and your route handlers, processing
+every request and response that flows through your application. It’s the
+right tool for cross-cutting concerns — things that apply to all
+requests, not just specific routes.
Responder gives you two levels of request processing:
+
Hooks (before_request / after_request) run inside Responder’s
+routing layer. They receive Responder’s req and resp objects and
+are the simplest way to add behavior:
Middleware runs at the ASGI level, wrapping the entire application.
+It’s more powerful but more complex — you work with raw ASGI scopes
+instead of Responder objects. Use middleware when you need to process
+requests before they reach Responder’s routing, or when you need to
+integrate with Starlette middleware.
The dispatch method receives a Starlette Request and a
+call_next function. Call call_next(request) to pass the request
+to the next middleware (or to your route handler). The return value is
+a Starlette Response that you can modify before it’s sent.
Middleware wraps your application like layers of an onion. The last
+middleware added is the outermost layer — it sees the request first
+and the response last.
+
Responder’s built-in middleware stack (from outermost to innermost):
+
+
SessionMiddleware
+
ServerErrorMiddleware
+
CORSMiddleware (if enabled)
+
TrustedHostMiddleware
+
HTTPSRedirectMiddleware (if enabled)
+
GZipMiddleware
+
ExceptionMiddleware
+
Your routes
+
+
When you call api.add_middleware(), your middleware is added outside
+the existing stack. Keep this in mind for ordering dependencies — if
+middleware A depends on middleware B having run first, add B before A.
HTTP is request-response — the client asks, the server answers, and the
+connection closes. WebSockets upgrade that into a persistent, bidirectional
+channel where both sides can send messages at any time. This is what powers
+chat apps, live dashboards, multiplayer games, and collaborative editors.
+
This tutorial builds a simple chat room to show how WebSockets work in
+Responder.
The client sends a normal HTTP request with an Upgrade:websocket
+header.
+
The server accepts the upgrade and the connection switches protocols.
+
Both sides can now send messages freely — no more request/response.
+
Either side can close the connection at any time.
+
+
In Responder, WebSocket routes receive a ws object instead of
+req and resp. The ws object has methods for accepting the
+connection, sending and receiving data, and closing.
A chat room needs to broadcast messages to all connected clients. We keep
+a set of active connections and iterate through them when someone sends
+a message:
You can run code before a WebSocket connection is established, just like
+HTTP before-request hooks. This is useful for authentication:
+
@api.before_request(websocket=True)
+asyncdefws_auth(ws):
+ # Check for a token in the query string
+ # (WebSocket headers are limited in browsers)
+ awaitws.accept()
+
+
+
WebSocket before-request hooks receive the ws object and must call
+awaitws.accept() if they want the connection to proceed.