Skip to content

Instantly share code, notes, and snippets.

@mxdvl
Last active September 19, 2024 16:31
Show Gist options
  • Save mxdvl/af32e79ed4710f6b53fb50a906b5a75b to your computer and use it in GitHub Desktop.
Save mxdvl/af32e79ed4710f6b53fb50a906b5a75b to your computer and use it in GitHub Desktop.
Get a typed JSON from any API
import { go, no, type Result } from "./result.ts";
/**
* Fetch JSON data and parse it safely.
*/
export const fetchJSON = async <T>(
url: Parameters<typeof fetch>[0],
{
headers,
parser,
}: {
headers?: HeadersInit;
parser: (data: unknown) => T;
},
): Promise<Result<"InvalidData" | "NetworkError", T>> => {
const response = await fetch(url, { headers })
.catch(() => no("NetworkError"));
if (!response.ok) {
return no("NetworkError");
}
return response.json()
.then((data) => parser(data))
.then((valid) => go(valid))
.catch(() => no("InvalidData"));
};
import { number, object, parse } from "jsr:@valibot/valibot";
import { fetchJSON } from "./json.ts";
const schema = object({
base_experience: number(),
});
if (import.meta.main) {
// invoke like so:
// deno run -A pokeapi.ts charmander
const [pokemon] = Deno.args;
const result = await fetchJSON(
`https://pokeapi.co/api/v2/pokemon/${pokemon}`,
{
parser: (data) => parse(schema, data),
},
);
if (!result.ok) {
console.error(result.no);
} else {
console.log(result.go.base_experience);
}
}
export type Result<No, Go> = { ok: false; no: No } | { ok: true; go: Go };
export function no<const T>(no: T): { ok: false; no: T } {
return { ok: false, no };
}
export function go<const T>(go: T): { ok: true; go: T } {
return { ok: true, go };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment