diff --git a/.changeset/proud-months-dance.md b/.changeset/proud-months-dance.md new file mode 100644 index 00000000000..131582cfb6b --- /dev/null +++ b/.changeset/proud-months-dance.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +More robust type definition for `headers` property passed to `createHttpLink` diff --git a/src/link/batch-http/__tests__/batchHttpLink.ts b/src/link/batch-http/__tests__/batchHttpLink.ts index e9b22ef431c..a939946c166 100644 --- a/src/link/batch-http/__tests__/batchHttpLink.ts +++ b/src/link/batch-http/__tests__/batchHttpLink.ts @@ -510,7 +510,7 @@ describe('SharedHttpTest', () => { execute(link, { query: sampleQuery, variables }).subscribe( makeCallback(resolve, reject, (result: any) => { - const headers: any = fetchMock.lastCall()![1]!.headers; + const headers: Record = fetchMock.lastCall()![1]!.headers as Record; expect(headers.authorization).toBe('1234'); expect(headers['content-type']).toBe('application/json'); expect(headers.accept).toBe('*/*'); @@ -527,7 +527,7 @@ describe('SharedHttpTest', () => { execute(link, { query: sampleQuery, variables }).subscribe( makeCallback(resolve, reject, (result: any) => { - const headers: any = fetchMock.lastCall()![1]!.headers; + const headers: Record = fetchMock.lastCall()![1]!.headers as Record; expect(headers.authorization).toBe('1234'); expect(headers['content-type']).toBe('application/json'); expect(headers.accept).toBe('*/*'); @@ -549,7 +549,7 @@ describe('SharedHttpTest', () => { execute(link, { query: sampleQuery, variables }).subscribe( makeCallback(resolve, reject, (result: any) => { - const headers: any = fetchMock.lastCall()![1]!.headers; + const headers: Record = fetchMock.lastCall()![1]!.headers as Record; expect(headers.authorization).toBe('1234'); expect(headers['content-type']).toBe('application/json'); expect(headers.accept).toBe('*/*'); @@ -570,7 +570,7 @@ describe('SharedHttpTest', () => { context, }).subscribe( makeCallback(resolve, reject, (result: any) => { - const headers: any = fetchMock.lastCall()![1]!.headers; + const headers: Record = fetchMock.lastCall()![1]!.headers as Record; expect(headers.authorization).toBe('1234'); expect(headers['content-type']).toBe('application/json'); expect(headers.accept).toBe('*/*'); diff --git a/src/link/http/createHttpLink.ts b/src/link/http/createHttpLink.ts index b97d9faf6f8..4022d2ef661 100644 --- a/src/link/http/createHttpLink.ts +++ b/src/link/http/createHttpLink.ts @@ -138,6 +138,7 @@ export const createHttpLink = (linkOptions: HttpOptions = {}) => { // does not match custom directives beginning with @defer if (hasDirectives(['defer'], operation.query)) { + options.headers = options.headers || {}; options.headers.accept = "multipart/mixed; deferSpec=20220824, application/json"; } diff --git a/src/link/http/selectHttpOptionsAndBody.ts b/src/link/http/selectHttpOptionsAndBody.ts index 57b23bf0375..76601a9eb7c 100644 --- a/src/link/http/selectHttpOptionsAndBody.ts +++ b/src/link/http/selectHttpOptionsAndBody.ts @@ -40,11 +40,11 @@ export interface HttpOptions { /** * An object representing values to be sent as headers on the request. */ - headers?: any; + headers?: Record; /** - * If set to true, header names won't be automatically normalized to - * lowercase. This allows for non-http-spec-compliant servers that might + * If set to true, header names won't be automatically normalized to + * lowercase. This allows for non-http-spec-compliant servers that might * expect capitalized header names. */ preserveHeaderCase?: boolean; @@ -92,7 +92,7 @@ export interface HttpQueryOptions { export interface HttpConfig { http?: HttpQueryOptions; options?: any; - headers?: any; + headers?: Record; credentials?: any; } @@ -173,7 +173,9 @@ export function selectHttpOptionsAndBodyInternal( }; }); - options.headers = removeDuplicateHeaders(options.headers, http.preserveHeaderCase); + if (options.headers) { + options.headers = removeDuplicateHeaders(options.headers, http.preserveHeaderCase); + } //The body depends on the http options const { operationName, extensions, variables, query } = operation; @@ -191,7 +193,7 @@ export function selectHttpOptionsAndBodyInternal( }; // Remove potential duplicate header names, preserving last (by insertion order). -// This is done to prevent unintentionally duplicating a header instead of +// This is done to prevent unintentionally duplicating a header instead of // overwriting it (See #8447 and #8449). function removeDuplicateHeaders( headers: Record, @@ -204,12 +206,12 @@ function removeDuplicateHeaders( Object.keys(Object(headers)).forEach(name => { normalizedHeaders[name.toLowerCase()] = headers[name]; }); - return normalizedHeaders; + return normalizedHeaders; } // If we are preserving the case, remove duplicates w/ normalization, // preserving the original name. - // This allows for non-http-spec-compliant servers that expect intentionally + // This allows for non-http-spec-compliant servers that expect intentionally // capitalized header names (See #6741). const headerData = Object.create(null); Object.keys(Object(headers)).forEach(name => {