Managing client IDs with server-side rendering
Managing client IDs with server-side rendering
This is for:
DeveloperIn server-side rendering (SSR), the initial request to Coveo APIs is made from the server rather than the client’s browser. This request requires a client ID to personalize responses based on the user’s behavior and previous interactions.
Without effective client ID management, you can’t accurately distinguish between new and returning visitors, leading to a lack of personalization and potential issues with session tracking.
There are two primary scenarios to consider when managing client IDs in an SSR context:
-
New visitors:
When a user visits the site for the first time, the server generates a unique client ID. The client ID is used to send a request to Coveo, and the response is returned to the browser. This generated client ID is stored in the browser as a cookie, enabling the browser to send it to Coveo for future client-side requests.
-
Returning visitors:
Users who have previously visited the site already have a client ID stored in their browser. When the server receives the request, it retrieves the client ID from the cookie and uses it to send a request to Coveo.
While the example in the following section uses Next.js, the same principles apply if you’re using a different language or framework in the back-end to perform SSR.
Note
If you’re using Coveo Relay to send client-side events and a client ID is stored in the browser, Relay will automatically use this stored client ID. |
Code example with Next.js route handlers
The following example showcases how to call the search endpoint of the Commerce API server-side and return a pre-rendered HTML file with a list of products. The sample uses Next.js’s route handler mechanism.
Note
The example given here shows how to make a request to the Commerce API, however, similar logic applies if you’re using the Search API. |
import { cookies } from "next/headers";
import { search } from "./search";
const COOKIE_NAME = "coveo_visitorId";
interface Product {
ec_name: string;
ec_description: string;
ec_price: number;
ec_thumbnails: string[];
}
const generateHtmlResponse = (products: Product[]): string => `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSR</title>
<script>
function deleteCookie() {
document.cookie = '${COOKIE_NAME}=; Max-Age=0; path=/;';
document.getElementById('cookieValue').innerText = 'No cookie set.';
alert('Cookie deleted.');
}
</script>
</head>
<body>
<h2>Product List</h2>
${
products.length > 0
? `
<ul>
${products
.map(
(product) => `
<li>
<h3>${product.ec_name}</h3>
<p>${product.ec_description}</p>
<p><strong>Price:</strong> ${product.ec_price}</p>
<img src="${product.ec_thumbnails[0]}" alt="${product.ec_name}" style="width: 150px; height: 150px; object-fit: cover;" />
</li>
`
)
.join("")}
</ul>
`
: `<p>No products found.</p>`
}
</body>
</html>
`;
const getOrCreateClientId = () => {
const cookieStore = cookies();
const existingClientId = cookieStore.get(COOKIE_NAME);
return existingClientId ? existingClientId.value : crypto.randomUUID();
};
export const GET = async (request: Request) => {
const clientId = getOrCreateClientId();
const query = "kayak";
const userAgent = request.headers.get("user-agent") || "";
const locationUrl = request.url;
const referrer = request.headers.get("referer") || "";
const searchResults = await search(
query,
clientId,
userAgent,
locationUrl,
referrer
);
const products: Product[] = searchResults.success
? searchResults.data.products || []
: [];
const htmlResponse = generateHtmlResponse(products);
return new Response(htmlResponse, {
status: 200,
headers: {
"Content-Type": "text/html",
"Set-Cookie": `${COOKIE_NAME}=${clientId}; Path=/;`,
},
});
};
Define the name of the cookie that stores the Coveo client ID. | |
Create a function that checks if there’s an existing client ID present in cookies() .
If a client ID is found, return it; otherwise, generate and return a new client ID. |
|
Get or create a client ID by using the getOrCreateClientId function defined earlier. |
|
Call the search helper function to make a request to the Commerce API.
Implementation details for this function are below. |
|
Retrieve a list of products returned from the search call. | |
Return the htmlResponse generated by the generateHtmlResponse helper function.
Set the client ID cookie by using the Set-Cookie header. |
The search
function that makes a request to the Commerce API is as follows:
export async function search(
query: string,
clientId: string,
userAgent: string,
locationUrl: string,
referrer: string
) {
const requestBody = {
trackingId: "<TRACKING_ID>",
clientId,
context: {
view: {
url: locationUrl,
},
capture: true,
cart: [],
user: {
userAgent,
referrer,
},
},
language: "en",
country: "US",
currency: "USD",
page: 0,
perPage: 5,
facets: [],
sort: { sortCriteria: "relevance" },
query,
};
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer "<TOKEN>"`,
"User-Agent": userAgent,
};
try {
const response = await fetch("<URL>", {
method: "POST",
headers,
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorMessage = await response.text();
return {
success: false,
status: response.status,
message: errorMessage || "Failed to fetch data from Coveo",
};
}
return {
success: true,
data: await response.json(),
};
} catch (error) {
return {
success: false,
status: 500,
message: "Failed to fetch data from Coveo",
};
}
}
Define the request body to send to the Commerce API.
Specify the tracking ID and other parameters, such as language and currency .
Make sure to forward context.view.url , userAgent , and referrer from the original request to Coveo. |
|
Define the headers to send with the request, including a token to authenticate the request. | |
Make a POST request to the Commerce API with the request body and headers. The URL of the request depends on your organization ID. For more details, see Organization endpoints. |