turbo query
Purpose and Scope
turbo query is Turborepo’s repository-inspection command for teams that need more detail than a task run, package list, or browser visualization can provide. The command exposes a GraphQL interface into the monorepo model, so you can ask targeted questions about packages, tasks, dependency relationships, and affected work. It is intended for diagnosis and automation: use it when you need to understand why work is selected, which packages match a task shape, or which parts of the repository are creating broad invalidation.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
The command belongs with Turborepo’s other repository-understanding tools. turbo devtools gives a browser-based visualization of the package graph, turbo ls lists packages and supports filters, and turbo run without task names reports available tasks and the packages where they are defined. turbo query goes deeper by letting you formulate structured GraphQL queries, returning JSON-shaped responses that can be read by humans or consumed by CI scripts.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
This page focuses on the command reference concerns that matter in practice: how to select packages and tasks, how affected queries fit CI, what output shape to expect, and how to migrate skip logic away from turbo-ignore. It also ties the command back to package graph, task graph, caching, and development workflow concepts, because query results are most useful when you understand the model being queried rather than treating the command as a standalone reporting tool.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx
Relevant Source Files
apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx- Defines theturbo queryguide entry, explains that the command provides a GraphQL interface starting in Turborepo 2.2.0, and shows concrete package and affected-package queries.apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx- Places query-driven affected detection in the CI workflow alongside Remote Caching, task filtering, and the--affectedflag.apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx- Defines tasks as scripts registered inturbo.json, explainsdependsOn, and provides the task-graph vocabulary that query users inspect.apps/docs/content/docs/crafting-your-repository/caching.mdx- Explains cache fingerprints, deterministic task assumptions, and cache misses, which are common reasons to inspect invalidation withturbo query.apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx- Explains internal packages and the package graph relationships derived frompackage.jsondependencies.apps/docs/content/docs/crafting-your-repository/developing-applications.mdx- Shows package filtering for development tasks and long-liveddevtask configuration, useful context for selecting task entry points.
Command Model and Output
At its core, turbo query evaluates a GraphQL request against Turborepo’s understanding of the repository. The documentation example queries packages(filter: { has: { field: TASK_NAME, value: "build"}}) and requests items { name }. The response is a JSON object with a top-level data field, then the requested GraphQL field, then items. In the example, packages named //, docs, and web are returned because those packages expose the requested task.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
turbo query "query { packages(filter: { has: { field: TASK_NAME, value: \"build\"}}) { items { name } } }"{
"data": {
"packages": {
"items": [
{ "name": "//" },
{ "name": "docs" },
{ "name": "web" }
]
}
}
}Because the response follows the requested GraphQL selection set, the output format is most useful when you ask only for fields your automation needs. For a local diagnosis, a short items { name } query is easy to read. For CI, include the fields that explain why a package is affected so a skipped or executed job can print a meaningful reason. For scripts, treat the response as structured JSON instead of scraping terminal prose.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx, apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx
Package and Task Selection
Package selection in turbo query builds on Turborepo’s package graph. Internal packages are discovered through workspace package metadata and dependency declarations in package.json; those relationships let Turborepo know which applications depend on shared libraries. When a query asks for packages with a task, direct dependents, or affected status, it is querying that graph rather than scanning random directories. This is why accurate workspace configuration and package names are important before the command can provide useful answers.
Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
Task selection uses the same task vocabulary as turbo run. A task is a package script that Turborepo can run when it is registered in the root turbo.json tasks object. The configuring-tasks guide explains that each key in tasks can be executed by turbo run, and Turborepo searches packages for matching package.json scripts. Therefore a query such as “packages that have a build task” is really asking which packages participate in that configured task surface.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
The docs also show a diagnostic query for packages whose direct dependent count is greater than ten. That use case is important for cache troubleshooting: a small shared package imported throughout the repository can cause many downstream tasks to become invalidated. If turbo query identifies a package like utils as a high-fanout dependency, the next design step may be to split it into smaller packages so unrelated changes no longer invalidate as much of the graph.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx
turbo query "query { packages(filter: { greaterThan: { field: DIRECT_DEPENDENT_COUNT, value: 10 } }) { items { name } } }"Affected Queries in CI
Affected queries connect repository inspection to CI efficiency. The constructing-CI guide explains that CI pipelines can run the same tasks registered in turbo.json, use Remote Caching with TURBO_TOKEN and TURBO_TEAM, and filter work by package, directory, Git history, or --affected. turbo query complements that flow by showing which packages are affected between two revisions and why they were invalidated, which is especially useful when a pipeline is doing more work than expected.
Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
A representative GraphQL query is affectedPackages(base: "HEAD^", head: "HEAD") { items { reason { __typename } } }. The documented response includes reasons such as FileChanged for a package changed directly and DependencyChanged for packages invalidated because something they depend on changed. This distinction is the key diagnostic value: a package may be selected not because its own files changed, but because the package graph says one of its dependencies changed.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
turbo query "query { affectedPackages(base: \"HEAD^\", head: \"HEAD\") { items { name reason { __typename } } } }"Use affected output in CI as an explanation layer, not only as a yes-or-no gate. If a build is unexpectedly running, print the package names and reason types so maintainers can decide whether to adjust package boundaries, task inputs, or filters. The CI guide also warns that Git-history-based filtering requires history to be available on the machine, so shallow clones can make affected-style decisions unreliable until the CI checkout is configured with sufficient history.
Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
Migration from turbo-ignore
turbo-ignore was historically used to decide whether a package or its dependencies changed before running CI work. The current guidance is to move skip-build decisions to turbo query affected, because it provides more precise task-level change detection and uses the same repository model as Turborepo itself. In practice, that means replacing a package-only skip command with a query or affected workflow that can report both selected packages and invalidation reasons.
Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
A migration should start by writing down what the old skip step protected: a deployment for one workspace, a test suite for a package subtree, or an expensive build for a shared library. Next, model that decision using package names, task names, and Git base/head revisions. Then run turbo query in CI and compare its selected packages with the old turbo-ignore decision for several pull requests. Once the output matches intent, remove the deprecated package and keep the query output in logs for auditability.
Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx
Compact Reference
| Area | Reference | Notes |
|---|---|---|
| Command | turbo query "query { ... }" | Executes a GraphQL request against Turborepo’s repository model. |
| Package filtering example | packages(filter: { has: { field: TASK_NAME, value: "build"}}) | Finds packages that define a matching task name. |
| High-fanout example | packages(filter: { greaterThan: { field: DIRECT_DEPENDENT_COUNT, value: 10 } }) | Finds packages imported by many dependents, useful for cache-miss diagnosis. |
| Affected example | affectedPackages(base: "HEAD^", head: "HEAD") | Compares two Git revisions and returns affected package items. |
| Reason field | reason { __typename } | Distinguishes direct file changes from dependency-driven invalidation. |
| Output | JSON GraphQL response with data and requested fields | Shape depends on the selection set. |
For next steps, use turbo ls when you only need the package list, turbo run without tasks when you need available task names, and turbo devtools when graph visualization is easier than a query. Use turbo query when the question is specific enough to benefit from GraphQL or when CI needs a structured, machine-readable explanation of affected work.
Sources: apps/docs/content/docs/crafting-your-repository/understanding-your-repository.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx