Files
shop/server/utils/cmsApi.ts
T
libretech 5c773a99e6 feat(orders): pass the CMS's status and safe error fields to the browser
The order routes (get, put, add-product, remove-product, checkout, capture)
call forwardToCms, which throws a CMS error on as
createError({ statusCode, statusMessage, data: { message, errors?, missing? } }),
built by the pure shopErrorFromCms (server/utils/cmsError.ts).

Before, the FetchError was thrown on as it was: the browser got the CMS's
status, but Nitro treated it as unhandled, answered "Server Error" without
data and logged every CMS 4xx as [unhandled]. Now the browser also gets the
CMS's message, the errors of a rejected update and the fields a checkout
misses, and no other field. A status that is the shop's own fault (401, 403,
...) is answered 500, a CMS that does not answer 503; 5xx are logged without
the query and the order uuid.

npm test runs tests/unit with Node's type stripping and no dependencies, as
in libreshop/cms. nuxt.config keeps tests/ out of the app's type check.

Refs libretech/mp#71
2026-10-09 02:32:14 +02:00

35 lines
1.2 KiB
TypeScript

import { cmsErrorLogLine, shopErrorFromCms } from "./cmsError";
/**
* Server-side CMS API utility.
* Use this for direct CMS access during SSR.
*/
export async function fetchCms<T>(endpoint: string, options: Parameters<typeof $fetch>[1] = {}): Promise<T> {
const config = useRuntimeConfig();
const url = `${config.cmsInternalUrl}/api${endpoint}`;
return await $fetch<T>(url, {
...options,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${config.shopApiToken}`,
...((options.headers as Record<string, string>) || {})
}
});
}
/**
* fetchCms for a route that forwards the browser's request to the CMS: a CMS error answers the browser with the CMS's status
* and the safe fields of its error (cmsError.ts), instead of an unhandled error, whose message and data Nitro hides.
*/
export async function forwardToCms<T>(endpoint: string, options: Parameters<typeof $fetch>[1] = {}): Promise<T> {
try {
return await fetchCms<T>(endpoint, options);
} catch (error) {
const shopError = shopErrorFromCms(error);
if (!shopError) throw error;
if (shopError.statusCode >= 500) console.error(cmsErrorLogLine(String(options.method ?? "GET"), endpoint, shopError));
throw createError(shopError);
}
}