diff --git a/packages/gatsby/cache-dir/page-ssr/index.d.ts b/packages/gatsby/cache-dir/page-ssr/index.d.ts index cf2a907e0b23b..2cedbe7685584 100644 --- a/packages/gatsby/cache-dir/page-ssr/index.d.ts +++ b/packages/gatsby/cache-dir/page-ssr/index.d.ts @@ -27,7 +27,12 @@ interface IQueryResult { // https://codemix.com/opaque-types-in-javascript/ type Opaque = T & { __TYPE__: K } // redacted details as this is meant to be opaque internal type that shouldn't be relied on by integrators (can change any time) -type ISSRData = Opaque<"ISSRData", {}> +type ISSRData = Opaque< + "ISSRData", + { + serverDataHeaders?: Record + } +> type PageContext = Record @@ -52,6 +57,12 @@ export function getData(args: { runQuery(query: string, context: Record): Promise findPageByPath(pathName: string): IGatsbyPage | undefined } + req: Partial<{ + query: Record + method: string + url: string + headers: Record + }> }): Promise export function renderPageData(args: { diff --git a/packages/gatsby/src/commands/serve.ts b/packages/gatsby/src/commands/serve.ts index e523a2b626831..26a124b1153f7 100644 --- a/packages/gatsby/src/commands/serve.ts +++ b/packages/gatsby/src/commands/serve.ts @@ -232,12 +232,12 @@ module.exports = async (program: IServeProgram): Promise => { program.directory, `.cache`, `query-engine` - )) + )) as typeof import("../schema/graphql-engine/entry") const { getData, renderPageData, renderHTML } = require(path.join( program.directory, `.cache`, `page-ssr` - )) + )) as typeof import("../utils/page-ssr-module/entry") const graphqlEngine = new GraphQLEngine({ dbPath: path.join(program.directory, `.cache`, `data`, `datastore`), }) @@ -260,6 +260,13 @@ module.exports = async (program: IServeProgram): Promise => { req, }) const results = await renderPageData({ data }) + if (page.mode === `SSR` && data.serverDataHeaders) { + for (const [name, value] of Object.entries( + data.serverDataHeaders + )) { + res.setHeader(name, value) + } + } return void res.send(results) } @@ -279,6 +286,13 @@ module.exports = async (program: IServeProgram): Promise => { req, }) const results = await renderHTML({ data }) + if (page.mode === `SSR` && data.serverDataHeaders) { + for (const [name, value] of Object.entries( + data.serverDataHeaders + )) { + res.setHeader(name, value) + } + } return res.send(results) } diff --git a/packages/gatsby/src/utils/page-ssr-module/entry.ts b/packages/gatsby/src/utils/page-ssr-module/entry.ts index 3ef48180dbd48..09ad424243b36 100644 --- a/packages/gatsby/src/utils/page-ssr-module/entry.ts +++ b/packages/gatsby/src/utils/page-ssr-module/entry.ts @@ -16,7 +16,7 @@ import { } from "../page-data-helpers" // @ts-ignore render-page import will become valid later on (it's marked as external) import htmlComponentRenderer from "./render-page" -import { getServerData } from "../get-server-data" +import { getServerData, IServerData } from "../get-server-data" export interface ITemplateDetails { query: string @@ -28,6 +28,7 @@ export interface ISSRData { page: IGatsbyPage templateDetails: ITemplateDetails potentialPagePath: string + serverDataHeaders?: Record } const pageTemplateDetailsMap: Record< @@ -71,7 +72,7 @@ export async function getData({ // 3. Execute query // query-runner handles case when query is not there - so maybe we should consider using that somehow let results: IExecutionResult = {} - let serverData: any = undefined + let serverData: IServerData | undefined if (templateDetails.query) { executionPromises.push( graphqlEngine @@ -104,7 +105,13 @@ export async function getData({ } results.pageContext = page.context - return { results, page, templateDetails, potentialPagePath } + return { + results, + page, + templateDetails, + potentialPagePath, + serverDataHeaders: serverData?.headers, + } } export async function renderPageData({