# pokedex-promise-v2: a typed promise wrapper over the PokéAPI v2 endpoints

> pokedex-promise-v2 turns the PokéAPI v2 REST endpoints into named methods on a single client object, so getPokemonSpeciesByName replaces a hand-written fetch and path string. It supports promises, callbacks and array batching, and the method surface is generated from the API schema.

**PokeAPI/pokedex-promise-v2** — An easy way to use pokeapi v2 with promises in node.js

- Repository: https://github.com/PokeAPI/pokedex-promise-v2
- Stars: 557 · Forks: 95
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pokeapi-pokedex-promise-v2

## What the wrapper replaces

The PokéAPI v2 is a plain REST service. Every call is a GET against a path such as /api/v2/pokemon/{name}, and the response is JSON. Nothing about it needs a library, and that is exactly the point of this one: the value is in turning paths into named functions and in the shape of the response, not in the transport.

So the problem this solves is narrow and real. Without a wrapper you write the URL, the path segment and the response handling for each resource yourself, and you get a string or an any-typed object back depending on how carefully you wrote it. With pokedex-promise-v2 you get a method per resource, and the same method handles a name, an integer ID, or an array of them.

The README notes the naming convention that governs the whole surface: any function with the designation ByName can also be passed an integer ID, while the functions with the designation ById can only be passed an integer ID. That is a small detail and it is the sort of thing that decides whether the client fits your data, because the PokéAPI itself distinguishes resources that have human-readable names from those that do not.

The library is also usable in three different call styles, which the README marks in the examples as with Async/Await, with Promise and with callback. The callback form is described as available as of v3, and it is why the package description still reads promises, or callbacks as of v3.

The primary language is TypeScript, the licence is MIT, the default branch is master, and the package is published on npm as pokedex-promise-v2 with typings at types/index.d.ts and the compiled entry at dist/src/index.js. The engines field requires Node >=18, while the README's install badge still advertises Node >=12, so the badge is out of date relative to the manifest.

## Installing, and the ESM boundary at version 4

The install is a normal package install, and the README offers it for the three package managers in use.

```bash
npm install pokedex-promise-v2 --save
```

```bash
yarn add pokedex-promise-v2
```

```bash
pnpm i pokedex-promise-v2
```

The one thing to read before you install is the note above the install blocks: as of 4.0.0 this package is now pure ESM, and the README links an explanation of what that means for consumers. The package manifest confirms it with a type of module.

That is the one real migration hazard in this library. If your project is CommonJS, or if your toolchain is old enough to treat a dependency without a type field as CommonJS, then requiring this package fails in a way that looks like a missing file rather than a format mismatch. The fix is on the consumer side, either by making your own project ESM or by using a dynamic import from CommonJS, but the point is that it is a decision you make before the first import rather than after the first failure in production.

Because the package is pure ESM, the require of it from a CommonJS file is the failure mode to design around. The README's own example uses import syntax, and so should yours.

The repository also ships both a package-lock.json, a pnpm-lock.yaml and a bun.lock, and the scripts section includes a locks:update task that removes and regenerates all three in turn, so the project is maintained against npm, pnpm and Bun rather than only the one its CI happens to use.

## Three call styles and the array shortcut

The usage section is short. You import the default export and construct a client.

```js
import Pokedex from 'pokedex-promise-v2';
const P = new Pokedex();
```

Then you call methods. The three styles sit in one block in the README, which is the clearest way to see that they are the same method with different consumption.

```js
(async () => { // with Async/Await
    try {
        const golduckSpecies = await P.getPokemonSpeciesByName("golduck")
        const frenchName = golduckSpecies.names.filter(pokeAPIName => pokeAPIName.language.name === 'fr')[0].name
        console.log(frenchName)
    } catch (error) {
        throw error
    }
})()

P.getPokemonByName(['eevee', 'ditto']) // with Promise
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.log('There was an ERROR: ', error);
  });

P.getPokemonByName(34, (response, error) => { // with callback
    if(!error) {
      console.log(response);
    } else {
      console.log(error)
    }
  });
```

The callback form is worth pausing on, because the signature is not the Node-style error-first one. The callback receives response and error as two arguments, and the README's own example tests for the error in the first branch of an if. If you are porting code from a Node convention, that difference will bite.

The array form is the feature that distinguishes this wrapper from a thin fetch helper. The README's update note says you can pass an array to each endpoint and it will retrieve data for each array element, and the Berries section shows it with a mixed array of names and a number.

```js
  P.getBerryByName(['cheri', 'chesto', 5])
    .then((response) => {
      console.log(response);
    })
  // response will be an Array containing 3 Objects
  // response.forEach((item) => {console.log(item.size)}) // 80,50,20
```

Three requests, one promise, one array out. For a script that needs a hundred berries, that is the difference between a hundred await points and one. The comment about the method fetching data asynchronously and being quite fast points at the real behaviour here: the array is not a sequential loop, the calls are issued together.

There is also a lower-level escape hatch. The getResource function accepts singles or arrays of URLs and paths, and the example mixes absolute URLs with relative path fragments.

```js
P.getResource(['/api/v2/pokemon/36', 'api/v2/berry/8', 'https://pokeapi.co/api/v2/ability/9/'])
  .then((response) => {
    console.log(response); // the getResource function accepts singles or arrays of URLs/paths
  });
```

That matters for endpoints the generated methods do not cover, since you can always fall back to a raw path.

## Configuration: host, protocol, cache and timeout

Every option is optional, and the README says so with some emphasis. Passing an object to the constructor configures the client.

```js
import Pokedex from 'pokedex-promise-v2';
const options = {
  protocol: 'https',
  hostName: 'localhost:443',
  versionPath: '/api/v2/',
  cacheLimit: 100 * 1000, // 100s
  timeout: 5 * 1000 // 5s
}
const P = new Pokedex(options);
```

The available options are protocol, hostName, versionPath, cacheLimit in milliseconds and timeout in milliseconds. The defaults are documented too: with no object passed, the client is initialised to grab data from pokeapi.co using http with a 20 second timeout and caching resources for 11 days, and HTTPS is the default protocol.

The defaults are worth reading carefully, because two of them are unusual. The protocol default is HTTPS even though the description of the default configuration says http, and the timeout is 20 seconds, which is generous for a JSON API and can hold a request open for a long time when a host is slow.

The 11 day cache is the setting that changes behaviour most visibly. It means a repeated lookup of the same resource inside that window does not hit the network, which for a script that loops over a list of Pokémon is the difference between a few hundred requests and a handful. It also means you will see stale data if the upstream data changes, and there is no documented way to force a refresh other than constructing a client with a smaller cacheLimit.

The hostName and versionPath options together are what let you point the client at a self-hosted instance of the API. The README's example uses hostName 'localhost:443' with versionPath '/api/v2/', which is the shape a local PokéAPI deployment would need. The combination of a configurable host and a configurable path is also what makes the getResource fallback practical, because you can point the base at your own server and still request arbitrary paths under it.

## The endpoint surface and how it is generated

The README's table of contents is a map of the resource groups the client covers: Berries, Contests, Encounters, Evolution, Games, Items, Machines, Moves, Locations, Pokemon and Utility, plus a section on custom URLs and paths and one on the root endpoints. Each group has one method per resource, named after the resource.

The naming is consistent enough that the method name is predictable once you know the convention. Under Berries, getBerryByName returns data about a specific berry, getBerryFirmnessByName returns data about the firmness of a specific berry and getBerryFlavorByName returns data about the flavor of a specific berry. Under Contests, getContestTypeByName returns data about the effects of moves when used in contests, getContestEffectById returns the same kind of data by numeric identifier, and getSuperContestEffectById covers super contests. Under Encounters, getEncounterMethodByName covers the conditions in which a trainer may encounter a pokemon in the wild and getEncounterConditionByName covers the data that affects which pokemon might appear in the wild.

The method names are longer than the underlying URLs, which is the trade the library makes: you lose nothing in expressiveness and gain a discoverable surface, since you can find the method for a resource by name rather than by remembering a path.

The interesting part is that this surface is generated. The package.json scripts show the machinery: apidata:clone clones the PokeAPI api-data repository, apidata:sync resets and pulls that clone, apidata:replace rewrites the schema paths from /schema/v2 to api-data/data/schema/v2, and then generate:types, generate:main and generate:jsdocs each compile a program from the generator/ directory and run it.

```bash
npm run generate:all
```

That is the real maintenance story of the library. The method list is not hand-maintained, it is produced from the API's own schema repository, which is why a new upstream resource becomes a new method rather than an open issue. The cost is that the schema data has to be fetched and patched before generation, and the path rewrite step with its sed pass over every schema file shows how much string-level patching is involved in keeping the generator pointed at the right place.

The generator/ directory in the repository tree is where those programs live, and the types/ directory is the published output.

## Where this client stops being the right choice

There are three limits, and the first is the ESM constraint seen from the other side.

The first is that pure ESM is a genuine cost in some stacks. A CommonJS service that wants to use this library has to restructure its module loading or reach for a dynamic import, and a build step that predates ESM in bundlers may need work to handle a dependency with no CommonJS entry. For a project already on ESM, that is nothing; for a legacy service, it is a migration triggered by a package you wanted to add for convenience.

The second is that the caching default hides network behaviour from you. An 11 day cache on resources means a script that expects live upstream data, or a test that asserts on a value that upstream has changed, will read the cached copy and quietly disagree with the server. The 20 second default timeout has the opposite effect, letting a slow request hold a worker slot far longer than an API call should.

The third is that the client only covers what PokéAPI exposes. It is a wrapper around a public, rate-limited, community-run service, and it inherits those constraints without adding anything of its own. There is no local database, no schema validation of the upstream data, no retry with backoff and no circuit breaker. If you need those, you need a different layer, and the getResource escape hatch is the place where you would build it.

The honest alternative is to skip the library and call the API directly. The endpoint surface is public and the responses are plain JSON, so fetch or a bare HTTP client with a typed response interface gives you the same data with zero dependencies, no ESM constraint and no cache you did not ask for. What the wrapper adds is the named methods, the ByName and ById distinction, the array batching and the generated typing, and the cost is the dependency and its ESM format. A script that makes one request has no real need for any of that. A script that makes thousands, in a TypeScript codebase, is exactly the case where it pays for itself.

The other comparison is the typed fetch route, where you define your own response interfaces from the published API documentation and call fetch directly. That gives you tighter control over types than a generated surface can promise, because the generator reflects upstream shape rather than the shape you want, at the cost of maintaining those interfaces yourself as upstream evolves.

## Conclusion

Adopt pokedex-promise-v2 if you are calling the PokéAPI from Node and want typed methods instead of hand-built fetch calls, since the method surface is generated from the API schema and will track the endpoints. Do not adopt it for a browser bundle without checking the module format, because the package is pure ESM from version 4.0.0. Before you start, confirm the resources you need exist as methods and note that the published release is 4.2.2 while the package.json in the repository is at 4.3.0.

## FAQ

### What is pokedex-promise-v2?

It is a library for using the PokéAPI v2 in Node.js with promises, and callbacks as of v3. It exposes a named method per API resource on a single client object, so you call getPokemonSpeciesByName rather than building a request path yourself.

### How do I install pokedex-promise-v2 and what Node version do I need?

Install it with npm install pokedex-promise-v2 --save, yarn add pokedex-promise-v2 or pnpm i pokedex-promise-v2. The package.json requires Node >=18, and as of version 4.0.0 the package is pure ESM, so a CommonJS project needs to account for that.

### Can I fetch several Pokemon in one call with pokedex-promise-v2?

Yes. You can pass an array to each endpoint and it retrieves data for each array element, returning an array of objects, and the array can mix names and numeric IDs. The requests are issued asynchronously rather than one after another.

### What configuration options does the Pokedex constructor accept?

The available options are protocol, hostName, versionPath, cacheLimit in milliseconds and timeout in milliseconds, and all of them are optional. With no object passed the client reads from pokeapi.co with a 20 second timeout and caches resources for 11 days.

### How are the pokedex-promise-v2 methods kept in step with the PokéAPI?

The method surface is generated. The scripts clone and sync the PokeAPI api-data repository, rewrite the schema paths and then run the generator programs in generator/ to produce the types, the main source and the JSDoc, and the npm run generate:all script runs the whole chain.

## Sources

- [Issues](https://github.com/PokeAPI/pokedex-promise-v2/issues)
- [License: MIT](https://github.com/PokeAPI/pokedex-promise-v2/blob/master/LICENSE)
- [PokeAPI/pokedex-promise-v2 on GitHub](https://github.com/PokeAPI/pokedex-promise-v2)
- [README](https://github.com/PokeAPI/pokedex-promise-v2/blob/master/README.md)
- [Releases](https://github.com/PokeAPI/pokedex-promise-v2/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pokeapi-pokedex-promise-v2
