Skip to content

Commit

Permalink
chore(lib/fetch): dynamic import @supabase/node-fetch (supabase#1303)
Browse files Browse the repository at this point in the history
  • Loading branch information
lyc committed Nov 9, 2024
1 parent 4c7f571 commit 4bf9624
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 26 deletions.
29 changes: 4 additions & 25 deletions src/lib/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,17 @@
// @ts-ignore
import nodeFetch, { Headers as NodeFetchHeaders } from '@supabase/node-fetch'

type Fetch = typeof fetch

export const resolveFetch = (customFetch?: Fetch): Fetch => {
let _fetch: Fetch
if (customFetch) {
_fetch = customFetch
} else if (typeof fetch === 'undefined') {
_fetch = nodeFetch as unknown as Fetch
} else {
_fetch = fetch
}
return (...args: Parameters<Fetch>) => _fetch(...args)
}

export const resolveHeadersConstructor = () => {
if (typeof Headers === 'undefined') {
return NodeFetchHeaders
}

return Headers
}
import { resolveFetch, resolveHeadersConstructor } from './helpers'
import { Fetch } from './types'

export const fetchWithAuth = (
supabaseKey: string,
getAccessToken: () => Promise<string | null>,
customFetch?: Fetch
): Fetch => {
const fetch = resolveFetch(customFetch)
const HeadersConstructor = resolveHeadersConstructor()

return async (input, init) => {
const accessToken = (await getAccessToken()) ?? supabaseKey
const HeadersConstructor = await resolveHeadersConstructor()

let headers = new HeadersConstructor(init?.headers)

if (!headers.has('apikey')) {
Expand Down
25 changes: 24 additions & 1 deletion src/lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// helpers.ts
import { SupabaseClientOptions } from './types'
import { Fetch, SupabaseClientOptions } from './types'

export function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
Expand Down Expand Up @@ -66,3 +66,26 @@ export function applySettingDefaults<

return result
}

export const resolveFetch = (customFetch?: Fetch): Fetch => {
let _fetch: Fetch
if (customFetch) {
_fetch = customFetch
} else if (typeof fetch === 'undefined') {
_fetch = (...args) =>
import('@supabase/node-fetch' as any).then(({ default: fetch }) => fetch(...args))
} else {
_fetch = fetch
}
return (...args: Parameters<Fetch>) => _fetch(...args)
}

export const resolveHeadersConstructor = async () => {
if (typeof Headers === 'undefined') {
return import('@supabase/node-fetch' as any).then(
({ Headers: NodeFetchHeaders }) => NodeFetchHeaders as typeof Headers
)
}

return Headers
}
72 changes: 72 additions & 0 deletions test/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,75 @@ test('override setting defaults', async () => {
// Existing property values should remain constant
expect(settings.db.schema).toBe(defaults.db.schema)
})

describe('resolveFetch', () => {
const TEST_URL = 'https://example.com'
const TEST_OPTIONS = { method: 'GET' }

beforeEach(() => {
// Reset any mocks between tests
jest.resetModules()
jest.clearAllMocks()
})

test('should use custom fetch if provided', async () => {
const customFetch = jest.fn()
const resolvedFetch = helpers.resolveFetch(customFetch)

await resolvedFetch(TEST_URL, TEST_OPTIONS)

expect(customFetch).toHaveBeenCalledTimes(1)
expect(customFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
})

test('should use global fetch if no custom fetch is provided', async () => {
const globalFetch = jest.fn()
global.fetch = globalFetch
const resolvedFetch = helpers.resolveFetch()

await resolvedFetch(TEST_URL, TEST_OPTIONS)

expect(globalFetch).toHaveBeenCalledTimes(1)
expect(globalFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
})

test('should use node-fetch if global fetch is not available', async () => {
const nodeFetch = jest.fn()
jest.mock('@supabase/node-fetch', () => nodeFetch)

global.fetch = undefined as any
const resolvedFetch = helpers.resolveFetch()

await resolvedFetch(TEST_URL, TEST_OPTIONS)

expect(nodeFetch).toHaveBeenCalledTimes(1)
expect(nodeFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
})
})

describe('resolveHeadersConstructor', () => {
beforeEach(() => {
// Reset any mocks between tests
jest.resetModules()
jest.clearAllMocks()
})

test('should use Headers if available', async () => {
const resolvedHeadersConstructor = await helpers.resolveHeadersConstructor()
expect(resolvedHeadersConstructor).toBe(Headers)
})

test('should use node-fetch Headers if global Headers is not available', async () => {
const MockHeaders = jest.fn()
jest.mock('@supabase/node-fetch', () => ({
Headers: MockHeaders,
}))

// Cannot assign read-only property, delete is available
// @ts-ignore
delete global.Headers

const resolvedHeadersConstructor = await helpers.resolveHeadersConstructor()
expect(resolvedHeadersConstructor).toBe(MockHeaders)
})
})

0 comments on commit 4bf9624

Please sign in to comment.