From 7b18d9fbe5938da992d8219fa5234ca61b974b88 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 1 Dec 2025 17:08:16 +0000 Subject: [PATCH] Adds Python examples to r2/api/workers docs. --- .../r2/api/workers/workers-api-reference.mdx | 28 ++++++- .../docs/r2/api/workers/workers-api-usage.mdx | 80 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/content/docs/r2/api/workers/workers-api-reference.mdx b/src/content/docs/r2/api/workers/workers-api-reference.mdx index ac28aeb841de536..5b821914013d9bd 100644 --- a/src/content/docs/r2/api/workers/workers-api-reference.mdx +++ b/src/content/docs/r2/api/workers/workers-api-reference.mdx @@ -3,7 +3,7 @@ pcx_content_type: reference title: Workers API reference --- -import { Type, MetaInfo, WranglerConfig } from "~/components"; +import { Type, MetaInfo, WranglerConfig, TabItem, Tabs } from "~/components"; The in-Worker R2 API is accessed by binding an R2 bucket to a [Worker](/workers). The Worker you write can expose external access to buckets via a route or manipulate R2 objects internally. @@ -43,6 +43,8 @@ The following methods are available on the bucket binding object injected into y For example, to issue a `PUT` object request using the binding above: + + ```js export default { async fetch(request, env) { @@ -66,6 +68,30 @@ export default { }; ``` + + +```py +from workers import WorkerEntrypoint, Response +from urllib.parse import urlparse + +class Default(WorkerEntrypoint): + async def fetch(self, request): + url = urlparse(request.url) + key = url.path[1:] + + if request.method == "PUT": + await self.env.MY_BUCKET.put(key, request.body) + return Response.new(f"Put {key} successfully!") + else: + return Response.new( + f"{request.method} is not allowed.", + status=405, + headers={"Allow": "PUT"} + ) +``` + + + - `head` - Retrieves the `R2Object` for the given key containing only object metadata, if the key exists, and `null` if the key does not exist. diff --git a/src/content/docs/r2/api/workers/workers-api-usage.mdx b/src/content/docs/r2/api/workers/workers-api-usage.mdx index 275de7831e82da5..dcd90389ac4efcb 100644 --- a/src/content/docs/r2/api/workers/workers-api-usage.mdx +++ b/src/content/docs/r2/api/workers/workers-api-usage.mdx @@ -194,6 +194,51 @@ export default { } } ``` + +```py +from workers import WorkerEntrypoint, Response +from urllib.parse import urlparse + + +class Default(WorkerEntrypoint): + async def fetch(self, request): + url = urlparse(request.url) + key = url.path[1:] + + if request.method == "PUT": + await self.env.R2.put( + key, + request.body, + onlyIf=request.headers, + httpMetadata=request.headers, + ) + return Response.new(f"Put {key} successfully!") + elif request.method == "GET": + obj = await self.env.R2.get( + key, + onlyIf=request.headers, + range=request.headers, + ) + + if obj is None: + return Response.new("Object Not Found", status=404) + + # When no body is present, preconditions have failed + body = obj.body if hasattr(obj, "body") else None + status = 200 if hasattr(obj, "body") else 412 + + headers = {"etag": obj.httpEtag} + return Response.new(body, status=status, headers=headers) + elif request.method == "DELETE": + await self.env.R2.delete(key) + return Response.new("Deleted!") + else: + return Response.new( + "Method Not Allowed", + status=405, + headers={"Allow": "PUT, GET, DELETE"}, + ) +``` @@ -214,6 +259,8 @@ For `PUT` and `DELETE` requests, you will make use of a new `AUTH_KEY_SECRET` en For `GET` requests, you will ensure that only a specific file can be requested. All of this custom logic occurs inside of an `authorizeRequest` function, with the `hasValidHeader` function handling the custom header logic. If all validation passes, then the operation is allowed. + + ```js const ALLOW_LIST = ["cat-pic.jpg"]; @@ -248,6 +295,39 @@ export default { }; ``` + + +```py +from workers import WorkerEntrypoint, Response +from urllib.parse import urlparse + +ALLOW_LIST = ["cat-pic.jpg"] + +# Check requests for a pre-shared secret +def has_valid_header(request, env): + return request.headers.get("X-Custom-Auth-Key") == env.AUTH_KEY_SECRET + +def authorize_request(request, env, key): + if request.method in ["PUT", "DELETE"]: + return has_valid_header(request, env) + elif request.method == "GET": + return key in ALLOW_LIST + else: + return False + +class Default(WorkerEntrypoint): + async def fetch(self, request): + url = urlparse(request.url) + key = url.path[1:] + + if not authorize_request(request, self.env, key): + return Response.new("Forbidden", status=403) + + # ... +``` + + + For this to work, you need to create a secret via Wrangler: ```sh