# How to Handle GET, POST, PUT, and DELETE Request Methods in Medusa API Routes

> Learn how to handle GET POST PUT DELETE request methods in Medusa API routes. Export named async functions from route.ts for robust API development.

- Repository: [Medusa/medusa](https://github.com/medusajs/medusa)
- Tags: how-to-guide
- Published: 2026-05-19

---

**Medusa API routes handle HTTP verbs by exporting named asynchronous functions (`GET`, `POST`, `PUT`, `DELETE`) from a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file, which the framework's router loader in [`packages/core/framework/src/http/router.ts`](https://github.com/medusajs/medusa/blob/main/packages/core/framework/src/http/router.ts) automatically registers on the Express router using typed `MedusaRequest` and `MedusaResponse` wrappers.**

The `medusajs/medusa` repository implements a convention-based routing system where each API endpoint lives in a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file under `packages/medusa/src/api/...`. Inside each file, you export functions matching the HTTP methods you want to support, enabling a clean separation between routing infrastructure and business logic workflows.

## Understanding the Named Export Handler Pattern

When the Medusa server initializes, the **router loader** ([`packages/core/framework/src/http/router.ts`](https://github.com/medusajs/medusa/blob/main/packages/core/framework/src/http/router.ts)) scans the API directory for [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) files. It inspects the exports and registers any functions named `GET`, `POST`, `PUT`, or `DELETE` as handlers for their respective HTTP verbs on the corresponding route path.

Every handler follows a strict TypeScript signature:

```ts
export const <METHOD> = async (
  req: MedusaRequest<Params, Query>,
  res: MedusaResponse<ResponseBody>
) => { … }

```

- **`MedusaRequest`** – Wraps Express' `Request` to inject Medusa-specific helpers including `req.scope` for dependency injection, `req.filterableFields` for parsed query parameters, and `req.queryConfig` for field selection.
- **`MedusaResponse`** – Wraps Express' `Response` providing typed `json()`, `status()`, and other response methods.

Inside a handler, the standard flow involves reading input from the request object, resolving a workflow or service via `req.scope`, executing the workflow, and returning a typed JSON response.

## Implementing GET Handlers for Read Operations

Use the `GET` export for read-only endpoints that retrieve data. The handler typically extracts filterable fields from `req.filterableFields` and passes them to a listing workflow.

The following example from the store shipping options endpoint demonstrates retrieving cart-specific shipping options:

```ts
// packages/medusa/src/api/store/shipping-options/route.ts
export const GET = async (
  req: MedusaRequest<{}, HttpTypes.StoreGetShippingOptionList>,
  res: MedusaResponse<HttpTypes.StoreShippingOptionListResponse>
) => {
  const { cart_id, is_return } = req.filterableFields

  const workflow = listShippingOptionsForCartWorkflow(req.scope)
  const { result: shipping_options } = await workflow.run({
    input: {
      cart_id,
      is_return: !!is_return,
      fields: req.queryConfig.fields,
    },
  })

  res.json({ shipping_options })
}

```

The handler resolves `listShippingOptionsForCartWorkflow` from the request scope, passes the cart ID and return flag from the parsed query parameters, and returns the result as JSON.

## Creating Resources with POST Handlers

The `POST` export handles resource creation and action endpoints such as creating carts, adding line items, or processing payments. These handlers read data from `req.body` and typically return a `201 Created` status.

Example from the cart creation endpoint:

```ts
// packages/medusa/src/api/store/carts/route.ts
export const POST = async (
  req: MedusaRequest<{}, HttpTypes.StorePostCartReq>,
  res: MedusaResponse<HttpTypes.StoreCartRes>
) => {
  const workflow = createCartWorkflow(req.scope)
  const { result: cart } = await workflow.run({
    input: {
      ...req.body,
      fields: req.queryConfig.fields,
    },
  })

  res.status(201).json({ cart })
}

```

Notice how the handler spreads `req.body` into the workflow input and explicitly sets the HTTP status to `201` to indicate successful resource creation.

## Removing Data with DELETE Handlers

Use the `DELETE` export to remove resources or revoke actions. These handlers extract resource identifiers from `req.params` and return a `200 OK` or `204 No Content` response upon successful deletion.

Example from the cart line item deletion endpoint:

```ts
// packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts
export const DELETE = async (
  req: MedusaRequest<{}, HttpTypes.StoreDeleteCartLineItemReq>,
  res: MedusaResponse<void>
) => {
  const workflow = deleteCartLineItemWorkflow(req.scope)
  await workflow.run({
    input: {
      cart_id: req.params.id,
      line_id: req.params.line_id,
    },
  })

  res.status(200).send()
}

```

The handler captures the cart ID and line item ID from the URL parameters, executes `deleteCartLineItemWorkflow`, and sends an empty response with a `200` status code.

## Handling PUT Requests

While the same named-export pattern applies to `PUT` handlers, Medusa rarely uses them. According to the source code conventions, updates are generally performed via `POST` requests to specific "update" endpoints (e.g., `/admin/products/:id`) rather than using `PUT`. When you do need a `PUT` handler, implement it using the same `async (req, res)` signature as `GET` or `POST`.

## Summary

- **Named exports drive routing** – Export `GET`, `POST`, `PUT`, or `DELETE` functions from [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) files to define supported HTTP methods.
- **Typed request/response objects** – Use `MedusaRequest` and `MedusaResponse` for type safety and access to Medusa-specific features like `req.scope` and `req.filterableFields`.
- **Workflow delegation** – Resolve workflows via `req.scope` and execute them with typed input rather than writing business logic directly in route handlers.
- **Status codes matter** – Return `201` for successful `POST` requests, `200` for successful `DELETE` requests, and use `res.json()` for structured responses.

## Frequently Asked Questions

### What is the difference between `MedusaRequest` and Express's standard `Request` object?

`MedusaRequest` is a thin wrapper around Express' `Request` that injects Medusa-specific properties including `req.scope` for accessing the dependency injection container, `req.filterableFields` for parsed query parameters, and `req.queryConfig` for field selection configuration. This allows handlers to interact with Medusa's modular architecture while maintaining Express compatibility.

### Can I define multiple HTTP methods in the same [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file?

Yes, you can export multiple handlers from a single file. For example, exporting both `GET` and `POST` from [`packages/medusa/src/api/store/products/route.ts`](https://github.com/medusajs/medusa/blob/main/packages/medusa/src/api/store/products/route.ts) registers both methods on the same route path, with each function handling its respective verb using the shared `MedusaRequest` and `MedusaResponse` types.

### Why does Medusa use `POST` instead of `PUT` for updates?

Medusa's API design favors `POST` requests to specific action endpoints (like `/admin/products/:id`) for updates rather than using `PUT`. This aligns with the framework's workflow-based architecture where updates often trigger complex business processes. While `PUT` handlers are technically supported via the named export pattern, they are rarely implemented in the core codebase.

### How do I access services or workflows inside a route handler?

Access the dependency injection container through `req.scope`. For workflows, import the workflow function (e.g., `createCartWorkflow` from `@medusajs/core-flows`) and invoke it with `req.scope` as the argument: `const workflow = createCartWorkflow(req.scope)`. Then execute the workflow using `await workflow.run({ input: ... })` to trigger the business logic.