v11
April 3, 2024
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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();
}
if (!res.ok) {
const body = await res.text();
throw new ValTownBlobError(body ? body : "Error getting blob data");
}
return res;
}
async function set(key: string, value: BodyInit) {
const res = await fetch(`${API_URL}/v1/blob/${encodeURIComponent(key)}`, {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
body: value,
});
if (!res.ok) {
const body = await res.text();
throw new ValTownBlobError(body ? body : "Error setting blob data");
}
}
async function copy(previous: string, next: string) {
const res = await get(previous);
await set(next, res.body);
}
async function move(previous: string, next: string) {
await copy(previous, next);
await delete_(previous);
}
async function getJSON(key: string) {
try {
const res = await get(key);
return res.json();
} catch (e) {
if (e instanceof ValTownBlobNotFoundError) {
return undefined;
}
throw e;
}
}
async function setJSON(key: string, value: any) {
return set(key, JSON.stringify(value));
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
v11 was merged from the PR "add copy and move methods" by pomdtr
v11
April 3, 2024
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.
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?
Sounds good!
Another note: I converted a val to use blob instead of separate val to store a string value, and I expected to use
get
andset
becauseset
hasvalue: string | BodyInit
for that. Took me a bit to figure out thatget
returns aResponse
(though I can see why it does) and it is not what you want for storing a stringgetJSON
with a plain string because a string is valid JSONI 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 likegetJSON/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
wrapsget
) but it would be nice to see that more clearly in the docs.Great point! I reorganized the readme to emphasize how they are lower-level primitives. Lmk if you have any other suggestions
It would be nice to have a rename/move function here.
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)
Thanks for the PR @pomdtr!