const staticState = await engineDefinition.fetchStaticState();
and you would hydrate it as follows:
const hydratedState = await engineDefinition.hydrateStaticState({
searchAction: staticState.searchAction,
});
However, you may sometimes need to access the engine and controllers before the initial search is executed during fetchStaticState, or before the initial search gets simulated during hydrateStaticState.
Coveo provides three utilities for these situations:
buildfetchStaticState.fromBuildResulthydrateStaticState.fromBuildResultYou can build an engine and its controllers directly from an engine definition by calling your engine definition’s build as follows:
const { engine, controllers } = await engineDefinition.build();
If you need to initialize the engine with a slightly different configuration than the one inherited from your engine definition, you may extend it as follows:
const { engine, controllers } = await engineDefinition.build({
extend(options) {
return {
...options,
logFormatter() {
// ...
},
};
},
});
Internally, the fetchStaticState method does five things:
searchAction.
This is an action which, when dispatched, is equivalent to re-executing the search and getting the same response.If you want to access the engine and controllers, you can use build, which effectively replaces steps 1 and 2.
You can then use fetchStaticState.fromBuildResult, which effectively replaces steps 3 to 5.
const { engine, controllers } = await engineDefinition.build();
const staticState = await engineDefinition.fetchStaticState.fromBuildResult({
buildResult: { engine, controllers },
});
Internally, the hydrateStaticState method does four things:
searchAction given to it.If you want to access the engine and controllers, you can use build, which effectively replaces steps 1 and 2.
You can then use hydrateStaticState.fromBuildResult, which effectively replaces steps 3 and 4.
const { engine, controllers } = await engineDefinition.build();
const staticState = await engineDefinition.hydrateStaticState.fromBuildResult({
buildResult: { engine, controllers },
searchAction,
});
If you choose to manipulate the engine or controllers before passing them to fromBuildResult, this could effect the state that’s returned by whichever fromBuildResult was called.
For this reason, we recommend that you extract any manipulations you do to your engine into a separate function.
You can then use it for both fetchStaticState.fromBuildResult and hydrateStaticState.fromBuildResult, as in the following code samples.
In common/engine-definition.ts:
import {
defineSearchEngine,
defineSearchBox,
defineResultList,
defineFacet,
getSampleSearchEngineConfiguration,
loadQueryActions,
SearchCompletedAction,
} from '@coveo/headless-react/ssr';
const engineDefinition = defineSearchEngine({ ①
configuration: getSampleSearchEngineConfiguration(),
controllers: {
searchBox: defineSearchBox(),
resultList: defineResultList(),
authorFacet: defineFacet({ field: "author" }),
sourceFacet: defineFacet({ field: "source" }),
},
});
async function getBuildResult() { ②
const buildResult = await engineDefinition.build();
const { updateQuery } = loadQueryActions(buildResult.engine);
buildResult.engine.dispatch(updateQuery({ q: "I like trains" }));
return buildResult;
}
export async function fetchStaticState() { ③
return engineDefinition.fetchStaticState.fromBuildResult({
buildResult: await getBuildResult(),
});
}
export async function hydrateStaticState(options: { ④
searchAction: SearchCompletedAction;
}) {
return engineDefinition.hydrateStaticState.fromBuildResult({
buildResult: await getBuildResult(),
searchAction: options.searchAction,
});
}
engineDefinition.fetchStaticState.hydrateStaticState.In server.ts:
import { fetchStaticState } from './common/engine-definition.ts';
// ...
const staticState = await fetchStaticState(); ①
// ...
In client.ts:
import { hydrateStaticState } from './common/engine-definition.ts';
// ...
const hydratedState = await hydrateStaticState({
searchAction: staticState.searchAction,
});
In this anti-pattern, the engine definition is exported directly and manipulated differently on the server and client.
The server dispatches updateQuery before calling fetchStaticState.fromBuildResult, but the client does not perform the same manipulation before calling hydrateStaticState.fromBuildResult.
This causes a mismatch between the server-rendered state and the client-hydrated state, which can lead to hydration errors, incorrect UI, or unexpected search behavior.
Always extract shared engine manipulations into a common function (as shown above) so that both the server and client start from the same state.
Avoid doing something like the following code samples.
In common/engine-definition.ts:
import {
defineSearchEngine,
defineSearchBox,
defineResultList,
defineFacet,
getSampleSearchEngineConfiguration,
} from '@coveo/headless-react/ssr';
export const engineDefinition = defineSearchEngine({
configuration: {
...getSampleSearchEngineConfiguration(),
analytics: { enabled: false },
},
controllers: {
searchBox: defineSearchBox(),
resultList: defineResultList(),
authorFacet: defineFacet({ field: "author" }),
sourceFacet: defineFacet({ field: "source" }),
},
});
In server.ts:
import { engineDefinition } from './common/engine-definition.ts';
import { loadQueryActions } from '@coveo/headless-react/ssr';
// ...
const buildResult = await engineDefinition.build();
const { updateQuery } = loadQueryActions(buildResult.engine);
buildResult.engine.dispatch(updateQuery({ q: "I like trains" }));
const staticState = await engineDefinition.fetchStaticState.fromBuildResult({
buildResult,
});
// ...
In client.ts:
import { engineDefinition } from './common/engine-definition.ts';
const buildResult = await engineDefinition.build();
const hydratedState = await engineDefinition.hydrateStaticState.fromBuildResult(
{
buildResult,
searchAction: staticState.searchAction,
}
);