How to Test Medusa API Routes: Complete Integration Testing Guide
You can test Medusa API routes using the createServer fixture, which spins up an in-memory Express server with full dependency injection and provides a supertest wrapper to execute authenticated HTTP requests against your routes.
Medusa's HTTP layer is built on a lightweight Express server that loads route handlers via the ApiLoader. The testing framework provides a special fixture at packages/core/framework/src/http/__fixtures__/server/index.ts that creates a fully-wired stack for integration testing, including authentication, query-string handling, and complete service registration.
Understanding the Test Infrastructure
The createServer Fixture
The createServer function, located in packages/core/framework/src/http/__fixtures__/server/index.ts, builds an in-memory Express application that mirrors production behavior. It loads all Medusa modules, registers the Awilix container, and loads routes from the repository root. The fixture returns a request function that wraps supertest, automatically handling Content-Type, Accept, and Host headers while building query strings.
Request Scope and Dependency Injection
Each test request receives a .scope property pointing to a fresh Awilix container, replicating Medusa's runtime request scoping. This ensures that services, repositories, and the workflow engine are properly instantiated per request, as implemented in the core framework.
Setting Up Your Test Environment
Import the fixture and initialize the server in your test suite's beforeAll block:
import { createServer } from "@medusajs/core/framework/src/http/__fixtures__/server"
const { request } = await createServer(__dirname)
The __dirname parameter points to the repository root, allowing the ApiLoader to discover and register all API routes, including packages/medusa/src/api/store/products/route.ts.
Testing Admin Routes with Authentication
Generating Admin JWT Tokens
The request function supports session injection via the adminSession option. The fixture automatically generates JWT tokens using the secret from your test configuration:
const response = await request("GET", "/admin/stores", {
adminSession: { userId: "admin_user_id" },
headers: adminHeaders,
})
expect(response.status).toBe(200)
Testing Store Routes with Publishable Keys
For customer-facing endpoints, create a publishable API key using admin credentials, then pass it in the x-publishable-api-key header:
const publishableKey = await api.post(
"/admin/api-keys",
{ title: "store key", type: "publishable" },
adminHeaders
).then(r => r.data.api_key)
const storeHeaders = {
headers: { "x-publishable-api-key": publishableKey.token },
}
const resp = await request("GET", "/store/products", {
headers: storeHeaders,
})
expect(resp.status).toBe(200)
expect(resp.body.products).toBeInstanceOf(Array)
Validating Query Parameters and Pagination
Test filtering and pagination by passing a query object. The fixture automatically serializes these into query strings and validates req.filterableFields and req.queryConfig processing:
const resp = await request("GET", "/store/products", {
query: { limit: 10, offset: 0, q: "unique" },
headers: storeHeaders,
})
expect(resp.body.count).toBeLessThanOrEqual(10)
expect(resp.body.products[0].id).toBeDefined()
Testing Inventory and Field Selection
Verify that computed fields like inventory quantities are properly injected by specifying fields in the request:
const resp = await request("GET", "/store/products", {
query: { fields: ["variants.inventory_quantity"] },
headers: storeHeaders,
})
expect(resp.body.products[0].variants[0]).toHaveProperty("inventory_quantity")
Complete Test Suite Example
A typical integration test in integration-tests/http/__tests__/product/store/product.spec.ts follows this pattern:
describe("GET /store/products", () => {
let request: any
let storeHeaders: any
beforeAll(async () => {
;({ request } = await createServer(__dirname))
const publishableKey = await api.post(
"/admin/api-keys",
{ title: "test key", type: "publishable" },
adminHeaders
).then(r => r.data.api_key)
storeHeaders = {
headers: { "x-publishable-api-key": publishableKey.token }
}
})
it("returns a list of published products", async () => {
const resp = await request("GET", "/store/products", {
headers: storeHeaders
})
expect(resp.status).toBe(200)
expect(resp.body.products).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: expect.any(String) })
])
)
})
})
This approach exercises the complete request-response lifecycle, validating route registration in packages/medusa/src/api/store/products/route.ts, business logic execution, and authentication middleware.
Summary
- Use
createServerfrompackages/core/framework/src/http/__fixtures__/server/index.tsto spin up an in-memory Express server with full Medusa module loading. - Authenticate requests using
adminSessionfor JWT-backed admin access orx-publishable-api-keyheaders for store routes. - Test query handling by passing
queryobjects to validatefilterableFieldsand pagination logic. - Verify field selection to ensure computed properties like
inventory_quantityare properly serialized. - Reference existing patterns in
integration-tests/http/__tests__/product/store/product.spec.tsandpackages/core/framework/src/http/__tests__/bodyparser.spec.ts.
Frequently Asked Questions
What is the createServer fixture in Medusa?
The createServer fixture is a testing utility in packages/core/framework/src/http/__fixtures__/server/index.ts that creates an in-memory Express application, registers the Awilix dependency container, loads all API routes via the ApiLoader, and returns a supertest wrapper. It provides isolated request scopes and automatic header management for integration testing.
How do I authenticate requests in Medusa API tests?
For admin routes, pass an adminSession option with a userId to the request function, which automatically generates a JWT using your test configuration secrets. For store routes, first create a publishable API key via the admin API, then include it in the x-publishable-api-key header.
Can I test query parameters and filters in Medusa integration tests?
Yes. The request function accepts a query object that automatically serializes into URL parameters, allowing you to test req.filterableFields, req.queryConfig, pagination limits, and search queries against the full database layer.
Where are the Medusa API route tests located in the repository?
Integration tests for API routes are located in integration-tests/http/__tests__/, such as integration-tests/http/__tests__/product/store/product.spec.ts. Lower-level HTTP utility tests reside in packages/core/framework/src/http/__tests__/bodyparser.spec.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →