Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/content/docs/r2/api/workers/workers-api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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:

<Tabs syncKey="workersExamples"> <TabItem label="JavaScript" icon="seti:javascript">

```js
export default {
async fetch(request, env) {
Expand All @@ -66,6 +68,30 @@ export default {
};
```

</TabItem> <TabItem label="Python" icon="seti:python">

```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"}
)
```

</TabItem> </Tabs>

- `head` <Type text="(key: string): Promise<R2Object | null>" />

- Retrieves the `R2Object` for the given key containing only object metadata, if the key exists, and `null` if the key does not exist.
Expand Down
80 changes: 80 additions & 0 deletions src/content/docs/r2/api/workers/workers-api-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,51 @@ export default {
}
}
```
</TabItem> <TabItem label="Python" icon="seti:python">
```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"},
)
```
</TabItem> </Tabs>
<Render file="request-dot-clone-warning" product="workers" />

Expand All @@ -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.

<Tabs syncKey="workersExamples"> <TabItem label="JavaScript" icon="seti:javascript">

```js
const ALLOW_LIST = ["cat-pic.jpg"];

Expand Down Expand Up @@ -248,6 +295,39 @@ export default {
};
```

</TabItem> <TabItem label="Python" icon="seti:python">

```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)

# ...
```

</TabItem> </Tabs>

For this to work, you need to create a secret via Wrangler:

```sh
Expand Down