11import { GraphQLError , GraphQLErrorOptions } from 'graphql' ;
22import { ApolloServerErrorCode } from './errors/index.js' ;
3+ import { HeaderMap , newHTTPGraphQLHead } from './runHttpQuery.js' ;
34
45// These error classes are not part of Apollo Server's external API; the
56// ApolloServerErrorCode enum is (exported from `@apollo/server/errors`).
@@ -23,7 +24,7 @@ export class SyntaxError extends GraphQLErrorWithCode {
2324 super ( graphqlError . message , ApolloServerErrorCode . GRAPHQL_PARSE_FAILED , {
2425 source : graphqlError . source ,
2526 positions : graphqlError . positions ,
26- extensions : graphqlError . extensions ,
27+ extensions : { http : newHTTPGraphQLHead ( 400 ) , ... graphqlError . extensions } ,
2728 originalError : graphqlError ,
2829 } ) ;
2930 }
@@ -36,18 +37,34 @@ export class ValidationError extends GraphQLErrorWithCode {
3637 ApolloServerErrorCode . GRAPHQL_VALIDATION_FAILED ,
3738 {
3839 nodes : graphqlError . nodes ,
39- extensions : graphqlError . extensions ,
40+ extensions : {
41+ http : newHTTPGraphQLHead ( 400 ) ,
42+ ...graphqlError . extensions ,
43+ } ,
4044 originalError : graphqlError . originalError ?? graphqlError ,
4145 } ,
4246 ) ;
4347 }
4448}
4549
50+ // Persisted query errors (especially "not found") need to be uncached, because
51+ // hopefully we're about to fill in the APQ cache and the same request will
52+ // succeed next time. We also want a 200 response to avoid any error handling
53+ // that may mask the contents of an error response. (Otherwise, the default
54+ // status code for a response with `errors` but no `data` (even null) is 400.)
55+ const getPersistedQueryErrorHttp = ( ) => ( {
56+ status : 200 ,
57+ headers : new HeaderMap ( [
58+ [ 'cache-control' , 'private, no-cache, must-revalidate' ] ,
59+ ] ) ,
60+ } ) ;
61+
4662export class PersistedQueryNotFoundError extends GraphQLErrorWithCode {
4763 constructor ( ) {
4864 super (
4965 'PersistedQueryNotFound' ,
5066 ApolloServerErrorCode . PERSISTED_QUERY_NOT_FOUND ,
67+ { extensions : { http : getPersistedQueryErrorHttp ( ) } } ,
5168 ) ;
5269 }
5370}
@@ -57,6 +74,11 @@ export class PersistedQueryNotSupportedError extends GraphQLErrorWithCode {
5774 super (
5875 'PersistedQueryNotSupported' ,
5976 ApolloServerErrorCode . PERSISTED_QUERY_NOT_SUPPORTED ,
77+ // Not super clear why we need this to be uncached (makes sense for
78+ // PersistedQueryNotFoundError, because there we're about to fill the
79+ // cache and make the next copy of the same request succeed) but we've
80+ // been doing it for years so :shrug:
81+ { extensions : { http : getPersistedQueryErrorHttp ( ) } } ,
6082 ) ;
6183 }
6284}
@@ -79,14 +101,22 @@ export class OperationResolutionError extends GraphQLErrorWithCode {
79101 {
80102 nodes : graphqlError . nodes ,
81103 originalError : graphqlError . originalError ?? graphqlError ,
82- extensions : graphqlError . extensions ,
104+ extensions : {
105+ http : newHTTPGraphQLHead ( 400 ) ,
106+ ...graphqlError . extensions ,
107+ } ,
83108 } ,
84109 ) ;
85110 }
86111}
87112
88113export class BadRequestError extends GraphQLErrorWithCode {
89- constructor ( message : string ) {
90- super ( message , ApolloServerErrorCode . BAD_REQUEST ) ;
114+ constructor ( message : string , options ?: GraphQLErrorOptions ) {
115+ super ( message , ApolloServerErrorCode . BAD_REQUEST , {
116+ ...options ,
117+ // Default to 400 status code, but caller can override. (If caller just
118+ // wants to override headers... well, they can't, sorry.)
119+ extensions : { http : newHTTPGraphQLHead ( 400 ) , ...options ?. extensions } ,
120+ } ) ;
91121 }
92122}
0 commit comments