Storefront routes
Route handlers in the Next.js app.
These are Next.js route handlers in apps/storefront, not Medusa endpoints.
GET /api/search/suggest
Type-ahead suggestions for the header search box.
Query parameters
| Name | Required | Default | Notes |
|---|---|---|---|
q | yes | — | Returns empty below 3 characters |
countryCode | no | my | Region for pricing |
Response
{
"q": "vios",
"count": 42,
"products": [
{
"id": "prod_...",
"title": "...",
"handle": "...",
"thumbnail": "https://...",
"vendor": "...",
"price": 12900,
"currency": "myr"
}
]
}Capped at 6 results. price is in the currency's minor unit (sen).
This goes through the server rather than the browser calling Medusa directly.
STORE_CORS did not list the live storefront domain, so a browser fetch died
at preflight with a bare "Failed to fetch" — which renders as "no results"
rather than as an error. Routing it server-side removes the class of bug.
Brackets are stripped from the query (q.replace(/[()\[\]]/g, " ")) using the
same normalisation the results page applies, so the dropdown never promises
results the results page will not deliver.
Why not Algolia
At ~4,445 products, Medusa's substring match answers in tens of milliseconds. An index would add an account, a sync to keep correct, and a bill for something Postgres already does. Algolia earns its place at a larger scale, or when typo tolerance and relevance ranking start mattering more than exact part numbers.
POST|GET /[countryCode]/checkout/curlec-return
Where Curlec sends the customer back after payment.
Accepts both methods: FPX returns via POST with form-encoded fields, cards
via GET with query parameters. Both are read, and the handler answers with a
303 so the browser issues a GET for the destination.
The receipt is verified by POST /store/curlec/return to recover the original
cart, then core cart completion returns the order. No cart cookie is required
on the bank's cross-site request and no replacement payment session is created.
Aftercare store endpoints
POST /store/orders/:id/fitment:{ "item_id": "...", "fits": true }.fitsmust be a boolean. Returns{ "ok": true, "fits": true }, withalready: truefor an answer already mirrored on the order. One vote per item.POST /store/orders/:id/return-request:{ "reason": "..." }, trimmed, 1–2000 characters. Returns{ "received": true }after persistence and notification processing succeed. An identical reason on an order reuses the request; retries use stable notification keys. On failure the saved request remains in the admin for staff follow-up.
These guest-capable endpoints retain the existing order-id-as-capability access model and require a publishable API key. Keep order confirmation links private.
GET /[countryCode]/auth/callback/[provider]
OAuth return for social sign-in. provider is checked against an allow-list of
google, apple, facebook; anything else is rejected rather than passed
through to Medusa.
Inactive until provider credentials are configured.