Guides

Query builder basics

The query builder API lets you filter and look up any dataset with readable, chainable method calls: no manual array manipulation required.


How it works

Every namespace method (.defenses(), .troops(), .heroes(), and so on) returns a query builder wrapping an internal array of typed data. Calling a filter or per-entity accessor method returns a new query builder containing only the matching items. You finish the chain with a terminal method (get(), first(), find(), findByName(), or count()) to extract results.

import { home } from 'clash-of-clans-data'

const results = home()
  .defenses() // Start with all Home Village defenses
  .byTownHall(12) // Filter to defenses available at TH12
  .byDamageType('splash') // Keep only splash-damage defenses
  .get() // Extract the array

Filtering by Town Hall / Builder Hall / Capital Hall

The most common filter across every base is scoping a category to a progression level. The method name follows the base: byTownHall(n) for Home Village, byBuilderHall(n) for Builder Base, and byDistrictHall(n) / byCapitalHall(n) for Clan Capital categories.

import { home, builder, clanCapital } from 'clash-of-clans-data'

const th12Defenses = home().defenses().byTownHall(12).get()
const bh9Troops = builder().troops().byBuilderHall(9).get()
const dh5Defenses = clanCapital().defenses().byDistrictHall(5).get()

These return buildings or units that have at least one level available at or before the given level: not just ones unlocked exactly at that level.


Per-entity accessors

Every entity in a category has its own accessor method, named after the entity in camelCase. Chain it with .first() to get the single matching item:

import { home, builder, clanCapital } from 'clash-of-clans-data'

const cannon = home().defenses().cannon().first()!
const barbarianKing = home().heroes().barbarianKing().first()!
const battleMachine = builder().heroes().battleMachine().first()!
const superBarbarian = clanCapital().troops().superBarbarian().first()!

This is usually faster to read than findByName() for a known entity, and it's fully typed: your editor autocompletes the available accessors for each namespace.


Category-specific filters

Beyond Town Hall scoping, most categories add a handful of filters specific to that category. A few examples:

import { home, clan } from 'clash-of-clans-data'

// Home Village defenses: damage type, gear-up capability
const splashDefenses = home().defenses().byDamageType('splash').get()
const gearedUp = home().defenses().hasGearUp().get()

// Home Village troops: housing space, target type
const cheapTroops = home().troops().byHousingSpace(1).get()
const groundTroops = home().troops().byTargetType('ground').get()

// Clan levels: badge tier
const goldLevels = clan().levels().byBadge('gold').get()

See each category's own docs page (for example Home Village defenses or Home Village troops) for the full filter list for that namespace.


Combining operations

Branching from a shared base

Since every filter and accessor returns a new instance, you can branch from any intermediate query:

import { home } from 'clash-of-clans-data'

const th14 = home().defenses().byTownHall(14)

const splash = th14.byDamageType('splash').get()
const single = th14.byDamageType('single').get()
const total = th14.count()

Passing a custom source into a namespace

Several factory functions accept an optional source array so you can wrap your own pre-filtered data in the same query builder:

import { home, type HomeDefense } from 'clash-of-clans-data'

const myDefenses: HomeDefense[] = home().defenses().byTownHall(10).get()
const query = home().defenses() // scoped to all defenses by default

Collecting results

As an array

const allDefenses = home().defenses().get() // HomeDefense[]
const names = home()
  .defenses()
  .get()
  .map((d) => d.name) // string[]

First item only

const cannon = home().defenses().cannon().first() // HomeDefense | undefined

By ID or name

const byId = home().troops().find('barbarian') // HomeTroop | undefined
const dragon = home().troops().findByName('Dragon') // HomeTroop | undefined

Count

const total = home().defenses().byTownHall(12).count() // number

Common patterns

Building a lookup table

import { home } from 'clash-of-clans-data'

const troopMap = new Map(
  home()
    .troops()
    .get()
    .map((t) => [t.id, t]),
)

const barbarian = troopMap.get('barbarian')

Conditional filtering

import { home, type HomeVillageDefenses } from 'clash-of-clans-data'

function queryDefenses(townHall?: number, splashOnly?: boolean) {
  let query = home().defenses()

  if (townHall) {
    query = query.byTownHall(townHall)
  }
  if (splashOnly) {
    query = query.byDamageType('splash')
  }

  return query.get()
}

Reading upgrade levels

Most entities carry a levels array on the entity itself, rather than a separate query. Once you have an entity, index or filter its levels directly:

import { home } from 'clash-of-clans-data'

const cannon = home().defenses().cannon().first()!
const th12Level = cannon.levels.find((l) => l.townHallRequired <= 12)
const maxLevel = cannon.levels[cannon.levels.length - 1]

console.log(
  `Cannon has ${cannon.levels.length} levels, max hitpoints ${maxLevel.hitpoints}`,
)

Next steps

Previous
Core concepts