# How to Create a Custom Medusa API Route: File-Based Routing Guide

> Learn to create a custom Medusa API route using file-based routing. Simply add a route.ts file to your src/api/ directory and export HTTP method handlers for instant endpoint registration.

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

---

**You create a custom Medusa API route by placing a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file under `src/api/<path>/` and exporting HTTP method handlers (like `GET` or `POST`) named after the verbs they handle, which automatically registers the endpoint at the corresponding URL path.**

Medusa uses a **file-based routing system** that scans `src/api/**/route.ts` files on startup and wires them to the appropriate HTTP methods without requiring manual route registration. This architecture lets you extend your commerce backend by simply adding TypeScript files that follow the framework's naming conventions. The system supports dynamic path parameters, dependency injection via the container, and custom middleware through a centralized configuration file.

## Understanding File-Based Routing Structure

Medusa automatically discovers API endpoints by scanning the `src/api` directory of your project. The folder structure directly determines the final URL path, while the exported handler functions determine supported HTTP methods.

According to the official plugin documentation in [`packages/plugins/loyalty/src/api/README.md`](https://github.com/medusajs/medusa/blob/main/packages/plugins/loyalty/src/api/README.md), the framework expects:

- **Route files**: Must be named [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) (or [`route.js`](https://github.com/medusajs/medusa/blob/main/route.js)) and live inside folders under `src/api`
- **URL mapping**: The folder path becomes the endpoint path (e.g., [`src/api/store/hello-world/route.ts`](https://github.com/medusajs/medusa/blob/main/src/api/store/hello-world/route.ts) becomes `GET /store/hello-world`)
- **HTTP handlers**: Export functions named exactly after HTTP verbs: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, or `HEAD`

## Creating a Basic GET Endpoint

To create your first custom route, add a [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file in a new folder under `src/api`.

```typescript
// src/api/store/hello-world/route.ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
  res.json({ message: "Hello world!" })
}

```

When the server starts, Medusa registers this handler at `GET /store/hello-world`. The `MedusaRequest` and `MedusaResponse` types provide full access to the request context and response helpers from the `@medusajs/framework/http` package.

## Handling Dynamic Path Parameters

You capture dynamic URL segments by using bracketed folder names like `[param]`. The parameter values become available on `req.params`.

```typescript
// src/api/store/products/[productId]/route.ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
  const { productId } = req.params
  res.json({ message: `You requested product ${productId}` })
}

```

This pattern matches `GET /store/products/123` and extracts `"123"` as `req.params.productId`. You can nest multiple parameter folders to create complex route hierarchies.

## Accessing Services via Dependency Injection

Custom routes integrate with Medusa's service layer through the request-scoped container. Use `req.scope.resolve("<serviceKey>")` to access any registered service.

```typescript
// src/api/store/product-count/route.ts
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
  const productService = req.scope.resolve("product")
  const [, count] = await productService.listAndCount()
  res.json({ count })
}

```

The `product` service key resolves to the core Product service, allowing you to reuse existing business logic and database queries. This dependency injection pattern ensures your custom routes remain consistent with Medusa's data access patterns.

## Adding Custom Middleware

To execute code before your route handler runs, create a [`middlewares.ts`](https://github.com/medusajs/medusa/blob/main/middlewares.ts) file in `src/api` and export a configuration using `defineMiddlewares`.

```typescript
// src/api/middlewares.ts
import { defineMiddlewares } from "@medusajs/framework/http"
import type { MedusaRequest, MedusaResponse, MedusaNextFunction } from "@medusajs/framework/http"

async function logger(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction) {
  console.log("[Custom Logger]:", req.method, req.originalUrl)
  next()
}

export default defineMiddlewares({
  routes: [
    {
      matcher: "/store/hello-world",
      middlewares: [logger],
    },
  ],
})

```

The `matcher` property supports route patterns, and the `middlewares` array accepts functions with the standard Express-style signature `(req, res, next)`. This configuration applies the middleware only to the specified paths while leaving other routes unaffected.

## Real-World Implementation Reference

For complex implementations that query data, handle feature flags, and transform responses, examine the core product routes in the Medusa source. The [`packages/medusa/src/api/store/products/route.ts`](https://github.com/medusajs/medusa/blob/main/packages/medusa/src/api/store/products/route.ts) file demonstrates how production routes wire together query building, inventory checking, and tax price wrapping:

```typescript
// packages/medusa/src/api/store/products/route.ts
import { MedusaResponse } from "@medusajs/framework/http"
import { HttpTypes, QueryContextType } from "@medusajs/framework/types"
import {
  ContainerRegistrationKeys,
  FeatureFlag,
  isPresent,
  QueryContext,
} from "@medusajs/framework/utils"
import IndexEngineFeatureFlag from "../../../feature-flags/index-engine"
import { wrapVariantsWithInventoryQuantityForSalesChannel } from "../../utils/middlewares"
import { RequestWithContext, wrapProductsWithTaxPrices } from "./helpers"

export const GET = async (
  req: RequestWithContext<HttpTypes.StoreProductListParams>,
  res: MedusaResponse<HttpTypes.StoreProductListResponse>
) => {
  // Implementation handles pagination, filtering, and response transformation
}

```

This implementation shows how to leverage `ContainerRegistrationKeys` for type-safe service resolution and integrate with Medusa's feature flag system.

## Summary

- **File naming**: Create [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) files under `src/api/<path>/` to automatically register endpoints at the corresponding URL paths
- **HTTP handlers**: Export functions named exactly after HTTP verbs (`GET`, `POST`, `PUT`, `DELETE`, etc.) to handle specific methods
- **Dynamic segments**: Use bracketed folder names like `[productId]` to capture URL parameters accessible via `req.params`
- **Service access**: Resolve services through `req.scope.resolve("<serviceKey>")` to maintain clean dependency injection
- **Middleware**: Configure route-specific middleware in [`src/api/middlewares.ts`](https://github.com/medusajs/medusa/blob/main/src/api/middlewares.ts) using the `defineMiddlewares` export

## Frequently Asked Questions

### What file extension should I use for Medusa API routes?

Medusa supports both TypeScript (`.ts`) and JavaScript (`.js`) files. Use [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) for TypeScript projects to benefit from type safety with `MedusaRequest` and `MedusaResponse` types imported from `@medusajs/framework/http`.

### How do I handle POST requests and request body validation?

Export a `POST` function from your [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file. Access the request body through `req.body`, which Medusa validates according to your defined schemas. The request object includes typed body data when you extend the base `MedusaRequest` type with your validation schema.

### Can I register multiple HTTP methods on the same route?

Yes. Simply export multiple handler functions from the same [`route.ts`](https://github.com/medusajs/medusa/blob/main/route.ts) file, such as `GET`, `POST`, and `PUT`. Medusa registers each exported verb as a handler for that specific HTTP method on the same URL path defined by the folder structure.

### Where should I place global middleware that affects all routes?

Create [`src/api/middlewares.ts`](https://github.com/medusajs/medusa/blob/main/src/api/middlewares.ts) at the root of your API directory and export a `defineMiddlewares` configuration. Use wildcard matchers like `/store/*` or apply middleware to specific routes using exact path matchers. This file acts as the central registry for all custom middleware in your application.