graphql-yoga
Version:
60 lines (59 loc) • 2.91 kB
JavaScript
import { getRequestId } from '../utils/request-id.js';
export const defaultGenerateRequestId = () => globalThis.crypto.randomUUID();
export const defaultRequestIdHeader = 'x-request-id';
export function getRequestIdHeaderName(opts) {
return opts?.headerName || defaultRequestIdHeader;
}
/**
* Reads a request id the host integration already assigned to the native request object it
* passes through the server context (e.g. Fastify's `request.id`, or an Express/Koa
* request-id middleware that sets `req.id`), before Yoga ever saw the request.
*
* This lets Yoga's own id generation defer to a host's existing correlation id instead of
* minting an unrelated one of its own, without the host needing to disable `useRequestId` and
* reimplement the logger scoping it provides.
*/
function getHostRequestId(serverContext) {
const req = serverContext?.req;
return typeof req?.id === 'string' && req.id.length > 0 ? req.id : undefined;
}
/**
* Correlates everything happening while handling a request under a single request id.
*
* A request id the host integration already set on the native request object (see
* {@link getHostRequestId}) is preferred first, so Yoga's logs always agree with the host's own;
* otherwise, if `trustHeader` is enabled (the default), the `x-request-id` header of the
* incoming request is used if present and usable (see {@link getRequestId}); otherwise, one is
* generated. It is then added to the server context's `log`ger as the `requestId` attribute, and
* set on the outgoing response's headers.
*/
export function useRequestId(opts) {
const requestIdByRequest = new WeakMap();
const headerName = getRequestIdHeaderName(opts);
const trustHeader = opts?.trustHeader ?? true;
const generateRequestId = opts?.generateRequestId || defaultGenerateRequestId;
return {
onRequest({ request, fetchAPI, serverContext }) {
const generate = () => generateRequestId({
request,
fetchAPI,
context: serverContext,
});
let requestId = getHostRequestId(serverContext);
requestId ??= trustHeader
? getRequestId(request.headers.get(headerName), generate)
: generate();
requestIdByRequest.set(request, requestId);
// `useConfigInServerContext` runs before this plugin and puts the logger in the server
// context. It can still be missing when this plugin is used on its own, in which case the
// request id is only propagated through the headers.
serverContext.log &&= serverContext.log.child({ requestId });
},
onResponse({ request, response }) {
const requestId = requestIdByRequest.get(request);
if (requestId) {
response.headers.set(headerName, requestId);
}
},
};
}