Typed, and kept honest
Every endpoint and field is typed from the PokéAPI schema. A daily job diffs those types against the live API and opens an issue the moment one drifts.
Get started
Types checked against the live PokéAPI every day, so they say what the API actually sends.
await api.pokemon.getPokemonByName("luxray")
npm install pokenode-tspnpm add pokenode-tsyarn add pokenode-tsbun add pokenode-tsThe same request, twice: once against the raw API, once through the client.
const res = await fetch('https://pokeapi.co/api/v2/pokemon/luxray');
const pokemon = await res.json(); // any
// a 404 resolves too: nothing here checks res.ok
const speciesRes = await fetch(pokemon.specie.url); // typo compiles, throws at runtime
const species = await speciesRes.json(); // any, again
console.log(species.flavor_text_entries[0].flavour_text); // undefined, no errorimport { MainClient } from 'pokenode-ts';
const api = new MainClient();
const pokemon = await api.pokemon.getPokemonByName('luxray'); // Pokemon
const species = await api.resolve(pokemon.species); // PokemonSpecies
console.log(species.flavor_text_entries[0].flavor_text);res.json() hands back any, so every typo compiles and only shows up as undefined or a TypeError at runtime. fetch resolves on a 404, so a missing Pokémon reads as an empty object. And every run hits the API again, which its fair-use policy asks you not to do.
The client types every response, rejects non-2xx with a PokenodeError, and caches from the first call. resolve needs no type annotation: the link carries the type of what it points at. Read the getting started guide to pick a client, or browse them under Clients in the nav.