Skip to content

Repository files navigation

IGDB Steam Game Service

TypeScript + Bun server for querying IGDB game data using Steam IDs with persistent SQLite caching.

Features

  • 🎮 Query game data from IGDB using Steam IDs
  • 💾 Persistent SQLite caching (permanent until force refresh)
  • ⚡ Fast responses via in-memory + disk cache
  • 🔄 OAuth token management with auto-refresh
  • 📊 Partial success responses (found/notFound/errors)
  • 🌐 Multi-language support for game names, genres and themes
  • 🚀 Built with Bun for optimal performance

Prerequisites

  • Bun v1.0+
  • Twitch Developer Account (for IGDB API access)

Setup

1. Install Bun

curl -fsSL https://bun.sh/install | bash

2. Get Twitch API Credentials

  1. Go to https://dev.twitch.tv/console
  2. Click "Register Your Application"
  3. Fill in details:
    • Name: "IGDB Service" (or any name)
    • OAuth Redirect URL: http://localhost
    • Category: Application Integration
  4. Copy Client ID and Client Secret

3. Configure Environment

cp .env.example .env

Edit .env and add your credentials:

TWITCH_CLIENT_ID=your_actual_client_id
TWITCH_CLIENT_SECRET=your_actual_client_secret
PORT=3000

4. Install Dependencies

bun install

5. Run Server

# Development (with hot reload)
bun run dev

# Production
bun run start

Server will start at http://localhost:3000

PM2 Deployment

Use PM2 to run the service in the background with auto-restart.

Install PM2

npm install -g pm2

Build & Deploy

# Build standalone executable
bun run build

# First time: start service
bun run pm2:start

# After code changes: rebuild and restart
bun run deploy

PM2 Commands

# Stop service
bun run pm2:stop

# Restart service
bun run pm2:restart

# View logs
bun run pm2:logs

# Check status
bun run pm2:status

Auto-start on System Boot

# Generate startup script
pm2 startup

# Save current process list
pm2 save

API Usage

Endpoint

POST /api/games

Request

{
  "steamIds": [730, 570, 440],
  "forceRefresh": false,
  "language": "en"
}

Parameters:

  • steamIds (required): Array of Steam app IDs (max 100)
  • forceRefresh (optional): Skip cache and fetch fresh data from IGDB
  • language (optional): Language code for game names/genres/themes (default: en)

Response

{
  "games": [
    {
      "steamId": 730,
      "name": "Counter-Strike: Global Offensive",
      "localizedName": "反恐精英:全球攻势",
      "summary": "Counter-Strike: Global Offensive...",
      "url": "https://www.igdb.com/games/counter-strike-global-offensive",
      "cover": {
        "url": "https://images.igdb.com/igdb/image/upload/t_cover_big/...",
        "width": 264,
        "height": 352
      },
      "screenshots": [
        {
          "image_id": "scii5z",
          "url": "https://images.igdb.com/igdb/image/upload/t_screenshot_big/scii5z.jpg",
          "width": 1920,
          "height": 1080
        }
      ],
      "artworks": [
        {
          "image_id": "ar47zf",
          "url": "https://images.igdb.com/igdb/image/upload/t_1080p/ar47zf.jpg",
          "width": 2560,
          "height": 1440,
          "artwork_type": 3
        }
      ],
      "videos": [
        {
          "name": "Trailer",
          "video_id": "abc123",
          "youtube_url": "https://www.youtube.com/watch?v=abc123"
        }
      ],
      "first_release_date": 1345075200,
      "aggregated_rating": 85.5,
      "total_rating": 88.2,
      "game_status": "Released",
      "age_ratings": [
        {
          "organization": "ESRB",
          "rating": "Mature",
          "synopsis": "..."
        }
      ],
      "platforms": [
        { "name": "PC (Microsoft Windows)" }
      ],
      "game_modes": [
        { "name": "Multiplayer" }
      ],
      "genres": [
        { "name": "Shooter" }
      ],
      "themes": [
        { "name": "Action" }
      ],
      "language_supports": [
        {
          "language": "English",
          "support_type": "Audio"
        }
      ],
      "similar_games": [
        {
          "name": "Counter-Strike",
          "cover": { "url": "..." }
        }
      ],
      "developers": [
        { "name": "Valve Corporation" }
      ],
      "publishers": [
        { "name": "Valve Corporation" }
      ]
    }
  ],
  "notFound": [],
  "errors": []
}

Response Fields:

  • games: Successfully fetched games (from cache or IGDB)
    • name: Original game name (always in English)
    • localizedName: Localized name (only present when found and different from name)
    • screenshots: Array of game screenshots
    • artworks: Array of official artworks (key art, concept art, logos, etc.)
    • videos: Array of game videos with YouTube links
  • notFound: Steam IDs with no IGDB mapping
  • errors: Steam IDs that failed to fetch (with reasons)

Artwork Types

The artwork_type field indicates the type of artwork:

ID Name Description
1 Artwork General artwork
2 Key art without logo Key art without game logo
3 Key art with logo Key art with game logo
4 Concept art Concept artwork
5 Game logo (white) White version of game logo
6 Game logo (black) Black version of game logo
7 Game logo (color) Color version of game logo
8 Infographic Infographic image

Filter Key Art:

const keyArts = game.artworks.filter(a => a.artwork_type === 2 || a.artwork_type === 3);

Examples

Fetch multiple games:

curl -X POST http://localhost:3000/api/games \
  -H "Content-Type: application/json" \
  -d '{"steamIds": [730, 570, 440]}'

Force refresh cached data:

curl -X POST http://localhost:3000/api/games \
  -H "Content-Type: application/json" \
  -d '{"steamIds": [730], "forceRefresh": true}'

Fetch with Chinese translations:

curl -X POST http://localhost:3000/api/games \
  -H "Content-Type: application/json" \
  -d '{"steamIds": [730], "language": "zh-CN"}'

Health check:

curl http://localhost:3000/health

Multi-language Support

The service supports localized game names, genres and themes.

Supported Languages

Code Language Game Names Genres/Themes
en English (default)
zh-CN Simplified Chinese
zh-TW Traditional Chinese
zh Chinese
ja Japanese
ko Korean
pt-BR Brazilian Portuguese

Game Name Localization

Game names are fetched from IGDB using two sources (in priority order):

  1. game_localizations - Official localized names by region
  2. alternative_names - Alternative titles with language comments

If no localized name is found, the original English name is returned.

Note: IGDB's localized name coverage varies by game. Many games may not have Chinese or other language names available.

Generate Translations

To add or update genre/theme translations, use the translation generator script:

bun run generate-translations --lang zh-CN,ja,ko

Required Environment Variables:

AI_API_KEY=your-api-key
AI_BASE_URL=https://your-api-endpoint.com/v1
AI_MODEL=gpt-4o-mini

The script will:

  1. Fetch all genres and themes from IGDB
  2. Translate them using AI
  3. Save to src/i18n/genres.json and src/i18n/themes.json

Testing

Run the test script:

./igdb_service/test-requests.sh

Tests cover:

  • Valid requests with known Steam IDs
  • Cache behavior
  • Force refresh
  • Invalid IDs
  • Error cases
  • Edge cases

Architecture

┌─────────────┐
│   Client    │
└──────┬──────┘
       │ POST /api/games
       ▼
┌─────────────────┐
│  HTTP Server    │
│  (Bun.serve)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐     ┌──────────────┐
│  GameService    │────▶│ CacheManager │
└────────┬────────┘     └──────────────┘
         │                    │
         │              ┌─────▼──────┐
         │              │  SQLite DB │
         │              └────────────┘
         ▼
┌─────────────────┐
│   IGDBClient    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   IGDB API      │
│  (Twitch OAuth) │
└─────────────────┘

Flow:

  1. Client sends Steam IDs to /api/games
  2. Service checks SQLite cache
  3. For cache misses, queries IGDB:
    • Maps Steam IDs → IGDB IDs (external_games)
    • Fetches game details (games endpoint)
  4. Transforms and caches results
  5. Returns partial success response

Cache Management

View cached games:

bun --eval "
const { Database } = require('bun:sqlite');
const db = new Database('./data/cache.db');
const games = db.query('SELECT steam_id, cached_at FROM games').all();
console.table(games);
db.close();
"

Clear cache:

rm ./data/cache.db

Cache will be recreated on next request.

Rate Limiting

IGDB free tier: 4 requests/second

The service implements:

  • Batch processing (10 Steam IDs per request)
  • 250ms delay between batches
  • Permanent caching to minimize API calls

Troubleshooting

"Failed to get OAuth token"

"IGDB API error: 429"

  • Rate limit exceeded
  • Wait a few seconds and retry
  • Check for excessive forceRefresh usage

"No mapping found for Steam ID"

  • Steam game not in IGDB database
  • Steam ID incorrect or game not released
  • Check Steam store page for correct app ID

Project Structure

igdb_service/
├── src/
│   ├── index.ts         # HTTP server + main entry
│   ├── service.ts       # Game service orchestration
│   ├── igdb-client.ts   # IGDB API client + OAuth
│   ├── cache.ts         # SQLite cache manager
│   ├── transformer.ts   # Data transformation
│   ├── types.ts         # TypeScript definitions
│   ├── enums.ts         # IGDB enum mappings
│   └── i18n/
│       ├── index.ts     # Translation loader
│       ├── genres.json  # Genre translations
│       └── themes.json  # Theme translations
├── scripts/
│   └── generate-translations.ts  # AI translation generator
├── data/
│   └── cache.db         # SQLite database (generated)
├── .env                 # Environment variables
├── package.json
├── tsconfig.json
└── README.md

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages