magenxcommerce / module-best-seller-graph-ql
GraphQL coverage for Magento best sellers: a bestSellers query (store-wide or per category) and an Amazon-style per-category best-seller badge on products. Reads the sales_bestsellers_aggregated_* report tables; leaves the core modules untouched.
Package info
github.com/magenxcommerce/module-best-seller-graph-ql
Type:magento2-module
pkg:composer/magenxcommerce/module-best-seller-graph-ql
Requires
- php: ~8.3.0||~8.4.0||~8.5.0
- magento/framework: *
- magento/module-backend: *
- magento/module-catalog: *
- magento/module-catalog-graph-ql: *
- magento/module-config: *
- magento/module-graph-ql: *
- magento/module-reports: *
- magento/module-sales: *
- magento/module-store: *
- magento/module-store-graph-ql: *
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Built with AI assistance. This module is built with significant use of Claude Code, under active human review and guidance. It is open source and in active development. AI assistance does not lower the engineering bar: every change must meet the same correctness, security, documentation and maintainability requirements as any other contribution.
No warranty. We are not responsible for any incompatibility. By installing this software, you accept responsibility for its security, upgrades, patches and monitoring.
GraphQL coverage for Magento best sellers. Stock Magento aggregates best-seller
data in the sales_bestsellers_aggregated_* report tables but exposes none
of it over GraphQL, and those tables have no per-category dimension. This
module adds that coverage without touching any core module (the same
"extend with GraphQL" split as Magento_CatalogGraphQl beside Magento_Catalog).
What it adds
bestSellers(category_id: Int, pageSize: Int): BestSellers— the top-N best-selling products, store-wide or scoped to a single category, ordered by quantity sold over the configured report period. EachBestSellerItemcarries arankand resolves a fullProductInterface(so the storefront can select any product fields it needs). Visibility is checked on ids alone and only the products that make the page are hydrated, so the whole page costs two product queries, not one per row.ProductInterface.best_seller: BestSellerBadge— an Amazon-style per-category badge (rank,category_id,category_name,category_url_key) computed as the product's best rank across the categories it belongs to, ornullwhen it doesn't rank within the configured threshold. Implemented as a batch resolver: an entire grid costs a fixed handful of queries, so it is safe to select on listing pages as well as the PDP.StoreConfigflags —best_seller_enabled,best_seller_count,best_seller_period,best_seller_badge_enabled,best_seller_badge_max_rank(viaextendedConfigData, the same pattern asproduct_alert_allow_price).
Ranking
Reads sales_bestsellers_aggregated_<period> filtered to the request's store id,
summing qty_ordered per product. Per-category ranking joins
catalog_category_product; a product's badge rank in a category is
1 + (number of products in that category that sold more), so tied products
share a rank. The aggregation cron (Reports → refresh statistics, or
bin/magento report aggregation) must have run for the tables to be populated.
Two things bound what the ranking can contain:
- The period is a bucket, not a lifetime total. Each aggregated table holds one row per (period, store, product), and queries are constrained to the current bucket — sales so far today, this month, or this year, in the store's timezone. Early in a bucket the ranking therefore covers a partial period; on Daily, shortly after midnight, it may be empty.
- Only publicly visible products rank. Products that are disabled, set to "Not Visible Individually", or not assigned to the request's website are dropped, and ranks are numbered afterwards so they stay contiguous. Category ids that do not belong to the store's own tree are rejected, and badges never rank a product against another store view's categories or name a category that is disabled in the store view.
- Quantities stay private. The query exposes each product's
rank, never the units sold —bestSellersis unauthenticated, and raw sales volume is business data.
Configuration
Admin → Stores → Configuration → Magenx → Best Sellers:
- General: Enable Best Sellers, Number of Products (default 20), Report Period (Daily / Monthly / Yearly, default Monthly).
- Product Badge: Enable Per-Category Badge, Maximum Badged Rank (default 10).
When the feature is disabled the bestSellers query returns an empty list and the
best_seller field resolves to null, so the feature is fully inert.
Install
bin/magento module:enable Magenx_BestSellerGraphQl
bin/magento setup:upgrade
bin/magento setup:di:compile # production mode
Tests
Unit tests live in Test/Unit and run from a Magento installation that has the
module in place — they mock Magento interfaces, so they need magento/framework
on the autoloader and cannot run against a bare checkout of this repo:
bin/magento dev:tests:run unit
# or, for this module only:
vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist vendor/magenxcommerce/module-best-seller-graph-ql/Test/Unit