@graphql-yoga/plugin-apq
Version:
APQ plugin for GraphQL Yoga.
56 lines (54 loc) • 2.22 kB
JavaScript
import { createGraphQLError, createLRUCache } from "graphql-yoga";
import { handleMaybePromise } from "@whatwg-node/promise-helpers";
//#region src/index.ts
function hashSHA256(text, api = globalThis) {
const inputUint8Array = new api.TextEncoder().encode(text);
return handleMaybePromise(() => api.crypto.subtle.digest({ name: "SHA-256" }, inputUint8Array), (arrayBuf) => {
const outputUint8Array = new Uint8Array(arrayBuf);
let hash = "";
for (const byte of outputUint8Array) {
const hex = byte.toString(16);
hash += "00".slice(0, Math.max(0, 2 - hex.length)) + hex;
}
return hash;
});
}
function createInMemoryAPQStore(options = {}) {
return createLRUCache({
max: options.max ?? 1e3,
ttl: options.ttl ?? 36e3
});
}
function isAPQExtension(input) {
return input != null && typeof input === "object" && "version" in input && input?.version === 1 && "sha256Hash" in input && typeof input?.sha256Hash === "string";
}
function decodeAPQExtension(input) {
if (isAPQExtension(input)) return input;
return null;
}
function useAPQ(options = {}) {
const { store = createInMemoryAPQStore(), hash = hashSHA256, responseConfig = {} } = options;
return { onParams({ params, setParams, fetchAPI }) {
const persistedQueryData = decodeAPQExtension(params.extensions?.["persistedQuery"]);
if (persistedQueryData === null) return;
if (params.query == null) return handleMaybePromise(() => store.get(persistedQueryData.sha256Hash), (persistedQuery) => {
if (persistedQuery == null) throw createGraphQLError("PersistedQueryNotFound", { extensions: {
http: { status: responseConfig.forceStatusCodeOk ? 200 : 404 },
code: "PERSISTED_QUERY_NOT_FOUND"
} });
setParams({
...params,
query: persistedQuery
});
});
return handleMaybePromise(() => hash(params.query, fetchAPI), (expectedHash) => {
if (persistedQueryData.sha256Hash !== expectedHash) throw createGraphQLError("PersistedQueryMismatch", { extensions: {
http: { status: responseConfig.forceStatusCodeOk ? 200 : 400 },
code: "PERSISTED_QUERY_MISMATCH"
} });
return store.set(persistedQueryData.sha256Hash, params.query);
});
} };
}
//#endregion
export { createInMemoryAPQStore, hashSHA256, useAPQ };