From ea3f8c904960e62f2cebb9f32fd58cb0ef371f24 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Wed, 28 Oct 2026 18:44:14 +0300 Subject: [PATCH 1/2] blob python docs: drop the upload handler's owner option --- blob/recipes/fastapi-react.mdx | 7 +++---- blob/sdks/py/upload-handler.mdx | 21 +-------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/blob/recipes/fastapi-react.mdx b/blob/recipes/fastapi-react.mdx index c46cc6f1..55b18f0a 100644 --- a/blob/recipes/fastapi-react.mdx +++ b/blob/recipes/fastapi-react.mdx @@ -8,7 +8,7 @@ A React app whose backend is Python: users pick files, the bytes go from the bro Three choices make that work: - **An `AsyncUploadHandler` mounted on FastAPI.** It speaks the same protocol as the TypeScript handler, so `useUpload` from `@upstash/blob/react` works against it unchanged. -- **One auth check for every route.** `require_user` is both the handler's `context` and the FastAPI dependency on the other endpoints, and `owner` binds each upload to the user who began it. +- **One auth check for every route.** `require_user` is both the handler's `context` and the FastAPI dependency on the other endpoints. - **One origin.** Vite proxies `/api` to FastAPI in development, and FastAPI serves the built app in production, so the session cookie reaches the handler with no CORS setup. This recipe uses a public bucket. If you have not created one yet, start with the [Quickstart](/blob/overall/quickstart). @@ -107,7 +107,6 @@ uploads = AsyncUploadHandler( content_types=["image/*", "application/pdf"], max_size="25mb", context=require_user, - owner=lambda user: user.id, on_before_upload=authorize, on_upload_complete=record, ) @@ -115,7 +114,7 @@ uploads = AsyncUploadHandler( The handler takes images and PDFs up to 25 MB and refuses anything else before a byte is uploaded. It runs on FastAPI's event loop, so it is an `AsyncUploadHandler` over an `AsyncBucket`; see [Sync or async](/blob/sdks/py/upload-handler#sync-or-async). -`require_user` runs on every POST to the route. Its `HTTPException(401)` reaches the browser as a `BlobError` with code `unauthorized`, so the React side can tell a dead session from a refused file. `owner` signs the user id into each upload, so a second signed-in user holding the completion token cannot finish or cancel it. See [context](/blob/sdks/py/upload-handler#context) and [owner](/blob/sdks/py/upload-handler#owner). +`require_user` runs on every POST to the route. Its `HTTPException(401)` reaches the browser as a `BlobError` with code `unauthorized`, so the React side can tell a dead session from a refused file. See [context](/blob/sdks/py/upload-handler#context). `unique_path` adds a random suffix to the filename, so two uploads of `photo.png` are two objects. `args.file.name` is the original filename, and the completion callback is the only place it survives, so the row stores it. @@ -303,7 +302,7 @@ if __name__ == "__main__": - Options, callbacks, routes, `input` and `owner`. + Options, callbacks, routes and `input`. diff --git a/blob/sdks/py/upload-handler.mdx b/blob/sdks/py/upload-handler.mdx index 4423d584..1bec1cd0 100644 --- a/blob/sdks/py/upload-handler.mdx +++ b/blob/sdks/py/upload-handler.mdx @@ -185,7 +185,6 @@ uploads = AsyncUploadHandler( multipart="100mb", endpoint="/api/upload", context=require_user, - owner=lambda user: user.id, input=Thread, on_before_upload=authorize, on_upload_complete=record, @@ -202,7 +201,6 @@ uploads = AsyncUploadHandler( | `multipart` | `MultipartOption` | Where an upload stops being one PUT. See [Large files](/blob/uploads/large-files#changing-the-threshold). | | `endpoint` | `str` | Where the handler is mounted. Only needed to separate two handlers on one bucket. | | `context` | `Callable[[request], Any]` | Runs once per POST. Its value is `args.ctx` in every callback. Without it, `ctx` is `None`. See [context](#context). | -| `owner` | `Callable[[ctx], Any]` | Binds each upload to who began it. Python only. See [owner](#owner). | | `input` | pydantic model, `TypeAdapter`, or callable | Validates what the browser sends as `input` before `on_before_upload` runs. Every route inherits it. See [Input and state](#input-and-state). | | `on_before_upload` | `Callable[[BeforeUploadArgs], BeforeUploadResult]` | Required, here or on every route. See [on_before_upload](#on_before_upload). | | `on_upload_complete` | `Callable[[UploadCompleteArgs], Any]` | What it returns becomes `upload.blob.data`. See [on_upload_complete](#on_upload_complete). | @@ -211,7 +209,7 @@ uploads = AsyncUploadHandler( There is no `constraints` object: `content_types` and `max_size` are options of their own, on the handler, on a route, and on what `on_before_upload` returns. -Everything except `routes`, `endpoint`, `context` and `owner` is a default each route inherits; see [Multiple routes](/blob/uploads/upload-handler#multiple-routes). Bad options raise where they are written, at startup: a `ValueError` for an unparseable size, a `TypeError` for an invalid route name, an empty `routes`, a missing `on_before_upload`, or a missing token. See [Bad options](/blob/sdks/py/errors#bad-options-raise-typeerror-or-valueerror). +Everything except `routes`, `endpoint` and `context` is a default each route inherits; see [Multiple routes](/blob/uploads/upload-handler#multiple-routes). Bad options raise where they are written, at startup: a `ValueError` for an unparseable size, a `TypeError` for an invalid route name, an empty `routes`, a missing `on_before_upload`, or a missing token. See [Bad options](/blob/sdks/py/errors#bad-options-raise-typeerror-or-valueerror). ### The bucket @@ -382,23 +380,6 @@ uploads = AsyncUploadHandler( `context` takes the framework's request. Its return value, awaited on `AsyncUploadHandler`, is `args.ctx` in every callback. When it runs is on [Context](/blob/uploads/upload-handler#context). -### owner - -```python app/uploads.py -uploads = AsyncUploadHandler( - context=require_user, - owner=lambda user: user.id, - on_before_upload=authorize, -) -``` - -`owner` reads `ctx` and returns who the upload belongs to. Its value, as a string, is signed into the [completion token](/blob/reference/signing#the-completion-token) at `begin`. Every later request for that upload, `parts`, `end` and `cancel`, must produce the same owner or it is refused with `forbidden` before storage or any callback is touched. So one signed-in user cannot finish, re-sign or cancel an upload somebody else began, even holding its token. - -- `owner` needs `context`. Without one it raises a `TypeError` at startup. -- A return of `None` or `""` is `unauthorized`. A `bool` raises a `TypeError`, since `True` would make every user the same owner. - -The TypeScript handler has no `owner`. - --- ## Routes From 4cb9fa59480c77e9af9e34b7936dec76490a9216 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:44:44 +0000 Subject: [PATCH 2/2] chore(llms): regenerate llms.txt and llms-full.txt --- llms-full.txt | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index ec6d834b..b7f48104 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -6917,7 +6917,6 @@ uploads = AsyncUploadHandler( multipart="100mb", endpoint="/api/upload", context=require_user, - owner=lambda user: user.id, input=Thread, on_before_upload=authorize, on_upload_complete=record, @@ -6934,7 +6933,6 @@ uploads = AsyncUploadHandler( | `multipart` | `MultipartOption` | Where an upload stops being one PUT. See [Large files](/docs/blob/uploads/large-files#changing-the-threshold). | | `endpoint` | `str` | Where the handler is mounted. Only needed to separate two handlers on one bucket. | | `context` | `Callable[[request], Any]` | Runs once per POST. Its value is `args.ctx` in every callback. Without it, `ctx` is `None`. See [context](#context). | -| `owner` | `Callable[[ctx], Any]` | Binds each upload to who began it. Python only. See [owner](#owner). | | `input` | pydantic model, `TypeAdapter`, or callable | Validates what the browser sends as `input` before `on_before_upload` runs. Every route inherits it. See [Input and state](#input-and-state). | | `on_before_upload` | `Callable[[BeforeUploadArgs], BeforeUploadResult]` | Required, here or on every route. See [on_before_upload](#on_before_upload). | | `on_upload_complete` | `Callable[[UploadCompleteArgs], Any]` | What it returns becomes `upload.blob.data`. See [on_upload_complete](#on_upload_complete). | @@ -6943,7 +6941,7 @@ uploads = AsyncUploadHandler( There is no `constraints` object: `content_types` and `max_size` are options of their own, on the handler, on a route, and on what `on_before_upload` returns. -Everything except `routes`, `endpoint`, `context` and `owner` is a default each route inherits; see [Multiple routes](/docs/blob/uploads/upload-handler#multiple-routes). Bad options raise where they are written, at startup: a `ValueError` for an unparseable size, a `TypeError` for an invalid route name, an empty `routes`, a missing `on_before_upload`, or a missing token. See [Bad options](/docs/blob/sdks/py/errors#bad-options-raise-typeerror-or-valueerror). +Everything except `routes`, `endpoint` and `context` is a default each route inherits; see [Multiple routes](/docs/blob/uploads/upload-handler#multiple-routes). Bad options raise where they are written, at startup: a `ValueError` for an unparseable size, a `TypeError` for an invalid route name, an empty `routes`, a missing `on_before_upload`, or a missing token. See [Bad options](/docs/blob/sdks/py/errors#bad-options-raise-typeerror-or-valueerror). ### The bucket @@ -7112,23 +7110,6 @@ uploads = AsyncUploadHandler( `context` takes the framework's request. Its return value, awaited on `AsyncUploadHandler`, is `args.ctx` in every callback. When it runs is on [Context](/docs/blob/uploads/upload-handler#context). -### owner - -```python app/uploads.py -uploads = AsyncUploadHandler( - context=require_user, - owner=lambda user: user.id, - on_before_upload=authorize, -) -``` - -`owner` reads `ctx` and returns who the upload belongs to. Its value, as a string, is signed into the [completion token](/docs/blob/reference/signing#the-completion-token) at `begin`. Every later request for that upload, `parts`, `end` and `cancel`, must produce the same owner or it is refused with `forbidden` before storage or any callback is touched. So one signed-in user cannot finish, re-sign or cancel an upload somebody else began, even holding its token. - -* `owner` needs `context`. Without one it raises a `TypeError` at startup. -* A return of `None` or `""` is `unauthorized`. A `bool` raises a `TypeError`, since `True` would make every user the same owner. - -The TypeScript handler has no `owner`. - *** ## Routes