diff --git a/.changeset/small-ears-heal.md b/.changeset/small-ears-heal.md new file mode 100644 index 000000000000..6eb34b8a3cc1 --- /dev/null +++ b/.changeset/small-ears-heal.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: add `event.isSubRequest` boolean indicating whether this is a call to one of the app's own APIs during SSR (or prerendering) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 94cb17e345c8..3f5b535fb23d 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1017,6 +1017,10 @@ export interface RequestEvent< * related to the data request in this case. Use this property instead if the distinction is important to you. */ isDataRequest: boolean; + /** + * `true` for `+server.js` calls coming from SvelteKit without the overhead of actually making an HTTP request. This happens when you make same-origin `fetch` requests on the server. + */ + isSubRequest: boolean; } /** diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index 1cd9d058ad6b..47b8d5c56b26 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -169,7 +169,8 @@ export async function respond(request, options, manifest, state) { } }, url, - isDataRequest: is_data_request + isDataRequest: is_data_request, + isSubRequest: state.depth > 0 }; /** @type {import('types').RequiredResolveOptions} */ diff --git a/packages/kit/test/apps/basics/src/hooks.server.js b/packages/kit/test/apps/basics/src/hooks.server.js index 41c77c6f000e..c684719ed476 100644 --- a/packages/kit/test/apps/basics/src/hooks.server.js +++ b/packages/kit/test/apps/basics/src/hooks.server.js @@ -56,6 +56,15 @@ export const handle = sequence( } return resolve(event); }, + ({ event, resolve }) => { + if ( + event.request.headers.has('host') && + !event.request.headers.has('user-agent') !== event.isSubRequest + ) { + throw new Error('SSR API sub-requests should have isSubRequest set to true'); + } + return resolve(event); + }, ({ event, resolve }) => { if (event.url.pathname.includes('fetch-credentialed')) { // Only get the cookie at the test where we know it's set to avoid polluting our logs with (correct) warnings