# Medusa API Route Conventions: File-Based Routing and HTTP Handler Patterns

> Discover Medusa API route conventions with file-based routing and HTTP handler patterns. Learn how to define endpoints using route.ts files and export uppercase HTTP verbs for efficient API development.

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

---

**Medusa API route conventions use a file-based system where each endpoint is defined in a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file under `packages/medusa/src/api/<scope>/<resource>/`, exporting HTTP verbs as uppercase constants like `GET` and `POST` with typed `AuthenticatedMedusaRequest` and `MedusaResponse` objects, while delegating business logic to workflows via dependency injection.**

The `medusajs/medusa` repository implements a strict, framework-level convention for building HTTP endpoints that separates routing concerns from business logic. Understanding these Medusa API route conventions is essential for extending the platform, as every store, admin, and plugin interface follows the same file-based structure and handler signature patterns established in the core codebase.

## File-Based Route Structure and Naming Conventions

Medusa uses a filesystem-based router where the directory path determines the final URL. Routes are organized under `packages/medusa/src/api/` into three primary scopes: **store**, **admin**, and plugin-specific namespaces.

### Route Files and Dynamic Segments

Each resource endpoint resides in a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file. For endpoints requiring identifiers, use the `[id]/route.ts` pattern, which maps to `/:id` in the URL structure.

- Store products list: [`packages/medusa/src/api/store/products/route.ts`](https://github.com/medusajs/medusa/blob/main/packages/medusa/src/api/store/products/route.ts) → `/store/products`
- Admin product by ID: `packages/medusa/src/api/admin/products/[id]/route.ts` → `/admin/products/:id`
- Nested calculations: `packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts` → `/store/shipping-options/:id/calculate`

### Exported HTTP Handlers

Within each [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file, HTTP verbs are exported as uppercase constants. The handler signature always follows `(req, res) => Promise<void>`.

Supported exports include:
- `GET` for retrieval operations
- `POST` for resource creation
- `PUT` and `PATCH` for updates
- `DELETE` for removals

## Typed Request and Response Objects

Type safety is enforced through generic request and response interfaces that vary by authentication scope.

### Store vs Admin Authentication

Store routes use `MedusaRequest<T>`, while admin routes require `AuthenticatedMedusaRequest<T>` to ensure proper authorization checks. Responses are typed with `MedusaResponse<T>`, commonly using namespaces from `@medusajs/framework/types` such as `HttpTypes.StoreProductListResponse` or `HttpTypes.AdminProductDeleteResponse`.

## Dependency Resolution and Workflow Integration

Rather than embedding business logic directly in route handlers, Medusa delegates to workflows through dependency injection.

### Accessing Services and Modules

Dependencies are resolved via `req.scope.resolve(<key>)`, typically using constants from the `Modules` enum:

```typescript
const productService = req.scope.resolve(Modules.PRODUCT)

```

### Workflow Execution

Business operations are handled by imported workflows from `@medusajs/core-flows`, which the route executes and returns:

```typescript
const { result } = await listProductsWorkflow(req.scope).run({
  input: { filterableFields, listConfig },
})

```

## Validation, Query Config, and Middleware Patterns

Medusa API route conventions enforce co-location of supporting utilities with route files to maintain modularity.

### Co-Located Utilities

Each route directory can contain:
- [`validators.ts`](https://github.com/medusajs/medusa/blob/main/validators.ts): Zod schemas or custom validation functions for request payloads
- [`query-config.ts`](https://github.com/medusajs/medusa/blob/main/query-config.ts): Default pagination limits, selectable fields, and searchable field definitions
- `middlewares/`: Cross-cutting concerns like tax context, pricing calculations, and inventory checks

Middlewares are composed in the route file using `router.use()` before handler registration.

## Practical Code Examples

### Basic Store GET Handler

The following example from [`packages/medusa/src/api/store/products/route.ts`](https://github.com/medusajs/medusa/blob/main/packages/medusa/src/api/store/products/route.ts) demonstrates a standard list operation:

```typescript
import {
  AuthenticatedMedusaRequest,
  MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { listProductsWorkflow } from "@medusajs/core-flows"

export const GET = async (
  req: AuthenticatedMedusaRequest,
  res: MedusaResponse<HttpTypes.StoreProductListResponse>
) => {
  const { filterableFields, listConfig } = req

  const { result } = await listProductsWorkflow(req.scope).run({
    input: {
      filterableFields,
      listConfig,
    },
  })

  res.status(200).json(result)
}

```

### Admin DELETE Handler

For admin operations requiring authentication, use `AuthenticatedMedusaRequest` as shown in `packages/medusa/src/api/admin/products/[id]/route.ts`:

```typescript
export const DELETE = async (
  req: AuthenticatedMedusaRequest,
  res: MedusaResponse<HttpTypes.AdminProductDeleteResponse>
) => {
  const { id } = req.params

  await deleteProductWorkflow(req.scope).run({
    input: { id },
  })

  res.status(200).json({
    id,
    object: "product",
    deleted: true,
  })
}

```

### Nested Dynamic Route

Deeply nested endpoints follow the `[param]/subresource` pattern, as seen in `packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts`:

```typescript
export const POST = async (
  req: AuthenticatedMedusaRequest,
  res: MedusaResponse<HttpTypes.StoreShippingOptionCalculateResponse>
) => {
  const { id } = req.params
  const { cart_id, address } = req.body

  const { result } = await calculateShippingOptionWorkflow(req.scope).run({
    input: { shipping_option_id: id, cart_id, address },
  })

  res.status(200).json(result)
}

```

## Summary

- **File-based routing**: Routes are defined in [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) files located under `packages/medusa/src/api/<scope>/<resource>/`, with dynamic segments using `[id]` folder syntax.
- **HTTP verb exports**: Handlers are exported as uppercase constants (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) with standardized `(req, res) => Promise<void>` signatures.
- **Type safety**: Store routes use `MedusaRequest` while admin routes require `AuthenticatedMedusaRequest`, both paired with `MedusaResponse<T>`.
- **Dependency injection**: Services and modules are accessed via `req.scope.resolve()` with workflow delegation to `@medusajs/core-flows`.
- **Co-located utilities**: Validation logic, query configurations, and middleware are stored alongside routes in [`validators.ts`](https://github.com/medusajs/medusa/blob/main/validators.ts), [`query-config.ts`](https://github.com/medusajs/medusa/blob/main/query-config.ts), and `middlewares/` directories.

## Frequently Asked Questions

### How do I create a custom admin API route in Medusa?

Create a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file under `packages/medusa/src/api/admin/<your-resource>/` and export your HTTP handlers using uppercase constants. Use `AuthenticatedMedusaRequest` for the request type to ensure proper authentication, and resolve your custom services via `req.scope.resolve()` before delegating to workflows.

### What is the difference between store and admin route types in Medusa?

Store routes use the `MedusaRequest<T>` type for unauthenticated or session-based customer requests, while admin routes use `AuthenticatedMedusaRequest<T>` which enforces admin user authentication. Both use `MedusaResponse<T>` for responses, but admin routes typically include additional middleware for permissions and context initialization.

### How does Medusa handle route parameters in nested URLs?

Medusa implements nested dynamic routes using filesystem brackets, where `[id]/route.ts` creates a `/:id` endpoint and deeper nesting like `[id]/calculate/route.ts` produces `/:id/calculate` URLs. Access these parameters via `req.params` inside your handler, with the parameter name matching the bracket notation used in the directory structure.

### Where should validation logic be placed in Medusa API routes?

Validation logic should be extracted into a co-located [`validators.ts`](https://github.com/medusajs/medusa/blob/main/validators.ts) file within your route directory, imported and executed in the handler before workflow invocation. This pattern keeps route files clean while ensuring Zod schemas or custom validators enforce request payload shapes consistently across the API surface.