A Rubyist in Go Land: Your First Pokémon API Client

thunderbong1 pts0 comments

A Rubyist in Go Land: Your First Pokémon API Client | baweaver

baweaver

baweaver

Software Engineering

Simplicity is hard work. But there's a huge payoff. The person who has a genuinely simpler system is going to be able to affect the greatest change with the least work.<br>— Rich Hickey

ruby<br>July 24, 2026<br>12 min read

A Rubyist in Go Land: Your First Pokémon API Client

When I was growing up there was one game that I played to death: Pokémon Blue. Of course, that was after I figured out the carpet was the exit for the house, which took longer than I’d care to admit. It was my start into technology, along with Legend of Zelda, that made me want to try my own hand at making games and RPGs. Not long after I stumbled upon RPG Maker, and when the XP version came out it had this lovely scripting engine called RGSS where you could modify anything, and to kid me that was a wondrous playground. Oh, and RGSS? That was Ruby Game Script System, which means I’ve been doing Ruby for north of 20 years now if we count that at the time of writing this article.

Why am I mentioning that? Last fall I was in the audience for Tess Griffin’s Learning Empathy from Pokémon Blue at Rocky Mountain Ruby. It covered the infamous MissingNo glitch, how it functioned, how well-intentioned developers may have ended up writing it. It reminded me of those times. Fast forward and she gave the same talk again at RubyConf this year, and it gave me a few ideas of how I’d approach another adventure I find myself on.

A lot of folks know me as the “Ruby guy” but I do know other languages like Scala, Javascript, Python, and a smidge of Go from previous roles. Granted not as deeply, but part of how you get there is building things and experimenting, and as of recently I need to get back up to speed on Go so here we are.

Given that, I’m looking back at Pokémon again, and more directly PokéAPI, the open Pokémon data API. We’re going to go through API clients, how I might write things in Ruby, and what that translates to in Go while trying to behave myself and not rewriting Ruby in Go.

To be clear this series isn’t going to teach you Go syntax, and it’s not going to rank the two against each other. I’m evaluating which of my Ruby instincts translate and which don’t, and experimenting with tooling I’m not immediately familiar with. That means to be fair, as mentioned above, I will be attempting to write Go like a Go programmer (correct me if I miss here) instead of like a Rubyist.

Shall we get started then?

A local Pokédex first

While PokéAPI is a great resource and API, it would be bad manners to point readers directly at it, so before writing a client we’re going to stand up a local copy to experiment against instead of hammering the live server.

The project publishes its entire dataset as static JSON in PokeAPI/api-data, which makes “run PokéAPI locally” less adventurous than it sounds. We pull the handful of Pokémon this post needs into a fixtures directory:

require "fileutils"<br>require "net/http"

MIRROR = "https://raw.githubusercontent.com/PokeAPI/api-data/master/data"<br>FIXTURE_DIR = File.expand_path("fixtures/data/api/v2/pokemon", __dir__)

# api-data stores records by id, so we keep a small name lookup here.<br># Add to this map as the series grows.<br>POKEMON = {<br>"bulbasaur" => 1,<br>"charmander" => 4,<br>"squirtle" => 7,<br>"pikachu" => 25<br>}.freeze

FileUtils.mkdir_p(FIXTURE_DIR)

POKEMON.each do |poke_name, national_id|<br>response = Net::HTTP.get_response(URI("#{MIRROR}/api/v2/pokemon/#{national_id}/index.json"))

unless response.is_a?(Net::HTTPSuccess)<br>abort("Mirror returned #{response.code} for #{poke_name}, giving up")<br>end

File.write(File.join(FIXTURE_DIR, poke_name), response.body)<br>File.write(File.join(FIXTURE_DIR, national_id.to_s), response.body)

puts format("fetched %-10s (%6d bytes)", poke_name, response.body.bytesize)<br>end

Giving us back files we can serve directly from our own lightweight server:

$ ruby fetch_fixtures.rb<br>fetched bulbasaur (537794 bytes)<br>fetched charmander (599737 bytes)<br>fetched squirtle (605737 bytes)<br>fetched pikachu (572204 bytes)

And we can write that server with a quick Go script:

// Serves the local PokeAPI fixtures so we never hit the live API.<br>//<br>// go run ./cmd/serve-fixtures<br>// curl http://localhost:9595/api/v2/pokemon/bulbasaur<br>package main

import (<br>"log"<br>"net/http"<br>"os"

func main() {<br>// Default to the fixtures directory the fetch script creates.<br>// Pass a different path as an argument if you cloned api-data.<br>fixtureDir := "fixtures/data"<br>if len(os.Args) > 1 {<br>fixtureDir = os.Args[1]

// `http.FileServer` serves static files from a directory. It maps<br>// the request path directly to the filesystem, so a request for<br>// /api/v2/pokemon/bulbasaur looks for fixtures/data/api/v2/pokemon/bulbasaur.<br>// No routing needed because we wrote the fixture files without extensions.<br>//<br>// `http.ListenAndServe` blocks forever (or until it errors).<br>// `log.Fatal` prints the error and exits if the server dies.<br>address :=...

ruby data fixtures pokemon http response

Related Articles