Skip to content

Fixes #15353 fix(framework, medusa, settings, types): prevent sorting orders by computed fields that have no DB column#15354

Open
Ultron03 wants to merge 4 commits into
medusajs:developfrom
Ultron03:fix/order-non-sortable-computed-fields
Open

Fixes #15353 fix(framework, medusa, settings, types): prevent sorting orders by computed fields that have no DB column#15354
Ultron03 wants to merge 4 commits into
medusajs:developfrom
Ultron03:fix/order-non-sortable-computed-fields

Conversation

@Ultron03
Copy link
Copy Markdown

@Ultron03 Ultron03 commented May 10, 2026

Fixes #15353 fix(framework, medusa, settings, types): prevent sorting orders by computed fields that have no DB column

Summary

What — What changes are introduced in this PR?

Adds a nonSortableFields mechanism that prevents sorting list endpoints by fields that have no backing database column. Applied immediately to GET /admin/orders, blocking total, fulfillment_status, and payment_status as sort keys. Requests using these fields now receive a clear 400 INVALID_DATA response. The admin column API (generateEntityColumns) is also updated to return sortable: false for those columns so the configurable orders table never renders sort affordances for them.

Why — Why are these changes relevant or necessary?

Sorting by these fields reaches MikroORM with a key that does not correspond to any column in the Order table, causing an uncaught server crash:
Error: Trying to order by not existing property Order.total
total, fulfillment_status, and payment_status are computed values derived from related modules at query time — they are not physical columns and cannot be used as sort targets at the database level. This fix mirrors the approach taken in #15262 for filtering, which applied the same pattern to mark these fields as non-filterable.

How — How have these changes been implemented?

  • @medusajs/types — Added nonSortableFields?: string[] to QueryConfig, a blocklist of fields that are invalid sort targets for a given list endpoint.
  • @medusajs/framework — prepareListQuery now checks nonSortableFields after stripping the descending prefix (-). Blocked fields short-circuit with a MedusaError(INVALID_DATA) before reaching the ORM.
  • @medusajs/medusa — listTransformQueryConfig for GET /admin/orders sets nonSortableFields: ["total", "fulfillment_status", "payment_status"].
  • @medusajs/settings — Added nonSortableFields to EntityOverride (interface, BUILTIN_ENTITY_OVERRIDES Order entry, registry merge logic, and getNonSortableFields helper). generateEntityColumns passes the list into
    processEntityType, which sets sortable: false for those columns.

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

Unit tests:

  • packages/core/framework/src/http/utils/tests/get-query-config.spec.ts — verifies that ASC and DESC sort on a nonSortableFields entry throws, and that fields not in the list still sort correctly.
  • packages/modules/settings/src/utils/tests/entity-overrides.spec.ts — verifies getNonSortableFields returns the correct fields for Order, empty for entities with no override, and that registry merging works
    correctly.

Integration tests:

  • packages/modules/settings/integration-tests/tests/entity-discovery.spec.ts — verifies total, payment_status, fulfillment_status columns have sortable: false while real DB columns (created_at, status,
    currency_code) remain sortable: true.
  • integration-tests/http/tests/order/admin/order-sorting.spec.ts — end-to-end HTTP tests confirming valid sort fields (created_at, updated_at, display_id) return 200, and all three non-sortable fields (both ASC and
    DESC) return 400 with the exact error message Order field is not sortable.

Manual repro:

Before fix — crashes with 500

curl "http://localhost:9000/admin/orders?order=total" -H "Authorization: Bearer "

After fix — returns 400

{ "message": "Order field total is not sortable", "type": "invalid_data" }


Checklist

Please ensure the following before requesting a review:

  • 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
  • I have verified the code works as intended locally
  • I have linked the related issue(s) if applicable

… sorting orders by computed fields that have no DB column
@Ultron03 Ultron03 requested a review from a team as a code owner May 10, 2026 18:02
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 10, 2026

🦋 Changeset detected

Latest commit: ed62371

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 78 packages
Name Type
@medusajs/framework Patch
@medusajs/medusa Patch
@medusajs/settings Patch
@medusajs/types Patch
@medusajs/test-utils Patch
@medusajs/analytics Patch
@medusajs/api-key Patch
@medusajs/auth Patch
@medusajs/cache-inmemory Patch
@medusajs/cache-redis Patch
@medusajs/caching Patch
@medusajs/cart Patch
@medusajs/currency Patch
@medusajs/customer Patch
@medusajs/event-bus-local Patch
@medusajs/event-bus-redis Patch
@medusajs/file Patch
@medusajs/fulfillment Patch
@medusajs/index Patch
@medusajs/inventory Patch
@medusajs/link-modules Patch
@medusajs/locking Patch
@medusajs/notification Patch
@medusajs/order Patch
@medusajs/payment Patch
@medusajs/pricing Patch
@medusajs/product Patch
@medusajs/promotion Patch
@medusajs/rbac Patch
@medusajs/region Patch
@medusajs/sales-channel Patch
@medusajs/stock-location Patch
@medusajs/store Patch
@medusajs/tax Patch
@medusajs/translation Patch
@medusajs/user Patch
@medusajs/workflow-engine-inmemory Patch
@medusajs/workflow-engine-redis Patch
@medusajs/analytics-local Patch
@medusajs/analytics-posthog Patch
@medusajs/auth-emailpass Patch
@medusajs/auth-github Patch
@medusajs/auth-google Patch
@medusajs/caching-redis Patch
@medusajs/file-local Patch
@medusajs/file-s3 Patch
@medusajs/fulfillment-manual Patch
@medusajs/locking-postgres Patch
@medusajs/locking-redis Patch
@medusajs/notification-local Patch
@medusajs/notification-sendgrid Patch
@medusajs/payment-stripe Patch
@medusajs/draft-order Patch
@medusajs/loyalty-plugin Patch
@medusajs/core-flows Patch
integration-tests-http Patch
@medusajs/medusa-oas-cli Patch
@medusajs/js-sdk Patch
@medusajs/modules-sdk Patch
@medusajs/orchestration Patch
@medusajs/utils Patch
@medusajs/workflows-sdk Patch
@medusajs/admin-bundler Patch
@medusajs/dashboard Patch
@medusajs/oas-github-ci Patch
@medusajs/http-types-generator Patch
@medusajs/cli Patch
@medusajs/deps Patch
@medusajs/telemetry Patch
@medusajs/admin-sdk Patch
@medusajs/admin-shared Patch
@medusajs/admin-vite-plugin Patch
@medusajs/icons Patch
@medusajs/toolbox Patch
@medusajs/ui-preset Patch
create-medusa-app Patch
medusa-dev-cli Patch
@medusajs/ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented May 10, 2026

@Ultron03 is attempting to deploy a commit to the medusajs Team on Vercel.

A member of the Team first needs to authorize it.

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03! 🎉

After an initial review, this PR looks good to us. Here's a summary:

✅ PR template is complete
✅ Linked to a verified issue (#15353)
✅ Follows contribution guidelines
✅ Tests included (unit, settings integration, and HTTP integration)
✅ Follows Medusa's conventions
✅ Changeset included with correct format and bump type

The approach mirrors the nonFilterableFields pattern from #15262 cleanly, and test coverage is thorough — unit tests for the validation logic, override registry behaviour, and end-to-end HTTP tests confirming both the 400 path and that valid sort fields still return 200.

Notes (non-blocking, no action required):

  • Error message is entity-specific in a generic functionpackages/core/framework/src/http/utils/get-query-config.ts throws Order field ${orderField} is not sortable, but nonSortableFields is added to the generic QueryConfig type and prepareListQuery is shared across all list endpoints. If another entity adopts nonSortableFields in the future, the message will say "Order field" incorrectly. This follows the same pre-existing quirk as the adjacent Order field ${orderField} is not valid check, so it's consistent with current patterns — worth a follow-up cleanup when convenient.

  • Weak deduplication test — the assertion in packages/modules/settings/src/utils/__tests__/entity-overrides.spec.ts uses expect(totalOccurrences).toBeGreaterThanOrEqual(1), which passes even if the field appears 100 times. Duplicates don't cause any runtime issues here (since includes() still works), but the assertion doesn't actually verify the intended behaviour.

A team member will do a final review before this is merged. We appreciate your patience!

Triggered by: PR description updated

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03! 🎉

After an initial review, this PR looks good to us. Here's a summary:

✅ PR template is complete
✅ Linked to a verified issue (#15353)
✅ Follows contribution guidelines
✅ Tests included (unit tests, integration tests, and end-to-end HTTP tests)
✅ Changeset included with correct fix(...) format
✅ Follows Medusa's conventions

The implementation cleanly mirrors the nonFilterableFields pattern introduced in #15262 — the approach is consistent and well-scoped. The error message (Order field <field> is not sortable) is clear, and placing the nonSortableFields check after the allowedFields guard in prepareListQuery is the right ordering. The admin UI column sortable: false path is also correctly handled via generateEntityColumns.

A team member will do a final review before this is merged. We appreciate your patience!

Triggered by: PR description updated

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03! 🎉

After an initial review, this PR looks good to us. Here's a summary:

✅ PR template is complete
✅ Linked to a verified issue (#15353)
✅ Follows contribution guidelines
✅ Tests included (unit + integration at both the HTTP and settings module layers)
✅ Changeset present with correct patch bump and fix(...): ... format
✅ Follows Medusa's conventions (mirrors the pattern from #15262 for nonFilterableFields)

The implementation is solid: the API-layer guard in prepareListQuery correctly strips the - prefix before the blocklist check (covering both ASC and DESC), and the admin UI layer correctly marks those columns as sortable: false via processEntityType. The registry merge for nonSortableFields is consistent with how nonFilterableFields is handled.

Notes (non-blocking):

  • The error message Order field ${orderField} is not sortable in get-query-config.ts hardcodes "Order" inside a generic utility — but this mirrors the pre-existing Order field ${orderField} is not valid message in the same function, so the PR is consistent. Worth a future cleanup to make it entity-agnostic (e.g. Field ${orderField} cannot be used for sorting), but not blocking.

A team member will do a final review before this is merged. We appreciate your patience!

Triggered by: new PR opened

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03! 🎉

After an initial review, this PR looks good to us. Here's a summary:

✅ PR template is complete
✅ Linked to a verified issue (#15353)
✅ Follows contribution guidelines
✅ Tests included (unit + integration + HTTP end-to-end)
✅ Follows Medusa's conventions
✅ Changeset present with correct fix(...) format

The implementation cleanly mirrors the nonFilterableFields approach from #15262 — adding nonSortableFields to QueryConfig<TEntity>, validating in prepareListQuery before the ORM is reached, and propagating sortable: false into the column generator so the admin UI never offers sort affordances for computed fields.

Notes (non-blocking):

The error message in get-query-config.ts uses the hardcoded prefix "Order field <field> is not sortable". This is consistent with the pre-existing "Order field <field> is not valid" check already in the same function, so it is not a new issue — but if nonSortableFields is ever applied to a non-Order endpoint, the message could be confusing. A more generic phrasing like "Field <field> is not sortable" would make this mechanism reusable without ambiguity. No change required for this PR.

A team member will do a final review before this is merged. We appreciate your patience!

Triggered by: PR description updated

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03!

After reviewing this PR, we need one thing addressed before we can move forward:

Required changes:

Explanation:

The most recent commit on develop is #15262 (fix(settings): mark Order computed status fields as non-filterable), which added a nonFilterableFields: string[] parameter to processEntityType and updated its call sites in generateEntityColumns. The column-generator.ts changes in this PR were written before that landed, so the diff context no longer matches the current file. Merging as-is would produce a conflict and — if resolved incorrectly — could silently drop the non-filterable fields behaviour.

After rebasing, the function signature should look like:

function processEntityType(
  schemaTypeMap: SchemaTypeMap,
  typeName: string,
  entity: DiscoveredEntity,
  columns: ViewConfigurationColumn[],
  processedFields: Set<string>,
  filterRules: FieldFilterRules,
  defaultVisibleFields: string[],
  fieldOrdering: Record<string, number>,
  nonFilterableFields: string[],   // from #15262 — must be kept
  propertyLabels?: Map<string, PropertyLabel>,
  parentPath: string = "",
  nonSortableFields: string[] = [] // your addition
): void {

and both call sites in generateEntityColumns should pass nonFilterableFields before propertyLabels.

Everything else in the PR looks solid: the approach is correct, the changeset is properly formatted, the PR template is complete, and the unit + integration tests are thorough. Once the rebase is done this should be in good shape.


Triggered by: PR description updated

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03!

After reviewing the updated commit, the rebase issue flagged in the previous review is still present.

Required changes:

  • Properly rebase onto the current develop HEAD before requesting re-review.

Explanation:

The diff still includes re-adding code that #15262 already merged into develop:

  • packages/modules/settings/src/utils/entity-overrides.ts — the diff shows adding nonFilterableFields? to the EntityOverride interface, adding nonFilterableFields to BUILTIN_ENTITY_OVERRIDES.Order, adding the nonFilterableFields merge in EntityOverrideRegistry.register(), and adding the getNonFilterableFields helper. All four are already present in develop.
  • packages/modules/settings/src/utils/column-generator.ts — the diff re-adds the getNonFilterableFields import and re-adds the filter ternary that uses nonFilterableFields.includes(fullPath). Both are already in develop.

Merging as-is will either produce conflicts or, if auto-merged, duplicate these symbols (duplicate interface properties are a TypeScript compile error; duplicate registry merges and imports are dead code).

After a proper git rebase origin/develop, the diff for entity-overrides.ts should only show the nonSortableFields additions (interface field, BUILTIN_ENTITY_OVERRIDES.Order entry, registry merge, and getNonSortableFields helper). The diff for column-generator.ts should only show adding the getNonSortableFields import, computing and passing nonSortableFields, and changing sortable: \!parentPath to sortable: \!parentPath && \!nonSortableFields.includes(fullPath) — it should not touch the filter logic or the getNonFilterableFields import.

One positive update: the error message was correctly changed from Order field ${orderField} is not sortable to Field ${orderField} is not sortable, making the utility generic. The tests are updated to match and that part looks good.

Everything else — the approach, the QueryConfig type addition, the prepareListQuery guard, the listTransformQueryConfig entry, and the test coverage — is solid. A clean rebase is all that's needed.

Triggered by: new commit pushed

@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot Bot commented May 10, 2026

Thank you for your contribution, @Ultron03!

Good progress since the last review — the entity-overrides.ts diff is now clean (no longer re-adding nonFilterableFields items), and column-generator.ts no longer re-adds the getNonFilterableFields import or the filter ternary. The error message fix, test coverage, and changeset are all solid.

There is one remaining issue to fix before this is ready:

Required changes:

  • Rebase onto the current develop and clean up the second processEntityType call in packages/modules/settings/src/utils/column-generator.ts

Explanation:

The current develop already has nonFilterableFields as parameter 9 of processEntityType (and at both call sites in generateEntityColumns) following the merge of #15262. The PR branch predates that merge, so:

  1. The first call diff context shows fieldOrdering, immediately above propertyLabels — but in current develop there is nonFilterableFields, between them. The diff context won't cleanly apply.

  2. The second call diff adds nonFilterableFields, as if it's new:

-        propertyLabels
+        nonFilterableFields,
+        propertyLabels,
+        "",
+        nonSortableFields

But nonFilterableFields is already there in develop. After a proper rebase, this diff should look identical to the first call — only adding the parentPath and nonSortableFields arguments:

-        propertyLabels
+        propertyLabels,
+        "",
+        nonSortableFields

After rebasing on the current develop, the entire column-generator.ts diff should only show: the getNonSortableFields import, the nonSortableFields variable, "", nonSortableFields appended to both call sites (identically), the new parameter on the function signature, and the sortable line change. Nothing else.


Minor note (non-blocking): The changeset description still says Order field total is not sortable but the code now correctly uses Field ${orderField} is not sortable. Worth updating the changeset text to match.

Once the rebase is done, this should be ready.

Triggered by: new commit pushed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Error trying to sort orders by: Total / Fulfillment status / Payment status

1 participant