CamDex

React, TypeScript, NestJS, PostgreSQL, Bun

The problem

I collect Pokemon in the games and I like a full living dex: one of every form. Pokemon HOME tracks that, but only inside the app, and not the way I wanted. I wanted it on the web, for every form, with a separate dex per game or per goal.

I also wanted somewhere to build competitive teams that knew each game's rules, so I stopped keeping them in notes.

What I chose

A real database, seeded from PokeAPI

There are about 1,800 trackable forms, so a spreadsheet wouldn't cut it. PostgreSQL on Supabase holds the data, seeded from PokeAPI, behind a NestJS API that I know well from work.

JWTs verified locally

Supabase Auth signs its tokens with RS256, so the API verifies them with the public key instead of calling Supabase on every request. It's faster and there's one less thing to go down.

Rules that depend on the game

Standard games cap EVs at 252 per stat and 508 in total. Pokemon Champions caps them at 32 per stat and 66 in total, and has an Item Clause. The team builder reads the rule set for the game, so it won't let you build something that's illegal there.

TanStack Router and Query

File-based routes and cached server state kept the front end small. I wrote tests with Vitest on the front end and Jest on the back end, because a collection tracker that loses your data isn't worth using.

The result

CamDex is live on Vercel and Railway. It was built for one player and I'm that player, so it gets used for real.

Back to projects