Skip to content

Grouping Requests

When building headless applications with Faust.js, you may want to group related GraphQL queries together for better debugging and monitoring. This is particularly useful when you need to trace requests that originate from a specific initial request, such as a server-side request that triggers subsequent client-side GraphQL queries.

When working with subsequent GraphQL queries in Faust.js (or any other framework), you may need to group queries together to help debug issues with your headless application. This is particularly useful for tracing related requests and understanding the flow of data through your application.

This example demonstrates how to capture the Cloudflare Ray ID from the initial response and pass it as a custom header for any subsequent GraphQL queries, enabling you to group these queries on the server side for better debugging and monitoring.

Note: This example shows how to pass the CF Ray ID from Cloudflare, but you can modify the code to capture different headers or adapt it to meet your specific requirements.

  1. Capture in middleware: Next.js middleware captures the CF Ray value from the incoming request and stores it in a cookie for client-side and server-side access
  2. Custom Faust plugin: A custom plugin retrieves the cookie value and adds it as a custom header to all GraphQL requests
  3. WordPress CORS setup: WordPress actions allow the custom header through CORS policies
middleware.js
import { NextResponse } from "next/server";
export function middleware(request) {
const response = NextResponse.next();
// Get CF Ray ID from the incoming request
const cfRayId = request.headers.get("cf-ray");
if (!cfRayId) {
return response;
}
// Set the cookie in the response so it can be accessed client-side and server-side
response.cookies.set("custom-initial-cf-ray-id", cfRayId, {
path: "/",
sameSite: "lax",
});
response.headers.set("X-CUSTOM-INITIAL-CF-RAY-ID", cfRayId);
response.headers.set(
"Access-Control-Expose-Headers",
"x-custom-request-id, cf-ray, x-custom-initial-cf-ray-id"
);
return response;
}
export const config = {
matcher: [
// Match all paths except static files and API routes that don't need tracking
"/((?!_next/static|_next/image|favicon.ico).*)",
],
};
faust.config.js
import { setConfig } from "@faustwp/core";
import templates from "./wp-templates";
import possibleTypes from "./possibleTypes.json";
import AddCustomHeaderPlugin from "./plugins/AddCustomHeaderPlugin";
const tracingPlugin = new AddCustomHeaderPlugin();
/**
* @type {import('@faustwp/core').FaustConfig}
**/
export default setConfig({
templates,
plugins: [tracingPlugin.getPlugin()],
experimentalPlugins: [],
possibleTypes,
});
plugins/AddCustomHeaderPlugin.js
import { setContext } from "@apollo/client/link/context";
class AddCustomHeaderPlugin {
constructor() {
this.pluginName = "add-custom-cf-ray-id-header-plugin";
}
/**
* Creates a plugin to add custom tracing headers to Apollo Client requests.
* @returns {Object} Plugin configuration object
*/
getPlugin() {
return {
apply: (hooks) => {
hooks.addFilter("apolloClientOptions", this.pluginName, (options) => {
const headerLink = setContext((_, { headers }) => {
let cfRayId = "";
const isServer = typeof window === "undefined";
const cfRayIdCookiePatterns = [
/(?:^|;\s*)custom-initial-cf-ray-id=([^;]*)/,
/(?:^|;\s*)cf-ray=([^;]*)/,
];
function extractCfRayId(cookieString) {
for (const pattern of cfRayIdCookiePatterns) {
const match = cookieString.match(pattern);
if (match) {
return decodeURIComponent(match[1]);
}
}
return "";
}
if (isServer) {
if (headers && headers.cookie) {
cfRayId = extractCfRayId(headers.cookie);
}
} else {
cfRayId = extractCfRayId(document.cookie);
}
if (!cfRayId || cfRayId === "") {
console.warn("No CF Ray ID found.");
return { headers };
}
return {
headers: {
...headers,
"CUSTOM-PARENT-CF-RAY-ID": cfRayId,
},
};
});
return {
...options,
link: headerLink.concat(options.link),
};
});
},
};
}
}
export default AddCustomHeaderPlugin;
functions.php
function add_cors_http_header() {
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, CUSTOM-PARENT-CF-RAY-ID");
header("Access-Control-Allow-Credentials: true");
}
add_action('init','add_cors_http_header');
function handle_preflight() {
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, CUSTOM-PARENT-CF-RAY-ID");
header("Access-Control-Max-Age: 86400");
exit(0);
}
}
add_action('init', 'handle_preflight');

Initial server request

Subsequent client request