Skip to main content
GROQ (Graph-Relational Object Queries) is Sanity’s query language. All content fetching in the Astro app goes through GROQ queries defined in astro-app/src/lib/sanity.ts.

defineQuery pattern

Every query is wrapped with defineQuery from the groq package. This enables Sanity TypeGen to extract the query’s return type automatically and write it to sanity.types.ts.
import groq, { defineQuery } from 'groq'

export const ALL_SPONSORS_QUERY = defineQuery(groq`
  *[_type == "sponsor" && hidden != true && ($site == "" || site == $site)]
  | order(name asc){
    _id, name, "slug": slug.current,
    logo{ asset->{ _id, url, metadata { lqip, dimensions } }, alt, hotspot, crop },
    tier, description, website, featured
  }
`)
The TypeGen-generated return type ALL_SPONSORS_QUERY_RESULT is imported in the same file and used wherever the query result is typed:
import type { ALL_SPONSORS_QUERY_RESULT } from '@/sanity.types'

export type Sponsor = ALL_SPONSORS_QUERY_RESULT[number]

How queries are used at build time

The Astro app uses a static build by default. Queries run at build time inside getStaticPaths or in the page frontmatter:
  1. getStaticPaths calls slug-listing queries (e.g. ALL_PAGE_SLUGS_QUERY) to enumerate all routes.
  2. prefetchPages() batch-fetches all page data in parallel (6 at a time) and caches it in a module-level Map.
  3. Per-page renders call helper functions (getPage, getAllSponsors, etc.) which return from the cache instantly.

getSiteParams() for multi-site filtering

Every query that targets site-aware document types includes a $site parameter. getSiteParams() returns the correct value for the current build:
export function getSiteParams(): Record<string, string> {
  return isMultiSite ? { site: SITE_ID } : { site: '' }
}
  • On the production dataset: { site: '' } — the ($site == "" || site == $site) condition short-circuits on the empty string, so all documents match.
  • On the rwc dataset: { site: 'rwc-us' } or { site: 'rwc-intl' } — only documents belonging to the current site are returned.
SITE_ID and DATASET are resolved at build time from PUBLIC_SITE_ID and PUBLIC_SANITY_DATASET environment variables, which Vite statically replaces.

loadQuery() wrapper

All queries go through loadQuery() rather than calling sanityClient.fetch() directly. When Visual Editing is active, it switches to the drafts perspective, enables stega encoding, and accumulates sync tags for the Live Content API:
export async function loadQuery<T>({ query, params }) {
  const perspective = visualEditingEnabled ? 'drafts' : 'published'
  const response = await sanityClient.fetch<T>(query, params ?? {}, {
    filterResponse: false,
    perspective,
    resultSourceMap: visualEditingEnabled ? 'withKeyArraySelector' : false,
    stega: visualEditingEnabled,
    ...(visualEditingEnabled ? { token } : {}),
  })
  return { result: response.result, syncTags: response.syncTags ?? [] }
}

Named queries exported from sanity.ts

ExportDescription
SITE_SETTINGS_QUERYFetch the singleton site settings document by ID
ALL_PAGE_SLUGS_QUERYAll published page slugs (for getStaticPaths)
PAGE_BY_SLUG_QUERYFull page data including all block projections
ALL_SPONSORS_QUERYAll visible sponsors ordered by name
ALL_SPONSOR_SLUGS_QUERYAll visible sponsor slugs (for getStaticPaths)
SPONSOR_BY_SLUG_QUERYSingle sponsor with associated projects
ALL_PROJECTS_QUERYAll projects with resolved sponsor references
ALL_PROJECT_SLUGS_QUERYAll project slugs (for getStaticPaths)
PROJECT_BY_SLUG_QUERYSingle project with team, mentor, and linked testimonials
ALL_TESTIMONIALS_QUERYAll testimonials with linked project reference
ALL_EVENTS_QUERYAll events ordered by date
ALL_EVENT_SLUGS_QUERYAll event slugs (for getStaticPaths)
EVENT_BY_SLUG_QUERYSingle event with SEO fields
EVENTS_BY_MONTH_QUERYEvents within a date range (for calendar navigation)
SPONSOR_BY_EMAIL_QUERYFind a sponsor by contact or allowed email (portal auth)
SPONSOR_PORTAL_QUERYSponsor + projects for the portal page
SPONSOR_PROJECTS_API_QUERYProjects for a sponsor by ID (portal API endpoint)
SPONSOR_PROJECTS_QUERYProjects for a sponsor by email (portal progress page)

Helper functions

FunctionCaches resultDescription
getSiteSettings()YesFetch site settings singleton
getAllSponsors()YesFetch all visible sponsors
getAllProjects()YesFetch all projects
getAllTestimonials()YesFetch all testimonials
getAllEvents()YesFetch all events
getPage(slug)Yes (via prefetch)Fetch a single page by slug
getSponsorBySlug(slug)NoFetch a single sponsor by slug
getProjectBySlug(slug)NoFetch a single project by slug
getEventBySlug(slug)NoFetch a single event by slug
prefetchPages(slugs)Populates cacheBatch-fetch all pages in parallel
getSponsorProjects(email)NoFetch projects for a sponsor by email
Cached helpers (getAllSponsors, etc.) bypass the cache and re-fetch when Visual Editing is enabled, so the preview always shows the latest draft data.

Build docs developers (and LLMs) love