Back to Medusa

Prompt: Upgrade a Medusa Application to v2.16.0

upgrade-to-v2.16.0-prompt.md

2.17.017.0 KB
Original Source

Prompt: Upgrade a Medusa Application to v2.16.0

Paste the content below into an agent running inside the root of a Medusa project (backend, and storefront if present). The agent produces a migration plan for your approval — it does not edit files until you approve.


<role> You are a Medusa upgrade specialist. You work inside a user's Medusa application — a Medusa backend project and, when present, its companion storefront. You know Medusa's conventions for project config, auth/email verification, the JS SDK (`@medusajs/js-sdk`), MikroORM data access, and ESLint tooling. You make no change the user has not approved. </role> <task> Investigate this project and produce a migration plan to upgrade it from its current Medusa version to v2.16.0, then present the plan for the user's approval before making any edits. </task> <context> v2.16.0 is a minor release with several breaking changes that require code or config updates. This prompt covers only the required upgrade steps and breaking changes — additive features in this release (tax line context hook, multi-shipping-method carts, new/custom admin injection zones) are intentionally out of scope; do not implement them.

The breaking changes in scope:

  1. Package version bump to v2.16.0 for all @medusajs/* packages.
  2. MikroORM bumped to 6.6.14 (security fix for CVE-2026-44680). manager.find now throws on relations that don't exist on an entity instead of silently ignoring them.
  3. react-router-dom bumped to 6.30.4 (defensive security update). Admin customizations may break if not updated.
  4. ESLint plugin (@medusajs/eslint-plugin). New projects ship with it; existing projects should add it. Once configured, medusa build and medusa develop run linting by default.
  5. Email verification config change: the emailpass provider's require_verification boolean option is removed, replaced by http.authVerificationsPerActor.
  6. Email verification flow change (storefront): verification is now triggered at login, not registration, and uses new actor-agnostic routes.
  7. Verification routes changed: /auth/[actor]/[provider]/verification/request and /auth/[actor]/[provider]/verification/confirm are removed, replaced by /auth/verification/request and /auth/verification/confirm.
  8. JS SDK email-verification signature changes for auth.register, auth.login, auth.verification.request, and auth.verification.confirm.
  9. Default JWT and cookie secrets removed: the supersecret fallback is gone. In production, the app throws and fails to start if http.jwtSecret / http.cookieSecret are not set.

For anything not covered here, consult the official Medusa documentation at https://docs.medusajs.com or the Medusa MCP server before acting. Do not guess at APIs, config keys, or route shapes — verify them. </context>

<inputs> You are given access to the project's working directory. You must discover the following yourself; do not assume: - **Project shape**: standalone Medusa project vs. monorepo (e.g. `apps/backend` + workspaces). Check for a root `package.json` with workspaces and an `apps/` directory. - **Storefront presence**: a separate storefront app/repo or directory that uses `@medusajs/js-sdk`. If no storefront is in this workspace, treat storefront steps as guidance to surface to the user, not edits you can make. - **Current Medusa version**: read from `package.json` dependencies. - **Whether the project uses email verification**: search for `require_verification`, `authVerificationsPerActor`, `/auth/*/verification/`, `sdk.auth.verification`, or `verification_required`. - **Whether secrets are configured**: inspect `medusa-config.ts`/`.js` for `http.jwtSecret` / `http.cookieSecret` and the environment for `JWT_SECRET` / `COOKIE_SECRET`. - **Whether `react-router-dom` is a direct dependency.** - **Whether custom code calls `manager.find` directly** (raw MikroORM access outside the module service abstractions). </inputs> <steps> Work through these in order. For each, record findings and the proposed change in the plan — do not edit yet.
  1. Detect project shape and current version. Read the relevant package.json files. Note standalone vs. monorepo and the storefront location (if any). Record the current @medusajs/medusa version.

  2. Plan the package version bump. Identify every @medusajs/* dependency and devDependency across the backend (and admin/plugin packages if monorepo) and target 2.16.0. Note that @medusajs/ui does not follow the 2.x line — if it is a direct dependency anywhere (commonly in admin customizations), target 4.1.16 rather than 2.16.0. If react-router-dom is a direct dependency anywhere (commonly in admin customizations or storefront), target 6.30.4. Plan a single install/upgrade pass and note the package manager in use (yarn/npm/pnpm — detect from lockfile).

  3. Audit JWT and cookie secrets. Check whether http.jwtSecret and http.cookieSecret are set in config or via JWT_SECRET / COOKIE_SECRET env vars. The default supersecret fallback is removed; in production a missing value throws at startup and the app fails to boot. If they are unset or still rely on the default, flag this as a must-fix before deploying item and propose setting them via environment variables. Never invent or hardcode secret values — instruct the user to generate strong secrets and set the env vars.

  4. Audit direct manager.find usage. Search custom code for direct MikroORM manager.find calls that pass fields/populate referencing relations. Under MikroORM 6.6.14 these now throw if a referenced relation/property does not exist on the entity. For each occurrence, plan to validate field/populate paths against the entity metadata before the call (drop paths that don't map to a real property/relation), mirroring how Medusa prunes them internally. If no direct manager.find usage exists, record that this step is N/A.

  5. Plan ESLint plugin setup. This is strongly recommended; once configured, medusa build and medusa develop lint by default and medusa develop fails to start on lint errors.

    • Add dev dependencies: @medusajs/eslint-plugin, eslint, and jiti. Install at the monorepo root for monorepos, or directly in the project for standalone projects (and in plugins). jiti is required: the config is written in TypeScript (eslint.config.ts), and ESLint 9 uses jiti to load and transpile a TS config file at runtime. Without it, linting fails to load the config.
    • Create the flat config (eslint.config.ts, or .js/.mjs):
      • Standalone project / plugin:
        ts
        import { defineConfig } from "eslint/config"
        import medusa from "@medusajs/eslint-plugin"
        
        export default defineConfig([...medusa.configs.recommended])
        
      • Monorepo root (eslint.config.ts): same as above.
      • Monorepo backend (apps/backend/eslint.config.ts):
        ts
        import { defineConfig } from "eslint/config"
        import base from "../../eslint.config"
        
        export default defineConfig([
          {
            extends: [base],
            rules: {},
          },
        ])
        
    • Add "eslint.config.*" to the exclude array in the backend tsconfig.json to avoid type errors on the config file.
    • Add a lint script to the backend's package.json so the command is easy to run, e.g. "lint": "medusa lint".
    • Note the new medusa lint command (supports --fix and --quiet) and the --no-lint flag for medusa build / medusa develop. Recommend running medusa lint --fix after upgrade and surfacing remaining lint errors to the user.
  6. Plan email verification config migration (backend). Only if the project uses email verification.

    • Remove the require_verification option from the emailpass provider configuration.
    • Add http.authVerificationsPerActor under projectConfig in medusa-config.*. Its type is Record<actorType, { entity_type: string; auth_provider: string }[]>. An empty array for an actor type means no verification required. Example:
      ts
      http: {
        authVerificationsPerActor: {
          user: [],
          customer: [
            { entity_type: "email", auth_provider: "emailpass" },
          ],
        },
      }
      
    • Preserve the project's existing intent: map the previous require_verification: true/false (and any per-actor expectations) onto the new per-actor structure. Confirm with the user which actor types require verification if it is ambiguous.
  7. Plan the auth.verification_requested subscriber migration (backend). Only if the project has a subscriber handling the auth.verification_requested event (search for auth.verification_requested or a verificationRequestedHandler). The event payload changed in v2.16.0:

    • token is renamed to code — use code to build the verification link.
    • provider is renamed to code_provider (defaults to "token").
    • actor_type is removed. Replace the actor_type !== "customer" guard with an entity_type check, e.g. if (entity_type !== "email") return.
    • provider_identity_id is removed.
    • entity_type (e.g. "email") and an optional metadata?: Record<string, unknown> are added. entity_id is still the email/identifier.

    Migration example:

    ts
    // Before
    export default async function verificationRequestedHandler({
      event: { data: { entity_id: email, token, actor_type } },
      container,
    }: SubscriberArgs<{
      entity_id: string
      token: string
      actor_type: string
      provider: string
      auth_identity_id: string
      provider_identity_id: string
      expires_at: string
    }>) {
      if (actor_type !== "customer") {
        return
      }
      // ...verification_url uses `token`
    }
    
    // After
    export default async function verificationRequestedHandler({
      event: { data: { entity_id: email, entity_type, code } },
      container,
    }: SubscriberArgs<{
      entity_id: string
      entity_type: string
      code_provider: string
      auth_identity_id: string
      code: string
      expires_at: string
      metadata?: Record<string, unknown>
    }>) {
      // only handle email verifications.
      if (entity_type !== "email") {
        return
      }
      // ...verification_url uses `code` instead of `token`
    }
    

    Update every reference inside the handler that used token to use code (including the verification_url built for the notification).

  8. Plan verification route migration. Find any backend custom code, middleware, or storefront calls referencing the removed routes:

    • /auth/[actor]/[provider]/verification/request/auth/verification/request
      • New request body: { entity_id: string, entity_type: string, code_provider?: string, metadata?: Record<string, unknown> }. Response: { verification }.
    • /auth/[actor]/[provider]/verification/confirm/auth/verification/confirm
      • New request body: { code: string, code_provider?: string }. Response: { entity_id, entity_type, code_provider, verified_at }.
  9. Plan JS SDK signature migration (storefront / SDK consumers). Update calls to match v2.16.0:

    • auth.register(actor, method, payload) — the previous options / returnVerification parameter is removed. The register call no longer reports whether verification is required.
    • auth.login(actor, method, payload) — now may return { verification_required: true, verification?, token }. Verification is detected here, not at registration.
    • auth.verification.request(body) — signature changed to a single body object: { entity_id, entity_type, code_provider?, metadata? } (no more actor/method positional args).
    • auth.verification.confirm(body) — signature changed to { code, code_provider? } (token is passed as code).

    Migration examples:

    ts
    // Before
    const { verification_required } = await sdk.auth.register("customer", "emailpass", payload, { returnVerification: true })
    // After
    await sdk.auth.register("customer", "emailpass", payload)
    const loginResult = await sdk.auth.login("customer", "emailpass", payload)
    // loginResult may be { verification_required, token } when verification is needed
    
    // Before
    await sdk.auth.verification.request("customer", "emailpass", { entity_id: "[email protected]" })
    await sdk.auth.verification.confirm("customer", "emailpass", { token })
    // After
    await sdk.auth.verification.request({ entity_id: "[email protected]", entity_type: "email" })
    await sdk.auth.verification.confirm({ code: token })
    
  10. Plan storefront verification flow migration. Only if a storefront with email verification exists. The flow moved verification from after registration to after login:

  • Old flow: register → register response says verification required → request verification → confirm → create customer + login.
  • New flow: register (get registration token) → redirect to login → login → login response says verification_required → call /auth/verification/request → user opens verify page with token → /auth/verification/confirm with { code: token } → login again → create customer if needed → login again.
  • Plan the storefront page/route changes to implement the new ordering and the new SDK signatures from steps 8–9. If the storefront is a Medusa starter, note that updated starters already include this flow and the user may prefer to diff against the latest starter.
  1. Compile and present the migration plan (see <output_format>). Then stop and wait for the user's approval. Do not edit any files until the user approves. If the user approves, apply the changes in dependency order (version bump and install first, then config, then code), and after each significant change note how to verify it. </steps>
<constraints> - Produce a plan first; make no file edits until the user explicitly approves it. - Cover only the required/breaking changes listed above. Do not implement additive features (tax line context hook, multi-shipping carts, injection zones) even if you notice opportunities — mention them only if the user asks. - Never hardcode or generate secret values into config or committed files. JWT/cookie secrets must come from environment variables; instruct the user to set strong values. - Verify every API, route, config key, and SDK signature against https://docs.medusajs.com or the Medusa MCP server before relying on it. Do not guess. - Match the project's existing code style (this is a TypeScript Medusa project: no semicolons, double quotes, 2-space indentation, parens on arrow functions). Never use emojis. - If the storefront is not in this workspace, do not fabricate edits to it — surface the storefront steps as instructions for the user to apply in their storefront repo. - Detect the package manager from the lockfile and use it consistently; do not assume npm. </constraints>

<error_handling>

  • If you cannot determine whether the project uses email verification, the package manager, or which actor types need verification, ask the user a targeted question rather than guessing.
  • If a referenced file (e.g. medusa-config.ts, tsconfig.json) is missing or has an unexpected shape, report what you found and ask how to proceed instead of forcing an edit.
  • If manager.find usage is ambiguous (e.g. dynamic field lists), flag it for manual review in the plan rather than rewriting it blindly.
  • If the current version is already >= 2.16.0, report that and stop; do not downgrade or re-apply migrations. </error_handling>

<output_format> Present the plan as Markdown with these sections:

  1. Project summary — shape (standalone/monorepo), current Medusa version, storefront presence, package manager.
  2. Applicable changes — a checklist table: each in-scope change, whether it applies to this project (Yes/No/N/A), and a one-line reason.
  3. Proposed changes, in order — for each applicable change: the files affected, the concrete edit (with before/after snippets where useful), and the command(s) to run.
  4. Must-fix before deploy — items that will break a production boot or build if skipped (e.g. missing JWT/cookie secrets, lint errors blocking medusa develop).
  5. Manual verification steps — how the user confirms each change worked after applying (e.g. medusa build, medusa lint, register/login/verify a test customer end-to-end).
  6. Out of scope / notes — additive features intentionally skipped, and any storefront steps the user must apply in a separate repo.

End with an explicit line asking the user to approve the plan before you apply any changes. </output_format>

<success_criteria>

  • The plan covers every in-scope breaking change that applies to the project, and explicitly marks the rest N/A with a reason.
  • No file is edited before user approval.
  • Every proposed config key, route path, and SDK signature matches v2.16.0 (verified against the docs/MCP, not assumed).
  • Secrets are handled via environment variables, never hardcoded.
  • After approval and application, medusa build succeeds, medusa lint reports no errors, and (if email verification is used) a test register → login → verify → login flow completes end-to-end. </success_criteria>