vtex.search-resolver is a VTEX IO backend service (builder node) that implements the GraphQL resolvers for the catalog, search, and orders schema published by vtex.search-graphql.
It is the resolver layer of the Intelligent Search stack. The GraphQL schema (the contract) lives in vtex.search-graphql; this app loads that schema at runtime via the dependency vtex.search-graphql/graphql and registers its resolvers + directives against it (see node/index.ts).
| App | Role |
|---|---|
vtex.search-graphql |
App-owned GraphQL schema (the contract) |
vtex.search-resolver (this repo) |
GraphQL resolvers for that schema |
vtex.intelligent-search-api |
Backend HTTP wrapper around the IS platform service |
vtex.search-result |
Store Framework PLP consuming the GraphQL surface |
vtex.delivery-promise-components |
Storefront blocks consuming catalog_count + pickup endpoints |
vtex.search-session |
Owns the search session namespace that feeds segment/facets |
| File | What it defines |
|---|---|
manifest.json |
App identity (vtex.search-resolver@1.102.2), builders (node, docs), dependencies (vtex.messages, vtex.catalog-api-proxy, vtex.search-graphql, vtex.rewriter, vtex.sae-analytics, vtex.intelligent-search-api), settingsSchema (slugifyLinks, shouldUseNewPDPEndpoint, shouldUseNewPLPEndpoint), policies (vtex.messages:translate-messages, vtex.catalog-api-proxy:catalog-proxy/authenticated-catalog-proxy, …) |
node/service.json |
Runtime: nodejs stack, 2048MB memory, 80 shared CPU @ 95%, ttl 30s, timeout 12s, 30–250 replicas, 2 workers. No routes — this service is a pure GraphQL plugin mounted by vtex.search-graphql via the schema. |
node/index.ts |
Service composition, LRU caches (segmentCache: 1000, searchCache: 3000, messagesCache: 3000, vbaseCache: 3000, appsCache: 1500, intschCache: 3000), per-client timeouts (default 3s, search: 6s, intelligentSearchApi: 9s), metrics.trackCache instrumentation. Imports schema from vtex.search-graphql/graphql. |
node/resolvers/index.ts |
Composes searchFieldResolvers, benefitsFieldResolvers, searchQueries, statsQueries. This is the resolver entry point. |
node/resolvers/search/ |
All catalog / search / autocomplete / product / brand / category / facets resolvers + tests (product.test.ts, productPriceRange.test.ts, assemblyOption.test.ts, newURLs.test.ts, index.test.ts) |
node/resolvers/benefits/, node/resolvers/stats/ |
Smaller resolver groups |
node/directives/ |
Schema directives (@cacheControl, @withSegment, etc.) registered via schemaDirectives in node/index.ts |
node/clients/index.ts |
Composes clients: search, checkout, rewriter, intelligentSearchApi, intsch |
node/clients/intelligent-search-api.ts |
ExternalClient against the vtex.intelligent-search-api IO app (note: NOT the IS platform service directly) |
node/clients/intsch/ |
Direct client to the IS platform backend (lower-level than intelligentSearchApi) |
node/clients/search.ts |
Legacy VTEX search backend (still used while the new IS endpoints are gated by settings) |
node/featureFlags.ts |
In-code feature toggles (currently VTEX_ASSETS_URL: true) |
node/commons/, node/services/, node/utils/ |
Shared helpers (caching keys, slug builders, query parsing, search state) |
node/typings/ |
Platform typings — regenerated by Toolbelt; do not hand-edit |
node/jest.config.js |
Jest 29 + ts-jest, coverageProvider: 'v8', `testRegex: '(.*(test |
node/mocks/, node/__mocks__/ |
Test fixtures shared across resolver tests |
policies.json |
Per-account policy declarations (parallel to manifest.json policies) |
dangerfile.js |
Danger rules for PR review |
.vtex/deployment.yaml, .vtex/catalog-info.yaml |
DK CI deployment definition (techdocs-v1 pipeline) + Backstage entity (referenceId: 6PIM3PYV) |
CHANGELOG.md |
Per-release changelog |
The service is a pure GraphQL plugin: node/index.ts exports new Service({...}) with no custom HTTP routes. vtex.search-graphql (declared as a dependency) provides the schema at vtex.search-graphql/graphql. At runtime, the IO platform composes the schema + the resolvers exported here.
GraphQL request ─► vtex.graphql-server / @vtex/api Service runtime
└─► schema (imported from vtex.search-graphql)
└─► resolvers (composed in node/resolvers/index.ts)
├─ Query.product / facets / autocomplete / topSearches / …
├─ field resolvers on Product, SKU, Brand, Category, Offer, AssemblyOption, …
├─ benefits field resolvers (promotions, discount lists)
└─ schemaDirectives (@cacheControl, @withSegment, …)
└─► clients.{search, intelligentSearchApi, intsch, checkout, rewriter}
| Directory | Purpose |
|---|---|
node/resolvers/search/ |
Catalog + search core. Sub-modules: • product.ts, brand.ts, category.ts — entity resolvers• autocomplete.ts, productPriceRange.ts, offer.ts, discount.ts — composed fields• assemblyOption.ts, itemMetadata*.ts — product configuration• newURLs.ts — URL/slug normalization• modules/ — internal helpers |
node/resolvers/benefits/ |
Promotions, discount lists, badges |
node/resolvers/stats/ |
Top-searches and analytics queries |
node/directives/ |
Schema directive implementations |
Three settings control the new-vs-legacy migration:
| Setting | Default | Effect |
|---|---|---|
slugifyLinks |
false |
When true, links use slugify instead of the default catalog slug |
shouldUseNewPDPEndpoint |
false |
When true, PDP queries hit the new IS endpoint via intelligentSearchApi / intsch; otherwise the legacy search client |
shouldUseNewPLPEndpoint |
false |
Same for PLP |
These are per-account app settings, not FeatureHub flags. Toggling them changes which client takes the hot path.
Configured in node/index.ts:
| Client | Cache | Capacity | Timeout | Notes |
|---|---|---|---|---|
search (legacy) |
searchCache |
3000 | 6000ms | concurrency: 10 |
intelligentSearchApi (IS via IO app) |
— | — | 9000ms | retries: 0, concurrency: 10 |
intsch (direct IS) |
intschCache |
3000 | — | — |
messagesGraphQL |
messagesCache |
3000 | 2000ms | concurrency: 10 |
segment |
segmentCache |
1000 | 3000ms | small + immutable |
vbase |
vbaseCache |
3000 | default | — |
apps |
appsCache |
1500 | 2000ms | retries: 2, concurrency: 5 |
| default | — | — | 3000ms | retries: 2 |
metrics.trackCache(<name>, <cache>) exposes hit/miss to platform observability.
From manifest.json policies:
| Policy | Purpose |
|---|---|
vtex.messages:translate-messages |
i18n via vtex.messages |
vtex.catalog-api-proxy:catalog-proxy |
Catalog reads through the official proxy |
vtex.catalog-api-proxy:authenticated-catalog-proxy |
Authenticated catalog reads |
(transitively via deps) vtex.intelligent-search-api, vtex.rewriter, vtex.sae-analytics |
IS, URL resolution, analytics |
.github/workflows/pull-request.yml runs three jobs on PR:
io-app-test—vtex/action-io-app-testruns builder-aware Jest in each builder directory.lint—vtex/action-lintruns the linter (rootyarn lint→cd node && yarn lint→tsc --noEmit).Cypress—vtex/action-io-app-cypress@v1runs thevtex/search-testssuite against thebiggyaccount, 4 containers, parallel. Requires repository secretsAPP_KEY,APP_TOKEN,RECORD_KEY.
make dev # yarn install (root + node) + vtex setup
make lint # cd node && yarn lint (tsc --noEmit — type-check)
make test # cd node && yarn test (jest 29 + ts-jest)
make coverage # cd node && yarn test --coverage (60% threshold)
make check # lint + test (matches root `yarn verify`)
make link # vtex link (uses active VTEX account/workspace)
make run # alias for make linkRoot yarn scripts: yarn lint, yarn format, yarn test, yarn verify (lint + test).
vtex-io-cli·vtex-io-app-structure·vtex-io-service-apps·vtex-io-graphql-apispecification+implementing(vtex-agent-skills) for the SDD Lite workflow- For SDD Full work, use the spec-kit pipeline against the parent Specs repo (
is-io-specs/.specify/)
- GitHub MCP — cross-repo references (especially
vtex.search-graphqlfor schema changes,vtex.intelligent-search-apifor backend,vtex/search-testsfor Cypress), issues, PRs. - Atlassian MCP — Jira/Confluence context.
Part of the is-io-specs multi-repo workspace. SpecKit artifacts (.specify/memory/constitution.md, docs/scope_of_work/, specs/<feature>/) live at the parent aggregator. This repo intentionally has no local .specify/. See the Multi-repo spec-kit extension.
- Never run
vtex link,vtex publish,vtex deploy, workspace promotion, or any account/workspace-changing Toolbelt command without explicit confirmation. - Never modify
manifest.jsonversiondirectly — usevtex release <patch|minor|major> stable. - Never modify
node/typings/— regenerated byvtex setup --typings --ignore-linked. - Ask before adding/removing
buildersorpoliciesinmanifest.json.
- The schema is owned by
vtex.search-graphql, not this repo. Adding a new field requires a coordinated PR there first. Resolver additions here will fail at runtime if the field is not in the schema. - Do not change field shapes of
Product,SKU,Brand,Category,Facet,Offer,AssemblyOptionwithout coordinated PRs invtex.search-result(PLP) and any private storefront consumers. - Schema directives (
@cacheControl,@withSegment) are part of the contract. Removing or changing semantics affects caching and segment isolation.
- Cache headers are emitted via the
@cacheControldirective on schema fields, not from resolver code. If you need a non-standard TTL, change the schema invtex.search-graphql, not the resolver. - Segment isolation:
@withSegmentensures the resolver sees the shopper's segment context. Do not bypass it for fields that vary per-shopper. @gocommerce/utilsbranches behavior for go-commerce stores. Always go through the existing helpers; do not gate by account name.
shouldUseNewPDPEndpoint/shouldUseNewPLPEndpointare per-account flags. Adding a new flag requires updatingmanifest.json:settingsSchemaand the corresponding read sites.- Never call
intelligentSearchApiand the legacysearchclient for the same field — pick one based on settings, or you'll double-bill and double-cache.
- Log via
ctx.vtex.loggerwith structured payloads.ctx.vtex.logger.info({ message, ... }), not bare strings. - Coverage is collected by Jest (
coverageProvider: 'v8') intonode/coverage/. The directory is gitignored; SonarQube ingestion is wired via DK CI.
- The Cypress job runs against the
biggyaccount. Never point it at a real customer account without explicit coordination — Cypress runs may issue real cart/order operations. - Repository secrets
APP_KEY,APP_TOKEN,RECORD_KEYmust be set in GitHub before the Cypress job will work in a forked PR.