Skip to Content

Nested externalIds by Finish

Date2026-03-24
StatusAccepted
Decision Makers@benjaminW78

Context and Problem Statement

Other game importers (Pokemon, Sorcery, One Piece) store marketplace IDs using a flat externalIds structure: { marketplace: id }. Each printing maps to a single TCGPlayer product ID and a single Cardmarket product ID.

SWU is different. Each variant + finish combination is a separate product on TCGPlayer and Cardmarket. For example, “Darth Vader - Dark Lord of the Sith” in the Standard variant has one TCGPlayer ID for Normal and another for Foil. The same card in Hyperspace variant has yet another pair of IDs. We need a way to store multiple marketplace IDs per printing, keyed by finish.

Decision Drivers

  • Each SWU variant+finish combo is a distinct product on marketplaces with its own price
  • The price importer’s getAllTcgPlayerIds() needs to iterate over all marketplace IDs to fetch prices
  • The ProductExternalIds type already supports both number and ExternalIdsByFinish (a Record<string, number | undefined>)
  • Other importers should not be affected by this change

Considered Options

  1. Nested structure: { marketplace: { finish: id } } — store IDs per finish within each marketplace
  2. Flat structure: { marketplace: id } — store a single ID per marketplace, losing finish-level granularity
  3. Separate products per finish: Create entirely separate product documents for each finish of the same card

Decision Outcome

Chosen option: nested structure { marketplace: { finish: id } }, because it accurately represents the one-to-many relationship between a printing and its marketplace products without duplicating card data.

// SWU printing externalIds structure externalIds: { [SupportedMarketplace.Tcgplayer]: { [StarWarsUnlimitedFinish.NORMAL]: 12345, [StarWarsUnlimitedFinish.FOIL]: 12346, }, [SupportedMarketplace.Cardmarket]: { [StarWarsUnlimitedFinish.NORMAL]: 67890, }, }

This is supported by the existing ProductExternalIds type which accepts both number | ExternalIdsByFinish for each marketplace key.

Consequences

Positive

  • Accurate marketplace ID mapping — each finish gets the correct price from the correct product
  • The price importer’s getAllTcgPlayerIds() correctly iterates over finish → id pairs
  • No duplication of card attributes across finishes of the same variant
  • Compatible with the existing ProductExternalIds type without schema changes

Negative

  • Different pattern from other game importers — code that expects flat externalIds may need conditional handling
  • Downstream consumers (admin UI, verification tools) must handle both flat and nested structures
  • Slightly more complex data shape to reason about when debugging
Last updated on