Skip to content

feat(medusa): add inventory_quantity support for cart items variants in store API#13945

Open
bqst wants to merge 6 commits intomedusajs:developfrom
bqst:feat/add-inventory-quantity-to-store-cart
Open

feat(medusa): add inventory_quantity support for cart items variants in store API#13945
bqst wants to merge 6 commits intomedusajs:developfrom
bqst:feat/add-inventory-quantity-to-store-cart

Conversation

@bqst
Copy link
Copy Markdown
Contributor

@bqst bqst commented Nov 3, 2025

Summary

What — What changes are introduced in this PR?

This PR adds support for inventory_quantity on cart item variants in the /store/carts/:id endpoint by applying the existing variants.inventory_quantity middleware from the products endpoint to the cart routes.

Why — Why are these changes relevant or necessary?

  • Feature parity: This functionality was available in Medusa v1 and is documented in v2 but not implemented (issue #12884)
  • Performance optimization: Eliminates the need for separate API calls to fetch inventory data, reducing the number of requests from N+1 (cart + inventory per product) to 1
  • Better UX: Enables real-time inventory validation in cart/checkout flows without additional round trips
  • Consistency: Aligns cart endpoint behavior with the existing /store/products endpoint

How — How have these changes been implemented?

  1. Reused the existing variants.inventory_quantity decorator that:
  • Calculates available inventory across sales channels
  • Aggregates stock from multiple locations
  • Only returns values for variants with manage_inventory: true
  1. No new code needed - leveraged existing infrastructure from products endpoint

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

Added comprehensive integration tests in cart.spec.ts (GET /store/carts/[id] > with inventory items section):

  • ✅ Returns inventory_quantity for managed inventory variants
  • ✅ Returns undefined for unmanaged inventory variants
  • ✅ Correctly calculates with multiple inventory items per variant
  • ✅ Aggregates inventory across multiple stock locations
  • ✅ Respects sales channel associations

Tests mirror the existing product endpoint tests for consistency.


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.

// Fetch cart with inventory quantities included
const response = await fetch(
  '/store/carts/cart_123?fields=*items.variant,+items.variant.inventory_quantity',
  {
    headers: {
      'x-publishable-api-key': 'pk_...'
    }
  }
)

const cart = await response.json()

// Access inventory quantity for each item
cart.items.forEach(item => {
  if (item.variant.manage_inventory) {
    console.log(`Product: ${item.title}`)
    console.log(`Available stock: ${item.variant.inventory_quantity}`)
    console.log(`Cart quantity: ${item.quantity}`)
    
    // Validate stock availability
    if (item.quantity > item.variant.inventory_quantity) {
      console.warn('Insufficient stock!')
    }
  }
})

Before this PR:

// Required multiple API calls
const cart = await fetch('/store/carts/cart_123')
// Then fetch each product's inventory separately
const product1 = await fetch('/store/products&id=prod_1?fields=variants.inventory_quantity')

After this PR:

// Single API call with all data
const cart = await fetch('/store/carts/cart_123?fields=*items.variant,*items.variant.inventory_quantity')
// Inventory data directly available on cart items

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

Additional Context

  • Related Issue: Closes [Bug]: Cannot retrieve inventory_quantity for cart line item variants via fields param #12884
  • Documentation: This feature is already documented in the v2 Store API docs - this PR implements the documented behavior
  • v1 Compatibility: Maintains feature parity with Medusa v1 where inventory_quantity was available on cart items
  • Breaking Changes: None - this is a backward-compatible addition that requires explicit field selection via query parameters
  • Implementation Note: Reuses existing, battle-tested middleware from /store/products endpoint, ensuring consistent behavior across the API
CleanShot 2025-11-03 at 16 10 42

Note

Adds sales-channel-aware inventory_quantity to cart item variants when requested via fields, refactors cart routes to pass request context, and adds integration tests.

  • Store API – Carts:
    • Compute and expose items.variant.inventory_quantity (when selected via fields) by enhancing refetchCart to strip the computed field from queries and wrap fetched variants with availability.
    • Update routes (/store/carts, /store/carts/:id, line-items add/update/delete, promotions add/remove, shipping-methods, taxes, complete) to use MedusaStoreRequest/AuthenticatedMedusaRequest and pass req to refetchCart.
  • Inventory/Sales Channel Middleware:
    • Extend wrapVariantsWithInventoryQuantityForSalesChannel and sales-channel validation to accept both authenticated/store requests, handle missing contexts safely, and require a single sales channel for availability.
  • Tests:
    • Add integration tests verifying inventory_quantity for managed variants, absence for unmanaged, multi-item and multi-location aggregation, and sales-channel behavior.
  • Changeset:
    • Patch releases for @medusajs/medusa and integration-tests-http.

Written by Cursor Bugbot for commit 3a5563c. This will update automatically on new commits. Configure here.

@bqst bqst requested a review from a team as a code owner November 3, 2025 15:12
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Nov 3, 2025

🦋 Changeset detected

Latest commit: 3a5563c

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

This PR includes changesets to release 74 packages
Name Type
integration-tests-http Patch
@medusajs/medusa Patch
@medusajs/test-utils Patch
@medusajs/medusa-oas-cli Patch
@medusajs/analytics Patch
@medusajs/api-key Patch
@medusajs/auth Patch
@medusajs/caching Patch
@medusajs/cart Patch
@medusajs/currency Patch
@medusajs/customer 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/region Patch
@medusajs/sales-channel Patch
@medusajs/settings Patch
@medusajs/stock-location Patch
@medusajs/store Patch
@medusajs/tax Patch
@medusajs/user Patch
@medusajs/workflow-engine-inmemory Patch
@medusajs/workflow-engine-redis Patch
@medusajs/draft-order Patch
@medusajs/oas-github-ci Patch
@medusajs/cache-inmemory Patch
@medusajs/cache-redis Patch
@medusajs/event-bus-local Patch
@medusajs/event-bus-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/core-flows Patch
@medusajs/framework Patch
@medusajs/js-sdk Patch
@medusajs/modules-sdk Patch
@medusajs/orchestration Patch
@medusajs/types Patch
@medusajs/utils Patch
@medusajs/workflows-sdk Patch
@medusajs/cli Patch
@medusajs/deps Patch
@medusajs/telemetry Patch
@medusajs/admin-bundler Patch
@medusajs/admin-sdk Patch
@medusajs/admin-shared Patch
@medusajs/admin-vite-plugin Patch
@medusajs/dashboard 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 Nov 3, 2025

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

A member of the Team first needs to authorize it.

cursor[bot]

This comment was marked as outdated.

@bqst bqst changed the title ✨ Add inventory_quantity support for cart items variants in store API feat(medusa): Add inventory_quantity support for cart items variants in store API Nov 3, 2025
@bqst bqst changed the title feat(medusa): Add inventory_quantity support for cart items variants in store API feat(medusa): add inventory_quantity support for cart items variants in store API Nov 3, 2025
Copy link
Copy Markdown
Contributor

@olivermrbl olivermrbl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the contribution! wonder if we should support this across all cart routes for the sake of consistency?

Comment on lines +55 to +57
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant")) &&
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we don't need both checks here

Suggested change
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant")) &&
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the double check, but this causes an issue. When using fields=+items.variant.inventory_quantity, the helper filters out items.variant.inventory_quantity but doesn't ensure items.variant is fetched, resulting in cart.items[x].variant being undefined and causing this error:

TypeError: Cannot read properties of undefined (reading 'id')
at wrapVariantsWithInventoryQuantityForSalesChannel

Should we ensure that fields=+items.variant.inventory_quantity automatically adds items.variant ?

Comment on lines +14 to +16
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant")) &&
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant")) &&
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))
const withInventoryQuantity =
req.queryConfig.fields.some((field) => field.includes("items.variant.inventory_quantity"))

if (withInventoryQuantity && cart.items?.length && req) {
await wrapVariantsWithInventoryQuantityForSalesChannel(
req,
cart.items.map((item) => item.variant)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Ensure items.variant Included When Fetching Inventory Quantity

The code filters out items.variant.inventory_quantity from the requested fields but doesn't ensure that items.variant itself is included in the query. The comment on line 21 states "and ensure items.variant is included if not already presents" but this isn't implemented. When inventory_quantity is requested and the code tries to map over cart.items.map((item) => item.variant) on line 46, it will fail with a runtime error if item.variant is undefined because the base variant object wasn't fetched. This can happen if a user requests custom fields that exclude items.variant but include items.variant.inventory_quantity, or if defaults don't include items.variant in some configurations.

The fix should ensure items.variant is added to fieldsToFetch when withInventoryQuantity is true, or add a check before accessing item.variant on line 46.

Fix in Cursor Fix in Web

@bqst
Copy link
Copy Markdown
Contributor Author

bqst commented Nov 6, 2025

I've implemented the feature consistently across all store cart routes as requested.

@willbouch
Copy link
Copy Markdown
Contributor

@olivermrbl this will be ready for a second round of review

@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Dec 18, 2025
@bqst
Copy link
Copy Markdown
Contributor Author

bqst commented Dec 18, 2025

unstale

@github-actions github-actions bot removed the Stale label Dec 19, 2025
@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Jan 19, 2026
@bqst
Copy link
Copy Markdown
Contributor Author

bqst commented Jan 21, 2026

unstale

@github-actions github-actions bot removed the Stale label Jan 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Feb 23, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 1, 2026

This PR was closed because it has been stalled for 5 days with no activity.

@github-actions github-actions bot closed this Mar 1, 2026
@NicolasGorga NicolasGorga reopened this Mar 1, 2026
@NicolasGorga NicolasGorga removed the Stale label Mar 1, 2026
@medusa-os-bot
Copy link
Copy Markdown

medusa-os-bot bot commented Apr 9, 2026

Thank you for your contribution! 🎉

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

✅ PR template is complete
✅ Linked to a verified issue (#12884)
✅ Follows contribution guidelines
✅ Tests included (303 lines of new integration tests)
✅ Changeset included
✅ Follows Medusa's conventions — reuses the existing wrapVariantsWithInventoryQuantityForSalesChannel pattern from the products endpoint

Notes:

  • Sales channel context on cart routes without publishable key middleware — Most cart routes (e.g. GET /store/carts/:id) don't apply maybeAttachPublishableKeyScopes, so publishable_key_context may be absent at runtime. The PR modifies wrapVariantsWithInventoryQuantityForSalesChannel to handle missing contexts, but it's worth clarifying — and ideally testing — whether a missing context silently skips inventory wrapping or throws a user-facing error. A silent skip would mean the field appears as undefined with no indication of why.

  • complete/route.ts error path — The error-handling branch at packages/medusa/src/api/store/carts/[id]/complete/route.ts calls refetchCart with a separately constructed cartReq object, not req. If the PR changes refetchCart to accept req for inventory wrapping, make sure this call site either passes the correct request or gracefully skips inventory computation.

Potential Bugs:

⚠️ packages/medusa/src/api/utils/middlewares/products/variant-inventory-quantity.ts — Unsafe property access in wrapVariants

variant.inventory_quantity = availability[variant.id].availability

If availability[variant.id] is undefined (e.g. a manage_inventory: true variant with no inventory items configured), this throws a TypeError at runtime. This is pre-existing in the products middleware, but this PR now exposes it to additional cart routes. Adding a guard like availability[variant.id]?.availability ?? 0 would make it safer.

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

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]: Cannot retrieve inventory_quantity for cart line item variants via fields param

4 participants