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
/**
* Fetches JSON data from a URL.
* @async
* @param {string | URL} url - The URL to fetch JSON data from.
* @param {RequestInit & { bearer?: string; fetch?: typeof fetch }} [options] - Additional options for the fetch.
* @returns {Promise<any>} - A Promise that resolves to the fetched JSON data.
*/
export const fetchJSON = async (
url: string | URL,
options?: RequestInit & { bearer?: string; fetch?: typeof fetch },
) => {
let headers = new Headers(options?.headers ?? {});
headers.set("accept", "application/json");
if (options?.bearer) {
headers.set("authorization", `Bearer ${options.bearer}`);
}
let fetch = options?.fetch ?? globalThis.fetch;
let resp = await fetch(normalizeURL(url), {
redirect: "follow",
...options,
headers,
});
let text = await resp.text();
try {
return JSON.parse(text);
}
catch (e) {
throw new Error(`fetchJSON error: ${e.message} in ${url}\n\n"${text}"`);
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { fetchText } from "https://esm.town/v/stevekrouse/fetchText";
export const fetchJSON = async (
url: string | URL,
options?: RequestInit & {
bearer?: string;
fetch?: typeof fetch;
},
) => {
let headers = new Headers(options?.headers ?? {});
headers.set("accept", "application/json");
let text = await fetchText(url, {
...options,
headers,
});
try {
return JSON.parse(text);
} catch (e) {
throw new Error(`fetchJSON error: ${e.message} in ${url}\n\n"${text}"`);
}
};
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
import { normalizeURL } from "https://esm.town/v/stevekrouse/normalizeURL";
export const fetchJSON = async (
url: string | URL,
options?: RequestInit & {
bearer?: string;
fetch?: typeof fetch;
},
) => {
let headers = new Headers(options?.headers ?? {});
headers.set("accept", "application/json");
if (options?.bearer) {
headers.set("authorization", `Bearer ${options.bearer}`);
}
let fetch = options?.fetch ?? globalThis.fetch;
let resp = await fetch(normalizeURL(url), {
redirect: "follow",
...options,
headers,
});
let text = await resp.text();
try {
return JSON.parse(text);
}
catch (e) {
throw new Error(`fetchJSON error: ${e.message} in ${url}\n\n"${text}"`);
}
};

Fetch Paginated Data

This val exports a function that loops through paginated API responses and returns the combined data as an array. It expects pagination with next and there to be a data property in the API response. This conforms to the Val Town API, so this function is useful when fetching paginated Val Town API responses for creating custom folders in pomdtr's vscode extension.

Usage:

Create valconst id = <vt user id> await fetchPaginatedData(`https://api.val.town/v1/users/${id}/vals`, { headers: { Authorization: `Bearer ${Deno.env.get("valtown")}` }, });

For demo usage in the context of the vscode extension see this val.

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
import { fetch } from "https://esm.town/v/std/fetch?v=4";
export async function fetchPaginatedData(url: string, init?: RequestInit, limit: number = 100) {
let u = new URL(url);
u.searchParams.set("limit", String(limit));
url = u.toString();
const data = [];
while (true) {
const resp = await fetch(url, init);
if (!resp.ok) {
throw new Error(`Fetch failed with status ${resp.status}`);
}
const body = await resp.json();
data.push(...body.data);
if (!body.links?.next) {
break;
}
url = body.links?.next;
}
return data;
}
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
import { lowercaseKeys } from "https://esm.town/v/stevekrouse/lowercaseKeys";
import { normalizeURL } from "https://esm.town/v/stevekrouse/normalizeURL";
export const fetchJSON = async (
url: string,
options?: RequestInit & {
bearer?: string;
},
) => {
let f = await fetch(normalizeURL(url), {
redirect: "follow",
...options,
headers: {
"content-type": "application/json",
authorization: options?.bearer ? `Bearer ${options.bearer}` : undefined,
...(lowercaseKeys(options?.headers ?? {})),
},
});
let t = await f.text();
try {
return JSON.parse(t);
}
catch (e) {
throw new Error(`fetchJSON error: ${e.message} in ${url}\n\n"${t}"`);
}
};
1
2
3
4
5
6
7
8
9
10
11
import { fetch } from "https://esm.town/v/std/fetch";
export const fetchJSON = <T extends any = any>(
url: string | URL,
options?: RequestInit,
): Promise<T> =>
fetch(url, {
redirect: "follow",
...options,
}).then((r) => r.json() as T);
// Forked from @stevekrouse.fetchJSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { fetch } from "https://esm.town/v/std/fetch";
import { normalizeURL } from "https://esm.town/v/stevekrouse/normalizeURL?v=3";
export const fetchJSON = async (url: string, options?: any) => {
let f = await fetch(normalizeURL(url), {
redirect: "follow",
...options,
headers: {
"Content-Type": "application/json",
...(options?.headers || {}),
},
});
let t = await f.text();
try {
return JSON.parse(t);
}
catch (e) {
throw new Error(`fetchJSON error: ${e.message} in ${url}\n\n"${t}"`);
}
};
// Forked from @stevekrouse.fetchJSON
1
Next