NestJS Elasticsearch
Integration guide for adding Elasticsearch-backed search to a NestJS GraphQL + MongoDB application. It fixes the architectural boundary (generic infra, a shared search layer, one owned index per feature), then covers the full lifecycle: the search projection, indexing and sync, ID-only querying with MongoDB hydration, cursor pagination, cross-entity derived fields, running with search optional via a configurable backend seam, testing without a live cluster, and the operations and DX around all of it.
The guiding contract is Elasticsearch finds, MongoDB serves: Elasticsearch is a pure search index that returns identifiers, and the requested entities are always hydrated from MongoDB, the source of truth.
When to Apply
Reference these guidelines when:
- Adding search to an existing NestJS GraphQL + MongoDB application
- Wiring the
@nestjs/elasticsearchclient and the shared search layer - Defining or changing an index mapping or the search projection
- Implementing MongoDB-to-Elasticsearch sync, reindexing, or index migrations
- Writing a search query, cursor pagination, or result hydration
- Keeping a derived value (counts, ratings) searchable across entities
- Making search optional, or swapping the backend, from configuration without a cluster
- Testing search with a mocked backend seam, or against a real throwaway cluster
- Debugging stale search results or indexing storms
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Architecture | CRITICAL | arch- |
| 2 | Indexing & Sync | CRITICAL | sync- |
| 3 | Schema & Mapping | HIGH | map- |
| 4 | Querying | HIGH | query- |
| 5 | Cross-Entity | MEDIUM-HIGH | rel- |
| 6 | Operations & DX | MEDIUM | ops- |
| 7 | Configuration & Environments | HIGH | cfg- |
| 8 | Testing | MEDIUM-HIGH | test- |
Quick Reference
1. Architecture (CRITICAL)
arch-base-search-repository- Put Shared Index and Query Logic in a Base Repositoryarch-entity-vs-search-document- Keep Elasticsearch Fields Out of the Entityarch-feature-owned-index- Let Each Feature Own and Register Its Indexarch-infra-module- Isolate the Elasticsearch Client in a Generic Infra Module
2. Indexing & Sync (CRITICAL)
sync-bulk-index- Index in Batches with the Bulk APIsync-debounce-same-id- Debounce Same-Id Burstssync-delete-immediately- Delete from the Index Immediatelysync-enqueue-index- Enqueue Indexing; Never Write to Elasticsearch Inlinesync-full-reindex-alias-swap- Reindex Through a Versioned Index and Swap the Aliassync-idempotent-index-creation- Create Indices Idempotently with Deterministic Names
3. Schema & Mapping (HIGH)
map-analyzers- Choose Text Analyzers Deliberatelymap-derived-fields- Compute Derived and Relation Fields in a Batch Mappermap-exclude-non-search-fields- Index Only Fields With a Search Purposemap-mapping-object- Declare the Mapping as a Typed Objectmap-typed-search-document- Type the Search Document Against the Entity
4. Querying (HIGH)
query-cursor-pagination- Paginate with search_afterquery-hydrate-restore-order- Hydrate from MongoDB and Restore Relevance Orderquery-ids-only-search- Search Returns IDs Onlyquery-sort-tiebreaker- Always Include a Unique Sort Tiebreaker
5. Cross-Entity (MEDIUM-HIGH)
rel-derived-counter-cache- Cache Derived Counters on the Owning Documentrel-owning-module-subscribes- The Owning Module Subscribes; Others Emit Their Own Events
6. Operations & DX (MEDIUM)
ops-cli-scripts- Provide Migration and Reindex CLI Commandsops-explain-debug- Provide an Explain Hookops-health-check- Include Elasticsearch in Health Checksops-log-es-requests- Log Elasticsearch Requests and Responsesops-no-cron-for-indexing- Never Schedule Indexing with an In-Process Timer
7. Configuration & Environments (HIGH)
cfg-mongo-fallback-backend- Fall Back to a Deterministic MongoDB Findercfg-search-backend-seam- Put Search Behind a Backend Seam
8. Testing (MEDIUM-HIGH)
test-mock-elasticsearch-seam- Mock the Search Backend Seam in Unit Teststest-real-es-for-query-semantics- Test Query Semantics Against a Real Elasticsearch
How to Use
Read individual rule files for detailed explanations and code examples:
rules/arch-entity-vs-search-document.md
rules/sync-full-reindex-alias-swap.md
rules/query-ids-only-search.md
Each rule file contains:
- Brief explanation of why it matters and which layer it applies to
- Incorrect code example with explanation
- Correct code example with explanation
- Additional context and references
Relation to Other Skills
nestjs-graphql-structure— file placement. This skill adds thesearch/folder to a feature and thecommon/search/+infra/elasticsearch/layers; it follows that skill's feature-module, repository, and event rules.nestjs-best-practices— behavioral rules for services, events, queues, config, and health checks that this skill's examples build on.- One deliberate exception —
nestjs-graphql-structure'sservice-background-jobsrule keeps processors in the owning feature. The search pipeline runs once incommon/search/and resolves each feature's repository through the index registry instead, because the pipeline is identical for every feature; seearch-base-search-repository.
Full Compiled Document
For the complete guide with all rules expanded in a single document, see AGENTS.md.