From 4de11adf1b7d6b4f30aa353b027b53c8a5b057a6 Mon Sep 17 00:00:00 2001 From: Matthieu Billard Date: Tue, 15 Apr 2025 22:55:18 -0700 Subject: [PATCH] feat: add Automatic Persisted Queries support --- src/runtime/plugin.ts | 35 +++++++++++++++++++++++++++++++++-- src/types.d.ts | 8 +++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/runtime/plugin.ts b/src/runtime/plugin.ts index 9263dbc1..a5f690e1 100644 --- a/src/runtime/plugin.ts +++ b/src/runtime/plugin.ts @@ -1,5 +1,6 @@ import { destr } from 'destr' import { onError } from '@apollo/client/link/error' +import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries' import { getMainDefinition } from '@apollo/client/utilities' import { createApolloProvider } from '@vue/apollo-option' import { ApolloClients, provideApolloClients } from '@vue/apollo-composable' @@ -69,6 +70,36 @@ export default defineNuxtPlugin((nuxtApp) => { headers: { ...(clientConfig?.httpLinkOptions?.headers || {}) } })) + let httpLinkChain = httpLink + + if (clientConfig.persisting) { + const sha256 = async (query: string) => { + if (import.meta.server) { + // Server-side hashing using Node.js crypto + const crypto = await import('node:crypto') + return crypto.createHash('sha256').update(query).digest('hex') + } else if (import.meta.client) { + // Client-side hashing using browser's SubtleCrypto API + const encoder = new TextEncoder() + const data = encoder.encode(query) + const hashBuffer = await crypto.subtle.digest('SHA-256', data) + return Array.from(new Uint8Array(hashBuffer)) + .map(byte => byte.toString(16).padStart(2, '0')) + .join('') + } + throw new Error('Unsupported environment') + } + + const apqOptions = { + ...(typeof clientConfig.persisting === 'object' ? clientConfig.persisting : {}), + sha256 + } + + const persistedQueriesLink = createPersistedQueryLink(apqOptions) + + httpLinkChain = persistedQueriesLink.concat(httpLink) + } + let wsLink: GraphQLWsLink | null = null if (process.client && clientConfig.wsEndpoint) { @@ -99,7 +130,7 @@ export default defineNuxtPlugin((nuxtApp) => { const link = ApolloLink.from([ errorLink, ...(!wsLink - ? [httpLink] + ? [httpLinkChain] : [ ...(clientConfig?.websocketsOnly ? [wsLink] @@ -109,7 +140,7 @@ export default defineNuxtPlugin((nuxtApp) => { return (definition.kind === 'OperationDefinition' && definition.operation === 'subscription') }, wsLink, - httpLink) + httpLinkChain) ]) ]) ]) diff --git a/src/types.d.ts b/src/types.d.ts index f9d41f72..153a763c 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,5 +1,5 @@ import type { ClientOptions } from 'graphql-ws' -import type { HttpOptions, DefaultOptions, InMemoryCacheConfig } from '@apollo/client' +import type { HttpOptions, DefaultOptions, InMemoryCacheConfig, PersistedQueryLink } from '@apollo/client' import type { CookieOptions } from 'nuxt/app' export type { ErrorResponse } from '@apollo/client/link/error' @@ -38,8 +38,10 @@ export type ClientConfig = { **/ wsEndpoint?: string; - // Enable Automatic Query persisting with Apollo Engine - // persisting?: boolean + /** + * Configure Automatic Persited Queries + **/ + persisting?: PersistedQueryLink.Options | null; /** * Specify if the client should solely use WebSocket.