Guides
Query builder basics
The query builder API lets you filter and sort any dataset with readable, chainable method calls -- no manual array manipulation required.
How it works
Every query builder wraps an internal array of typed data. When you call a filter or sort method, it 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 { crops } from 'stardew-valley-data'
const results = crops() // Start with all crops
.bySeason('summer') // Filter to summer crops
.regrowing() // Keep only regrowing crops
.sortBySellPrice('desc') // Sort by price, highest first
.get() // Extract the array
Chaining filters
Filters are additive -- each one narrows the results further. You can chain as many as you need:
import { crops } from 'stardew-valley-data'
// Summer crops sold at Pierre's that can grow into giant crops
const giantSummerCrops = crops()
.bySeason('summer')
.byShop('Pierre')
.giant()
.get()
Crops example
The CropQuery provides filters for season, category, shop, regrowing, giant, trellis, multi-season, extra harvest, available in shop, and edibility:
import { crops } from 'stardew-valley-data'
// Fast-growing spring vegetables
const quickVeggies = crops()
.bySeason('spring')
.byCategory('Vegetable')
.sortByGrowDays('asc')
.get()
// All multi-season regrowing crops, sorted by value
const perennials = crops()
.multiSeason()
.regrowing()
.sortBySellPrice('desc')
.get()
Fish example
The FishQuery provides filters for season, catch type, weather, and location:
import { fish } from 'stardew-valley-data'
// Rod-caught fish available on rainy spring days
const rainySpringFish = fish()
.bySeason('spring')
.byCatchType('rod')
.byWeather('rainy')
.sortByDifficulty('desc')
.get()
// All ocean fish sorted by name
const oceanFish = fish().byLocation('ocean').sortByName().get()
Villagers example
The VillagerQuery provides filters for marriageable status and birthday season:
import { villagers } from 'stardew-valley-data'
// All marriageable villagers sorted by birthday
const singles = villagers().marriageable().sortByBirthday().get()
// Spring birthdays alphabetically
const springBirthdays = villagers()
.byBirthdaySeason('spring')
.sortByName()
.get()
Animals example
The AnimalQuery provides filters for pets, farm animals, building type, harvest method, and purchasability:
import { animals } from 'stardew-valley-data'
// All coop animals
const coopAnimals = animals().byBuilding('Coop').get()
// Farm animals you can buy at Marnie's
const purchasable = animals().farmAnimals().purchasable().get()
Trees example
The TreeQuery provides filters for fruit trees, wild trees, season, and tappable trees:
import { trees } from 'stardew-valley-data'
// Fruit trees that produce in summer, by value
const summerFruit = trees()
.bySeason('summer')
.sortByProduceSellPrice('desc')
.get()
// Wild trees you can tap
const tappable = trees().tappable().get()
Sorting
Sort methods reorder the results without removing any items. Each sort method accepts an order parameter -- typically 'asc' or 'desc'.
import { crops } from 'stardew-valley-data'
// Cheapest crops first
const cheapest = crops().sortBySellPrice('asc').get()
// Fastest-growing crops first (default for sortByGrowDays is 'asc')
const fastest = crops().sortByGrowDays().get()
Sorts can be combined with filters in any order:
import { fish } from 'stardew-valley-data'
// Sort first, then filter -- the filter preserves the sort order
const sorted = fish().sortBySellPrice('desc').bySeason('summer').get()
// Filter first, then sort -- equivalent result
const filtered = fish().bySeason('summer').sortBySellPrice('desc').get()
Combining operations
Branching from a shared base
Since each method returns a new instance, you can branch from any intermediate query:
import { crops } from 'stardew-valley-data'
const summer = crops().bySeason('summer')
const summerFruit = summer.byCategory('Fruit').get()
const summerVeggies = summer.byCategory('Vegetable').get()
const summerCount = summer.count()
Passing results between queries
You can take the output of one query and feed it into another module's factory function using the source parameter:
import { crops, fish } from 'stardew-valley-data'
// Get crop names, then use them to find related fish (conceptual example)
const springCropNames = crops()
.bySeason('spring')
.get()
.map((c) => c.name)
// Use the results in application logic
console.log(`Spring crops: ${springCropNames.join(', ')}`)
Wrapping custom data in a query builder
Every factory function accepts a source array, so you can apply the query builder to your own data:
import { crops, Crop } from 'stardew-valley-data'
// Imagine you fetched a subset from your API
const myFavorites: Crop[] = await fetchFavoriteCrops()
// Wrap in a query builder for filtering and sorting
const sorted = crops(myFavorites).regrowing().sortBySellPrice('desc').get()
Collecting results
As an array
const allCrops = crops().get() // Crop[]
const names = crops()
.get()
.map((c) => c.name) // string[]
First item only
const best = crops().sortBySellPrice('desc').first() // Crop | undefined
By ID or name
const crop = crops().find('248') // Crop | undefined
const melon = crops().findByName('Melon') // Crop | undefined
Count
const total = crops().count() // number
Common patterns
Building a lookup table
import { villagers } from 'stardew-valley-data'
const villagerMap = new Map(
villagers()
.get()
.map((v) => [v.id, v]),
)
const penny = villagerMap.get('penny_id')
Conditional filtering
import { crops } from 'stardew-valley-data'
function queryCrops(season?: string, regrowing?: boolean) {
let query = crops()
if (season) {
query = query.bySeason(season as any)
}
if (regrowing) {
query = query.regrowing()
}
return query.sortBySellPrice('desc').get()
}
Aggregating data
import { crops } from 'stardew-valley-data'
const springCrops = crops().bySeason('spring').get()
const totalValue = springCrops.reduce((sum, c) => sum + c.cropSellPrice, 0)
const avgValue = totalValue / springCrops.length
console.log(`Average spring crop value: ${avgValue.toFixed(0)}g`)
Next steps
- Explore TypeScript integration for type-safe querying
- Learn about the cross-module search utility
- See individual module pages (e.g., Crops, Fish) for module-specific filters and sorts