mdbasequery is an Obsidian Bases-compatible query engine for Markdown vaults.
It ships as:
- a CLI (
mdbasequery) for scripts and terminal workflows, - a TypeScript library for embedding query execution in applications.
Supported runtimes: Node.js 20+, Bun, and Deno 2.x.
CLI Manual
Install and Run
You can run the CLI without installing globally:
npx mdbasequery --help bunx mdbasequery --help
Or install globally:
npm install -g mdbasequery
# then
mdbasequery --helpCommand Model
Command form:
There is one command surface; all behavior is controlled through options.
Query Input Modes
mdbasequery supports three ways to provide the query definition:
.basefile via--base <path>- Inline YAML via
--yaml <yaml-text> - Flag-built query mode (
--filter,--select,--sort, etc.)
If --base/--yaml are not provided, the query is built from flags.
Common CLI Examples
Run a .base file against a vault:
mdbasequery \ --base ./fixtures/queries/basic.base \ --dir ./fixtures/vaults/basic \ --format json
Use inline YAML (heredoc to avoid shell escaping issues):
QUERY_YAML=$(cat <<'EOF' views: - type: table name: default filters: score >= 7 properties: - title - score EOF ) mdbasequery --yaml "$QUERY_YAML" --dir ./fixtures/vaults/basic --format md
Use CLI flags instead of YAML:
mdbasequery \
--dir ./fixtures/vaults/basic \
--filter "score >= 7" \
--select title \
--select score \
--sort score:desc \
--format csvSelect a specific view from a .base file and write output to disk:
mdbasequery \ --base ./fixtures/queries/grouped.base \ --view grouped \ --dir ./fixtures/vaults/basic \ --format yaml \ --out ./result.yaml
Disable strict symbol/function checking:
mdbasequery --yaml "views: [{ type: table, name: default }]" --no-strictCLI Option Reference
| Option | Type | Description |
|---|---|---|
--dir <path> |
string | Target directory to scan (default: current directory). |
--base <path> |
string | Path to Obsidian-style .base YAML file. |
--yaml <yaml-text> |
string | Inline YAML text. |
--view <name> |
string | View name to run (default: first view). |
| `--format <json | jsonl | yaml |
--out <path> |
string | Write serialized output to file instead of stdout. |
--strict |
flag | Enable strict mode (default behavior). |
--no-strict |
flag | Disable strict mode for unknown symbols/functions. |
--include <glob> |
repeatable string | Include path glob(s), matched against vault-relative paths. |
--exclude <glob> |
repeatable string | Exclude path glob(s), matched against vault-relative paths. |
--debug |
flag | Enable debug diagnostics in result metadata. |
--filter <expr> |
repeatable string | Add filter expression(s) in flag-built mode. |
--select <property> |
repeatable string | Select/project property/column in flag-built mode. |
| `--sort <prop:asc | desc>` | repeatable string |
--group-by <property> |
string | Group rows by one property in flag-built mode. |
--limit <n> |
number | Limit row count in flag-built mode. |
--version, -v |
flag | Show CLI version number. |
--help, -h |
flag | Show CLI help text. |
Output Formats
json: one structured object with rows, columns, summaries, stats, diagnostics.jsonl: one JSON object per row.yaml: YAML representation of the full result object.csv: projected columns in declared/selected order.md: Markdown table for quick human review.
Exit Codes and Diagnostics
- Exit code
0: successful query execution. - Exit code
1: fatal CLI/query error, or row-level diagnostics recorded as errors.
Diagnostics are returned as part of result metadata (and included in structured formats).
Compatibility and Behavior Notes
- Filters and formulas share the same expression language.
- Effective filter per view is
global filter AND view filter. - Formula dependencies are topologically ordered; cycles fail clearly.
- Strict mode is enabled by default.
- Summary formulas evaluate with
valuesbound to selected column values. - Output ordering is deterministic.
thiscontext in CLI/library mode is a file-like record of the base file when--base/basePathis used, otherwise an empty record at the vault root. Supports the documented Obsidian patterns (this.file.folder,file.hasLink(this.file)).
Library Manual
Installation
npm install mdbasequery
# or
bun add mdbasequeryQuick Start
import { queryBase, serializeResult } from "mdbasequery"; const result = await queryBase({ dir: "./fixtures/vaults/basic", yaml: ` views: - type: table name: default filters: score >= 7 properties: - title - score `.trim(), strict: true, }); console.log(result.rows.length); console.log(serializeResult(result, "json"));
API Overview
Primary exports:
queryBase(options): Promise<QueryResult>parseBaseYaml(input): QuerySpeccompileQuery(spec, options?): CompiledQueryrunCompiledQuery(compiled, source?): Promise<QueryResult>serializeResult(result, format): string
Runtime adapters:
detectRuntimeAdapter()nodeAdapter,bunAdapter,denoAdapter
queryBase Options
queryBase accepts a QueryBaseOptions object with:
spec?: QuerySpec- already parsed query spec.basePath?: string- path to.basefile.yaml?: string- inline YAML text.view?: string- selected view name.dir?: string- vault root directory.strict?: boolean- strict symbol/function behavior (default true).include?: string[]- include globs.exclude?: string[]- exclude globs.debug?: boolean- enable debug diagnostics.adapter?: RuntimeAdapter- runtime adapter injection.
Provide exactly one query source among spec, basePath, or yaml.
Compile Once, Run Many
import { compileQuery, parseBaseYaml, runCompiledQuery } from "mdbasequery"; const spec = parseBaseYaml(` views: - type: table name: default filters: score >= 7 `.trim()); const compiled = compileQuery(spec, { strict: true }); const first = await runCompiledQuery(compiled, { dir: "./vault-a" }); const second = await runCompiledQuery(compiled, { dir: "./vault-b" }); console.log(first.rows.length, second.rows.length);
Result Shape
QueryResult contains:
rows: row contexts withnote,file,formula,this, andprojectedfields.columns: projected column order.groups?: grouped rows when grouping is active. Group rows are projected records (same shape asrows[]entries); internal row data (file,note,this,formula,raw) is never exposed in serialised output.summaries?: computed summary values.stats: scan/match timing and counts.diagnostics: warnings and errors.
Error Handling
- Schema/YAML issues throw validation errors (for example from
parseBaseYaml). - Strict mode (default) rejects identifiers that exist nowhere in the vault (typo'd properties, undeclared
formula.Xreferences) and unknown functions with clear errors naming the identifier and the containing expression. Properties present on only some notes remain valid and evaluate as empty on notes that lack them. - Formula cycles throw explicit cycle errors.
- CLI returns exit code
1for failures and diagnostic errors.
Development Manual
Prerequisites
- Bun (primary dev runtime)
- Node.js 20+
- Deno 2.x
Setup
Build and Test Commands
bun run build bun run lint-typecheck bun run test:bun bun run test:node bun run test:deno bun run ci
bun run ci runs the required local validation suite:
- typecheck/lint
- Bun tests
- Node conformance tests
- Deno conformance tests
CI Jobs
GitHub Actions runs:
lint-typechecktest-buntest-nodetest-denocompat-smoke-cli(matrix across Bun/Node/Deno)
Repository Layout
src/core- schema, expression engine, query engine, serialization, indexing.src/runtime-adapters- runtime-specific filesystem/path adapters.src/cli.ts- CLI entrypoint.tests- Bun test suite (unit/integration/docs smoke).test-runtime- Node/Deno conformance tests.fixtures- vault and query fixtures..github/workflows/ci.yml- CI workflow definitions.
Release Checklist
- Ensure
bun run cipasses locally. - Ensure GitHub Actions checks are green.
- Build artifacts (
dist) and package metadata are correct. - Publish to npm.
- Verify
npx mdbasequery --helpandbunx mdbasequery --helppost-release.