v2.6.0

medusajs/medusav2.6.0Mar 4, 2025by olivermrbl

AI Summary

This release enhances security with updated token handling for password resets and refactors the API Routes loader for better middleware and route management. It also includes significant performance improvements for cart operations.

Key Highlights

  • Updated token handling for password resets (Bearer token instead of query params) - Breaking change.
  • Refactored API Routes loader for predictable middleware and route registration.
  • Significant performance improvements for cart operations (30-35% faster).
  • Added credit lines to cart workflows.
  • Configuration recommendations for database pooling.

Breaking Changes

  • Token for updating provider identity now requires a Bearer token in the authorization header instead of query parameters.

New Features

  • Add middleware and routes sorter
  • Add routes loader
  • Add middleware-file-loader
  • Replace existing router with the new implementation

Full Release Notes

<!-- 
![image](https://github.com/user-attachments/assets/a7827bd6-a5c5-4361-9bac-bdb23ac7e24d)

# v2.6.0: Authentication improvements

This release enhances security and improves overall application performance.

CMS_BREAK -->

## Highlights

### Authentication improvements

This release enhances security around resetting passwords.

**Updated token handling in password resets**

> [!WARNING]  
Breaking changes: this change is not backward compatible due to security considerations

The token for updating provider identity is now accepted as a Bearer token in the authorization header instead of query parameters. 

Before:
```
POST /auth/user/emailpass/update?token=ey...
```
After:
```
POST /auth/user/emailpass/update
// headers
{ authorization: "Bearer ey..." }
```

This enhances security around token management, for example, by preventing token exposure in logs.

More specifically, this will affect the reset password flow that relies on this endpoint.

Required action
- In your client (e.g. Next.js storefront), move the token from query params to the authorization header

**Removed deprecated `actorType` from reset password event**
We have removed a long deprecated property, `actorType` from the reset password event. Instead, use the `actor_type`.

### API Routes loader refactor

> [!IMPORTANT]  
While these changes sound dramatic, if you have used middleware and API Routes as intended, there will be no required actions nor breaking changes.

This release includes a refactor of our API Routes loading mechanism. The previous loader had long-standing issues that required hacky workarounds. To resolve these issues, we’ve rebuilt the loader from scratch to ensure predictable behavior in how routes and middleware are registered within the application. 

The root of the issues with the old implementation was mainly related to the following decisions:

- Plugins, Medusa core, and the application used isolated router instances of Express. This means global middleware and error handlers are isolated, too. If you define an error handler in your app, it won't be able to handle errors from the core routes.

- Middlewares defined inside the `src/api/middlewares.ts` file were always defined before the filesystem routes, and this led to unexpected behavior in many scenarios. For example, the middleware handler registered for route `admin/payments/payment-providers` will also run when a request comes in for `/admin/payments/:id`, even though there is a static route in place for the `/admin/payments/payment-providers` URI.

- There was no clarity on the ordering in which routes and middleware were executed. With the new implementation, this has been fixed (more on ordering is discussed below).

- Overriding an existing route defines multiple express handlers under the hood. It worked previously, because the route handlers never call `next`, hence the previous implementation was never executed. With the newer implementation, the route overrides from a Medusa application will be registered over the routes from the core or the plugins.

To resolve these issues, we’ve rebuilt the loader from scratch to ensure predictable behavior in how routes and middleware are registered within the application. Additionally, the old API Routes loader unintentionally allowed overriding core API routes and middleware. While this behavior remains for now, it will be removed in a future release. Please prepare your application based on the behavior described in the following sections.

**Global middleware**

A global middleware is defined using the `defineMiddlewares` function without specifying an HTTP method. 
```
defineMiddlewares([
  {
    matcher: '/admin',
    middleware: [
      (req, res, next) => console.log("Hi from global middleware")
    ],
  }
])
```

The order of registering global middleware is as follows:
1. Global middleware from the core
2. Global middleware from plugins (in the order plugins are registered)
3. Global middleware from the app

**Route middleware**

A route middleware is also defined using the `defineMiddlewares` method. However, they must always specify the HTTP method(s) they want to target.
```
defineMiddlewares([
  {
    matcher: '/admin/*',
    methods: ["GET", "POST"],
    middleware: [
      (req, res, next) => console.log("Hi from route middleware")
    ],
  }
])
```

The order of registering route middleware is as follows:
1. Route middleware from the core
2. Route middleware from plugins (in the order plugins are registered)
3. Route middleware from the app

**API Routes**

An API Route is always attached to a method, decided by the exported const from the route file. API Routes are registered by scanning the filesystem.

Two types of API Routes are supported:
1. Static segments: `/admin/products/route.ts`
2. Dynamic segments: `/admin/products/[id]/route.ts`


**Sorting routes and middleware**

Before registering routes + middleware with Express, they are sorted as described below:

1. Global middleware
2. Route middleware
3. API Routes

On top of this, each segment from the API Route matcher is sorted by its specificity:

1. Wildcard segments, e.g. `/admin/products/*`
2. Regex segments, e.g. `/admin/(product-types|product-collections)`
3. Static segments, e.g. `/admin/products`
4. Dynamic segments, e.g. `/admin/products/:id`


**Important note about middleware**
Middleware **never overrides any existing middleware**; they are added to the stack of existing middleware. For example, if you define a custom validation middleware on an existing route, then both the original and the custom validation middleware will run.

### Performance improvements

This release contains general and, in some areas, significant performance improvements. Over the past weeks, we have focused on removing performance bottlenecks in Medusa. While many of the improvements impacted the entire application, the most significant ones are for cart operations.

Here's a summary of the changes and improvements:

**Application-level changes**
- Eliminated unnecessary module calls in cart workflows to ensure we don't create unused transactions and have redundant database communication
- Minimized redundant data fetching in cart workflows, ensuring that only the data that is needed for the operation is fetched
- Changed workflows to accept prefetched data instead of always fetching the data independently
- Introduced finer control over cart refresh logic to ensure we don't perform unnecessary cart refreshes
- Replaced the Redis `del` operation with the non-blocking delete `unlink` to reduce the load on Redis
- Replaced inefficient soft and hard deletes by performing the operation in a single query instead of multiple
- (Dashboard) Minimized redundant data fetching for dashboard pages

**Database-level changes**
These changes are a result of thorough testing of our own infrastructure. Therefore, they should be deemed a recommendation rather than an absolute configuration. Additionally, it is important to highlight that these tests were run against a Neon Database with a PgBouncer setup. Without PgBouncer and Neon, the configuration would likely look different.
```ts
// medusa-config.ts
export default defineConfig({
  projectConfig: {
    // ...other options
    databaseDriverOptions: {
      pool: {
        max: 8, // Fixed pool size of 8
        idleTimeoutMillis: 30000, // Close idle connections after 30 seconds
        connectionTimeoutMillis: 10000, // Fail on connection attempt after 10 seconds
        createRetryIntervalMillis: 100 // Time between connection retries
      },
    },
    ...
  },
}
```

**Results**

We have run two tests:

Test 1:
- Operation: Add to cart
- Database spec:
  - 0.25 CPU
  - 1 GB RAM
- Server spec:
  - 1 CPU
  - 2 GB RAM
- Load: 5 carts, operations for each cart run in parallel; for each cart, we add 100 items sequentially

p95: 1180ms -> 866ms
p50: 833ms -> 593ms

Test 2:
- Operation: Add to cart
- Database spec:
  - 0.5 CPU
  - 2 GB RAM
- Server spec:
  - 1 CPU
  - 2 GB RAM
- Load: 5 carts, operations for each cart run in parallel; for each cart, we add 100 items sequentially

p95: 1180ms -> 773ms
p50: 849 -> 537ms

Both tests show an approximate performance improvement of 30-35%. 

### Features
* feat: add middleware and routes sorter by @thetutlage in https://github.com/medusajs/medusa/pull/11526
* feat(core-flows,types,cart): add credit lines to cart by @riqwan in https://github.com/medusajs/medusa/pull/11419
* feat: add routes loader by @thetutlage in https://github.com/medusajs/medusa/pull/11592
* feat: add middleware-file-loader by @thetutlage in https://github.com/medusajs/medusa/pull/11638
* feat(utils): add error message when manager is not found in context by @riqwan in https://github.com/medusajs/medusa/pull/11693
* feat: Replace existing router with the new implementation by @thetutlage in https://github.com/medusajs/medusa/pull/11646

### Bugs
* fix(types, medusa): remove fulfillment and payment status filters from validator + http types by @shahednasser in https://github.com/medusajs/medusa/pull/11604
* fix(types): Allow providing either vite@5 or vite@6 for the peer dependency by @kasperkristensen in https://github.com/medusajs/medusa/pull/11581
* fix(promotion): scope uniqueness index to non deleted promotions by @riqwan in https://github.com/medusajs/medusa/pull/11624
* fix(core-flows): support 0 as a valid unit price for custom line items by @riqwan in https://github.com/medusajs/medusa/pull/11631
* fix(core-flows): unsafe access to a variant property by @fPolic in https://github.com/medusajs/medusa/pull/11588
* fix(medusa): deleting location level by @fPolic in https://github.com/medusajs/medusa/pull/11630
* fix: Exclude the health endpoint from being logged by @sradevski in https://github.com/medusajs/medusa/pull/11650
* fix(dashboard): display CG in PL configuration details by @fPolic in https://github.com/medusajs/medusa/pull/11653
* fix(dashboard): few admin bug fixes by @fPolic in https://github.com/medusajs/medusa/pull/11655
* fix(core-flows): return created order return from workflow by @fPolic in https://github.com/medusajs/medusa/pull/11642
* fix(dashboard): filter outbound shipping options in RMA by @fPolic in https://github.com/medusajs/medusa/pull/11629
* fix(dashboard): variant inventory section by @fPolic in https://github.com/medusajs/medusa/pull/11669
* fix(dashboard): bust variant inventory cache on inventory update by @riqwan in https://github.com/medusajs/medusa/pull/11690
* fix(types,medusa): calculate taxes for original price by @riqwan in https://github.com/medusajs/medusa/pull/11692
* fix(dashboard): Use derived state in DataTable by @kasperkristensen in https://github.com/medusajs/medusa/pull/11487
* fix(dashboard, core-flows, medusa): prevent creation of empty fulfillments by @fPolic in https://github.com/medusajs/medusa/pull/11664
* fix(order): summary raw totals by @carlos-r-l-rodrigues in https://github.com/medusajs/medusa/pull/11712

### Documentation
* docs: fix start command in deployment guides by @shahednasser in https://github.com/medusajs/medusa/pull/11571
* docs: updates for 2.5.1 release by @shahednasser in https://github.com/medusajs/medusa/pull/11574
* docs: fix OAS generator + generate OAS by @shahednasser in https://github.com/medusajs/medusa/pull/11584
* docs: fix to recipe after latest update by @shahednasser in https://github.com/medusajs/medusa/pull/11586
* docs: document different ways of selling products by @shahednasser in https://github.com/medusajs/medusa/pull/11446
* docs: document swc dependency for plugins by @shahednasser in https://github.com/medusajs/medusa/pull/11426
* docs: fix incorrect link in tax-inclusive-pricing by @shahednasser in https://github.com/medusajs/medusa/pull/11599
* docs: fix types for provider docs not generated correctly by @shahednasser in https://github.com/medusajs/medusa/pull/11598
* docs: finish user guide updates by @shahednasser in https://github.com/medusajs/medusa/pull/11603
* docs: prepare projects configurations for user guide by @shahednasser in https://github.com/medusajs/medusa/pull/11610
* docs: add user guide to navbar by @shahednasser in https://github.com/medusajs/medusa/pull/11611
* docs: add ERP recipe + Odoo guide by @shahednasser in https://github.com/medusajs/medusa/pull/11594
* docs: add user guide tags + sidebar links for commerce modules by @shahednasser in https://github.com/medusajs/medusa/pull/11612
* docs: fix small typo by @shahednasser in https://github.com/medusajs/medusa/pull/11616
* docs: add new cards to recipe section by @shahednasser in https://github.com/medusajs/medusa/pull/11615
* docs: add notes + missing links for user guide by @shahednasser in https://github.com/medusajs/medusa/pull/11621
* docs: updates to tax provider and other provider guides by @shahednasser in https://github.com/medusajs/medusa/pull/11626
* docs: change question in user guide feedback form by @shahednasser in https://github.com/medusajs/medusa/pull/11628
* docs: fix meta assets of user guide by @shahednasser in https://github.com/medusajs/medusa/pull/11639
* docs: update subscriptions recipe with stripe payment by @shahednasser in https://github.com/medusajs/medusa/pull/11651
* docs: typo on homePage codeTabs notificationModuleService by @Rkallenkoot in https://github.com/medusajs/medusa/pull/11658
* docs: move edit date to footer by @shahednasser in https://github.com/medusajs/medusa/pull/11667
* docs: fix weird scroll behavior by @shahednasser in https://github.com/medusajs/medusa/pull/11668
* docs: fix examples in workflows reference by @shahednasser in https://github.com/medusajs/medusa/pull/11688
* docs: add section on row click in DataTable by @shahednasser in https://github.com/medusajs/medusa/pull/11694
* docs: add more clarification to reset password in API reference by @shahednasser in https://github.com/medusajs/medusa/pull/11695
* docs: Added a missing "/" by @Sai-Santhan-Dodda in https://github.com/medusajs/medusa/pull/11696
* docs: fix Sanity example by @ismailsabet in https://github.com/medusajs/medusa/pull/11697
* docs: open prerequisite links in new tab by @shahednasser in https://github.com/medusajs/medusa/pull/11701
* docs: update OAS of reset password routes by @shahednasser in https://github.com/medusajs/medusa/pull/11711

### Chores
* chore(ui): explicitely specify the props type of InlineTip by @shahednasser in https://github.com/medusajs/medusa/pull/11575
* chore: update TSDocs of account holder additions by @shahednasser in https://github.com/medusajs/medusa/pull/11576
* chore(types): change payment provider types to interfaces by @shahednasser in https://github.com/medusajs/medusa/pull/11597
* chore(ui): updates to InlineTip's TSDocs by @shahednasser in https://github.com/medusajs/medusa/pull/11580
* chore(): Improve internal repository delete algo by @adrien2p in https://github.com/medusajs/medusa/pull/11601
* chore(core-flows): reserve inventory from available location by @carlos-r-l-rodrigues in https://github.com/medusajs/medusa/pull/11538
* chore(product): revamp upsertWithReplace and Remove its usage from product creation by @adrien2p in https://github.com/medusajs/medusa/pull/11585
* chore(core-utils): avoid overfetching to refresh cart by @carlos-r-l-rodrigues in https://github.com/medusajs/medusa/pull/11602
* chore(core-flows): pass cart as reference to subflows by @carlos-r-l-rodrigues in https://github.com/medusajs/medusa/pull/11617
* chore(core-flows): use cart id when force_refresh is true by @carlos-r-l-rodrigues in https://github.com/medusajs/medusa/pull/11625
* chore(types): improvements to tax provider tsdocs by @shahednasser in https://github.com/medusajs/medusa/pull/11627
* chore: Move token from params to body by @olivermrbl in https://github.com/medusajs/medusa/pull/11281
* chore: prevent workflow steps to call modules when not necessary by @adrien2p in https://github.com/medusajs/medusa/pull/11632
* chore(): Improve cascade soft deletetion/restoration and update by @adrien2p in https://github.com/medusajs/medusa/pull/11618
* chore(): Add support for extra pool configuration by @adrien2p in https://github.com/medusajs/medusa/pull/11636
* chore(): Prevent sub workflow events release early + redis unlink by @adrien2p in https://github.com/medusajs/medusa/pull/11641
* Chore/link module serialization by @adrien2p in https://github.com/medusajs/medusa/pull/11643
* chore(): Improve cart update line items by @adrien2p in https://github.com/medusajs/medusa/pull/11666
* chore(workflows-sdk): fix examples in TSDocs by @shahednasser in https://github.com/medusajs/medusa/pull/11689
* chore(framework): Unified resource loading and exclude non js/ts files by @adrien2p in https://github.com/medusajs/medusa/pull/11707

### Other Changes
* Fix/add additionl data to product categories hook by @bouazzaayyoub in https://github.com/medusajs/medusa/pull/11226
* Add missing translations & change some badly translated by @Furman1331 in https://github.com/medusajs/medusa/pull/11670
* feat: added hook for createStockLocationsWorkflow by @Avia-Code in https://github.com/medusajs/medusa/pull/11491

## New Contributors
* @bouazzaayyoub made their first contribution in https://github.com/medusajs/medusa/pull/11226
* @Rkallenkoot made their first contribution in https://github.com/medusajs/medusa/pull/11658
* @Sai-Santhan-Dodda made their first contribution in https://github.com/medusajs/medusa/pull/11696
* @ismailsabet made their first contribution in https://github.com/medusajs/medusa/pull/11697

**Full Changelog**: https://github.com/medusajs/medusa/compare/v2.5.1...v2.6.0