@@ -31,6 +31,12 @@ type RequestOptions = {
3131} ;
3232
3333type Options = {
34+ /**
35+ * Enable use of automated persisted queries, this will always add a extra
36+ * roundtrip to the server if queries aren't cacheable
37+ * @default false
38+ */
39+ apq ?: boolean ;
3440 /**
3541 * Disables all forms of caching for the fetcher, use only in development
3642 *
@@ -81,6 +87,7 @@ export const initServerFetcher =
8187 defaultTimeout = undefined ,
8288 defaultHeaders = { } ,
8389 includeQuery = false ,
90+ apq = false ,
8491 createDocumentId = getDocumentId ,
8592 } : Options = { } ,
8693 ) =>
@@ -137,63 +144,36 @@ export const initServerFetcher =
137144 } ) ;
138145 }
139146
140- // Skip automatic persisted queries if operation is a mutation
141147 const queryType = getQueryType ( query ) ;
142- if ( queryType === "mutation" ) {
143- return tracer . startActiveSpan ( request . operationName , async ( span ) => {
144- try {
145- const response = await gqlPost (
146- url ,
147- request ,
148- { cache, next } ,
149- requestOptions ,
150- ) ;
151-
152- span . end ( ) ;
153- return response as GqlResponse < TResponse > ;
154- } catch ( err : unknown ) {
155- span . setStatus ( {
156- code : SpanStatusCode . ERROR ,
157- message : err instanceof Error ? err . message : String ( err ) ,
158- } ) ;
159- throw err ;
160- }
161- } ) ;
148+ if ( ! apq ) {
149+ return post < TResponse , TVariables > (
150+ request ,
151+ url ,
152+ cache ,
153+ next ,
154+ requestOptions ,
155+ ) ;
162156 }
163157
164- // Otherwise, try to get the cached query
165- return tracer . startActiveSpan ( request . operationName , async ( span ) => {
166- try {
167- let response = await gqlPersistedQuery (
168- url ,
169- request ,
170- { cache, next } ,
171- requestOptions ,
172- ) ;
173-
174- // If this is not a persisted query, but we tried to use automatic
175- // persisted queries (APQ) then we retry with a POST
176- if ( ! isPersistedQuery ( request ) && hasPersistedQueryError ( response ) ) {
177- // If the cached query doesn't exist, fall back to POST request and
178- // let the server cache it.
179- response = await gqlPost (
180- url ,
181- request ,
182- { cache, next } ,
183- requestOptions ,
184- ) ;
185- }
158+ // if apq is enabled, only queries are converted into get calls
159+ // https://www.apollographql.com/docs/apollo-server/performance/apq#using-get-requests-with-apq-on-a-cdn
160+ if ( queryType === "mutation" ) {
161+ return post < TResponse , TVariables > (
162+ request ,
163+ url ,
164+ cache ,
165+ next ,
166+ requestOptions ,
167+ ) ;
168+ }
186169
187- span . end ( ) ;
188- return response as GqlResponse < TResponse > ;
189- } catch ( err : any ) {
190- span . setStatus ( {
191- code : SpanStatusCode . ERROR ,
192- message : err ?. message ?? String ( err ) ,
193- } ) ;
194- throw err ;
195- }
196- } ) ;
170+ return get < TResponse , TVariables > (
171+ request ,
172+ url ,
173+ cache ,
174+ next ,
175+ requestOptions ,
176+ ) ;
197177 } ;
198178
199179const gqlPost = async < TVariables > (
@@ -204,7 +184,6 @@ const gqlPost = async <TVariables>(
204184) => {
205185 const endpoint = new URL ( url ) ;
206186 endpoint . searchParams . append ( "op" , request . operationName ) ;
207-
208187 const response = await fetch ( endpoint . toString ( ) , {
209188 headers : options . headers ,
210189 method : "POST" ,
@@ -253,3 +232,66 @@ const parseResponse = async (
253232
254233 return await response . json ( ) ;
255234} ;
235+ function get < TResponse , TVariables > (
236+ request : GraphQLRequest < TVariables > ,
237+ url : string ,
238+ cache : RequestCache | undefined ,
239+ next : NextFetchRequestConfig ,
240+ requestOptions : RequestOptions ,
241+ ) : GqlResponse < TResponse > | PromiseLike < GqlResponse < TResponse > > {
242+ return tracer . startActiveSpan ( request . operationName , async ( span ) => {
243+ try {
244+ let response = await gqlPersistedQuery (
245+ url ,
246+ request ,
247+ { cache, next } ,
248+ requestOptions ,
249+ ) ;
250+
251+ // If this is not a persisted query, but we tried to use automatic
252+ // persisted queries (APQ) then we retry with a POST
253+ if ( ! isPersistedQuery ( request ) && hasPersistedQueryError ( response ) ) {
254+ // If the cached query doesn't exist, fall back to POST request and
255+ // let the server cache it.
256+ response = await gqlPost ( url , request , { cache, next } , requestOptions ) ;
257+ }
258+
259+ span . end ( ) ;
260+ return response as GqlResponse < TResponse > ;
261+ } catch ( err : any ) {
262+ span . setStatus ( {
263+ code : SpanStatusCode . ERROR ,
264+ message : err ?. message ?? String ( err ) ,
265+ } ) ;
266+ throw err ;
267+ }
268+ } ) ;
269+ }
270+
271+ function post < TResponse , TVariables > (
272+ request : GraphQLRequest < TVariables > ,
273+ url : string ,
274+ cache : RequestCache | undefined ,
275+ next : NextFetchRequestConfig ,
276+ requestOptions : RequestOptions ,
277+ ) : GqlResponse < TResponse > | PromiseLike < GqlResponse < TResponse > > {
278+ return tracer . startActiveSpan ( request . operationName , async ( span ) => {
279+ try {
280+ const response = await gqlPost (
281+ url ,
282+ request ,
283+ { cache, next } ,
284+ requestOptions ,
285+ ) ;
286+
287+ span . end ( ) ;
288+ return response as GqlResponse < TResponse > ;
289+ } catch ( err : unknown ) {
290+ span . setStatus ( {
291+ code : SpanStatusCode . ERROR ,
292+ message : err instanceof Error ? err . message : String ( err ) ,
293+ } ) ;
294+ throw err ;
295+ }
296+ } ) ;
297+ }
0 commit comments