fix(dashboard): avoid unnecessary product relations to be returned by default (#14175)

## Summary

**What** — What changes are introduced in this PR?

Improve Admin UI product detail performance after updating the entity.

**Why** — Why are these changes relevant or necessary?  

Products with a lot of relations would cause issues when trying to be updated through various edit components, since the core update api route returns these entities by default.

**How** — How have these changes been implemented?

We remove these unnecessary relations when calling the update route, by passing the `fields` query param with the negation sign for each of these. For example: `fields=-type,-collection...`

**Testing** — How have these changes been tested, or how can the reviewer test the feature?

Tested all of the update components and validated they still work correctly, plus, none depend on the returned product to perform an action or render information.

---

## Examples

Provide examples or code snippets that demonstrate how this feature works, or how it can be used in practice.  
This helps with documentation and ensures maintainers can quickly understand and verify the change.

```ts
// Example usage
```

---

## Checklist

Please ensure the following before requesting a review:

- [x] I have added a **changeset** for this PR
    - Every non-breaking change should be marked as a **patch**
    - To add a changeset, run `yarn changeset` and follow the prompts
- [ ] The changes are covered by relevant **tests**
- [x] I have verified the code works as intended locally
- [x] I have linked the related issue(s) if applicable

---

## Additional Context

Add any additional context, related issues, or references that might help the reviewer understand this PR.

fixes #13783, #14183
closes CORE-1296, SUP-2791


Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
Nicolas Gorga
2025-12-07 16:03:25 -03:00
committed by GitHub
parent 842c0f5007
commit 9e4d2df72f
23 changed files with 90 additions and 36 deletions

View File

@@ -117,7 +117,9 @@ const useDynamicSearchResults = (
{
q: debouncedSearch,
limit,
fields: "id,title,thumbnail",
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"id,title,thumbnail,-type,-collection,-options,-tags,-images,-variants,-sales_channels",
},
{
enabled: isAreaEnabled(currentArea, "product"),

View File

@@ -340,7 +340,11 @@ export const useUpdateProduct = (
>
) => {
return useMutation({
mutationFn: (payload) => sdk.admin.product.update(id, payload),
mutationFn: (payload) =>
sdk.admin.product.update(id, payload, {
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
}),
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({
queryKey: productsQueryKeys.lists(),

View File

@@ -7,7 +7,8 @@ type UseProductTableQueryProps = {
}
const DEFAULT_FIELDS =
"id,title,handle,status,*collection,*sales_channels,variants.id,thumbnail"
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
"id,title,handle,status,*collection,*sales_channels,variants.id,thumbnail,-type,-options,-tags,-images,-variants"
export const useProductTableQuery = ({
prefix,

View File

@@ -35,7 +35,9 @@ export const PriceListPricesForm = ({
const { products, isLoading, isError, error } = useProducts({
id: ids.map((id) => id.id),
limit: ids.length,
fields: "title,thumbnail,*variants",
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"title,thumbnail,*variants,-type,-collection,-options,-tags,-images,-sales_channels",
})
const { setCloseOnEscape } = useRouteModal()

View File

@@ -36,7 +36,9 @@ export const PriceListPricesAddPricesForm = ({
const { products, isLoading, isError, error } = useProducts({
id: ids.map((id) => id.id),
limit: ids.length,
fields: "title,thumbnail,*variants",
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"title,thumbnail,*variants,-type,-collection,-options,-tags,-images,-sales_channels",
})
const { setValue } = form

View File

@@ -22,7 +22,9 @@ export const PriceListPricesEdit = () => {
id: productIds,
limit: productIds?.length || 9999, // Temporary until we support lazy loading in the DataGrid
price_list_id: [id!],
fields: "title,thumbnail,*variants",
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"title,thumbnail,*variants,-type,-collection,-options,-tags,-images,-sales_channels",
})
const { isReady, regions, currencies, pricePreferences } =

View File

@@ -33,7 +33,8 @@ export const ProductVariantEdit = () => {
} = useProduct(
variant?.product_id!,
{
fields: "-variants",
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-tags,-images,-variants,-sales_channels",
},
{
enabled: !!variant?.product_id,

View File

@@ -4,7 +4,6 @@ import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals"
import { useProduct } from "../../../hooks/api/products"
import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants"
import { ProductAttributesForm } from "./components/product-attributes-form"
export const ProductAttributes = () => {
@@ -12,7 +11,9 @@ export const ProductAttributes = () => {
const { t } = useTranslation()
const { product, isLoading, isError, error } = useProduct(id!, {
fields: PRODUCT_DETAIL_FIELDS,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
})
if (isError) {

View File

@@ -9,7 +9,11 @@ export const ProductCreateOption = () => {
const { id } = useParams()
const { t } = useTranslation()
const { product, isLoading, isError, error } = useProduct(id!)
const { product, isLoading, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
})
if (isError) {
throw error

View File

@@ -6,7 +6,10 @@ import { CreateProductVariantForm } from "./components/create-product-variant-fo
export const ProductCreateVariant = () => {
const { id } = useParams()
const { product, isLoading, isError, error } = useProduct(id!)
const { product, isLoading, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-tags,-images,-variants,-sales_channels",
})
if (isError) {
throw error

View File

@@ -1,7 +1,6 @@
import { HttpTypes } from "@medusajs/types"
import { UIMatch } from "react-router-dom"
import { useProduct } from "../../../hooks/api"
import { PRODUCT_DETAIL_FIELDS } from "./constants"
type ProductDetailBreadcrumbProps = UIMatch<HttpTypes.AdminProductResponse>
@@ -13,7 +12,9 @@ export const ProductDetailBreadcrumb = (
const { product } = useProduct(
id!,
{
fields: PRODUCT_DETAIL_FIELDS,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
},
{
initialData: props.data,

View File

@@ -9,7 +9,10 @@ export const ProductEditOption = () => {
const { id, option_id } = useParams()
const { t } = useTranslation()
const { product, isPending, isFetching, isError, error } = useProduct(id!)
const { product, isPending, isFetching, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-tags,-images,-variants,-sales_channels",
})
const option = product?.options.find((o) => o.id === option_id)

View File

@@ -4,7 +4,6 @@ import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals"
import { useProduct } from "../../../hooks/api/products"
import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants"
import { EditProductForm } from "./components/edit-product-form"
export const ProductEdit = () => {
@@ -12,7 +11,9 @@ export const ProductEdit = () => {
const { t } = useTranslation()
const { product, isLoading, isError, error } = useProduct(id!, {
fields: PRODUCT_DETAIL_FIELDS,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
})
if (isError) {

View File

@@ -21,7 +21,11 @@ export const ProductImageVariantsEdit = () => {
const { product, isPending } = useProduct(
product_id!,
{ fields: "images.id,images.url,images.variants.id" },
{
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"images.id,images.url,images.variants.id,-type,-collection,-options,-tags,-variants,-sales_channels",
},
{
enabled: !!product_id && !!image_id,
}

View File

@@ -8,7 +8,10 @@ export const ProductMedia = () => {
const { t } = useTranslation()
const { id } = useParams()
const { product, isLoading, isError, error } = useProduct(id!)
const { product, isLoading, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-options,-tags,-variants,-sales_channels",
})
const ready = !isLoading && product

View File

@@ -5,7 +5,11 @@ import { useProduct, useUpdateProduct } from "../../../hooks/api"
export const ProductMetadata = () => {
const { id } = useParams()
const { product, isPending, isError, error } = useProduct(id!)
const { product, isPending, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
})
const { mutateAsync, isPending: isMutating } = useUpdateProduct(product?.id!)

View File

@@ -4,7 +4,6 @@ import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals"
import { useProduct } from "../../../hooks/api/products"
import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants"
import { ProductOrganizationForm } from "./components/product-organization-form"
export const ProductOrganization = () => {
@@ -12,7 +11,8 @@ export const ProductOrganization = () => {
const { t } = useTranslation()
const { product, isLoading, isError, error } = useProduct(id!, {
fields: PRODUCT_DETAIL_FIELDS,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "*categories,-options,-images,-variants,-sales_channels",
})
if (isError) {

View File

@@ -7,7 +7,10 @@ import { PricingEdit } from "./pricing-edit"
export const ProductPrices = () => {
const { id, variant_id } = useParams()
const { product, isLoading, isError, error } = useProduct(id!)
const { product, isLoading, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-options,-tags,-images,-sales_channels",
})
if (isError) {
throw error

View File

@@ -6,7 +6,10 @@ import { EditSalesChannelsForm } from "./components/edit-sales-channels-form"
export const ProductSalesChannels = () => {
const { id } = useParams()
const { product, isLoading, isError, error } = useProduct(id!)
const { product, isLoading, isError, error } = useProduct(id!, {
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields: "-type,-collection,-options,-tags,-images,-variants",
})
if (isError) {
throw error

View File

@@ -4,7 +4,6 @@ import { useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/modals"
import { useProduct } from "../../../hooks/api/products"
import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants"
import { ProductShippingProfileForm } from "./components/product-organization-form"
export const ProductShippingProfile = () => {
@@ -12,7 +11,9 @@ export const ProductShippingProfile = () => {
const { t } = useTranslation()
const { product, isLoading, isError, error } = useProduct(id!, {
fields: PRODUCT_DETAIL_FIELDS,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"*shipping_profile,-type,-collection,-options,-tags,-images,-variants,-sales_channels",
})
if (isError) {

View File

@@ -17,7 +17,11 @@ export const TargetItem = ({
}: TargetItemProps) => {
const { product } = useProduct(
value,
{ fields: "id,title" },
{
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"id,title,-type,-collection,-options,-tags,-images,-variants,-sales_channels",
},
{ enabled: !label }
)

View File

@@ -39,18 +39,15 @@ export const TaxOverrideCard = ({ taxRate }: TaxOverrideCardProps) => {
return null
}
const groupedRules = taxRate.rules.reduce(
(acc, rule) => {
if (!acc[rule.reference]) {
acc[rule.reference] = []
}
const groupedRules = taxRate.rules.reduce((acc, rule) => {
if (!acc[rule.reference]) {
acc[rule.reference] = []
}
acc[rule.reference].push(rule.reference_id)
acc[rule.reference].push(rule.reference_id)
return acc
},
{} as Record<string, string[]>
)
return acc
}, {} as Record<string, string[]>)
const validKeys = Object.values(TaxRateRuleReferenceType)
const numberOfTargets = Object.keys(groupedRules).map((key) =>
@@ -282,6 +279,9 @@ const useReferenceValues = (
{
id: ids.slice(0, DISPLAY_OVERRIDE_ITEMS_LIMIT),
limit: DISPLAY_OVERRIDE_ITEMS_LIMIT,
// TODO: Remove exclusion once we avoid including unnecessary relations by default in the query config
fields:
"-type,-collection,-options,-tags,-images,-variants,-sales_channels",
},
{
enabled: !!ids.length && type === TaxRateRuleReferenceType.PRODUCT,