Val Town Blob Storage - https://docs.val.town/std/blob

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { API_URL } from "https://esm.town/v/std/API_URL";
import { ValTownBlobError } from "https://esm.town/v/std/ValTownBlobError";
import { ValTownBlobNotFoundError } from "https://esm.town/v/std/ValTownBlobNotFoundError";
export const blob = {
get: get,
set: set,
copy: copy,
move: move,
list: list,
delete: delete_,
getJSON: getJSON,
setJSON: setJSON,
};
async function list(prefix?: string): Promise<{ key: string; size: number; lastModified: string }[]> {
let querystring = prefix ? `?prefix=${encodeURIComponent(prefix)}` : "";
const res = await fetch(`${API_URL}/v1/blob${querystring}`, {
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (!res.ok) {
const body = await res.text();
throw new ValTownBlobError(body ? body : "Error listing blobs");
}
return res.json();
}
async function delete_(key: string) {
const res = await fetch(`${API_URL}/v1/blob/${encodeURIComponent(key)}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (!res.ok) {
const body = await res.text();
throw new ValTownBlobError(body ? body : "Error deleting blob");
}
}
async function get(key: string) {
const res = await fetch(`${API_URL}/v1/blob/${encodeURIComponent(key)}`, {
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (res.status === 404) {
throw new ValTownBlobNotFoundError();
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.
v11 was merged from the PR "add copy and move methods" by pomdtr
Comments
davidcrespo avatar

I think it would be helpful if this doc mentioned the scope of the blob storage. My assumption, because it is not mentioned, is that this is global (account-scoped), not val-scoped, i.e., if I get and set the same key from two different vals, they will be sharing and overwriting the same value in the global store.

std avatar

Great note! I added this to the readme above: "Blob storage is scoped globally to your account. If you set a blob in one val, you can retrieve it by the same key in another val." What do you think?

davidcrespo avatar

Sounds good!

davidcrespo avatar

Another note: I converted a val to use blob instead of separate val to store a string value, and I expected to use get and set because set has value: string | BodyInit for that. Took me a bit to figure out that

  1. get returns a Response (though I can see why it does) and it is not what you want for storing a string
  2. I can use getJSON with a plain string because a string is valid JSON

I think of these as things that could use noting in the docs rather than a problem with the API. However, I will say unqualified get/set presents to me as the default/simplest API unless I'm told otherwise, but it seems like getJSON/setJSON are actually the first thing most people will want to reach for. That is hinted at by the fact that most of the examples on this page and here use the JSON variant. I think get/set are good names based on the implementation (get is most general, getJSON wraps get) but it would be nice to see that more clearly in the docs.

std avatar

Great point! I reorganized the readme to emphasize how they are lower-level primitives. Lmk if you have any other suggestions

pomdtr avatar

It would be nice to have a rename/move function here.

stevekrouse avatar

I don't think rename/move is in the s3 API, so it'd be a get and then set operation, in which case building it would be a great pull request to this val (when we launch pull requests later today or tomorrow)

std avatar

Thanks for the PR @pomdtr!