Open bradlis7 opened 10 months ago
I also experience this issue.
I've found a workaround for my use case: a small Nuxt app generated to a static SPA with no server-side component.
"@datadog/browser-rum": "^5.4.0",
"nuxt": "^3.9.0",
"ofetch": "^1.3.3",
plugins/01.datadogRum.ts
import { datadogRum } from '@datadog/browser-rum'
export default defineNuxtPlugin(nuxtApp => {
datadogRum.init({
clientToken: '<CLIENT_TOKEN>',
applicationId: '<APPLICATION_ID>',
site: 'datadoghq.com',
})
return {
provide: {
datadogRum,
},
}
})
Invoking datadogRum.init
eventually causes createFetchObservable
which causes instrumentMethod
.
[!IMPORTANT] To capture RUM resource data on fetch calls, the RUM
instrumentationWrapper
(packaged by theinstrumentMethod
function aswindow.fetch
) must be invoked.ofetch
invokes nativefetch
directly through its stored reference, bypassing Datadog'sinstrumentationWrapper
.I wonder if a Datadog Nuxt module could solve this issue as a way to run
datadogRum.init
before$fetch
gets created? 🤔
plugins/02.instrumentedFetch.ts
(the workaround)import { createFetch, type FetchRequest, type FetchOptions } from 'ofetch'
export default defineNuxtPlugin(nuxtApp => {
/**
* Create a new ofetch, passing in the version of fetch that is now the RUM instrumentationWrapper
* put in place by the invocation of datadogRum.init in the 01.datadogRum plugin.
*/
function instrumentedFetch<
T = unknown,
R extends FetchRequest = FetchRequest,
O extends FetchOptions = FetchOptions,
>(request: R, opts: O) {
return <Promise<T>>createFetch({ fetch })(request, opts) // Optionally configure fetch defaults here
}
return {
provide: {
instrumentedFetch,
},
}
})
You can now invoke useNuxtApp().$instrumentedFetch()
with mostly the same function signature as $fetch()
.
[!WARNING] As I mentioned, my application doesn't have a server component. I wonder if
useAsyncData
could be used with the instrumented fetch? I could be unaware of other server-side functionality this approach lacks from the built-in fetch composables.
That said, I don't think this is actually an issue with ofetch
.
The workaround above did break the functionality of the registerEndpoint
helper from @nuxt/test-utils v3.9.0
.
For some reason in my runtime, only globalThis.$fetch
gets replaced with the vitest-environment-nuxt
version assigned here (or maybe globalThis.fetch
gets overridden by something else after vitest-environment-nuxt
while globalThis.$fetch
doesn't?).
Whatever the cause, adding this line to a file included in our Vitest setupFiles
got our instrumentedFetch
helper to work with registerEndpoint
.
__tests__/setup.ts
import type { $Fetch } from 'ofetch'
/* Causes instrumentedFetch to use the fetch that works with registerEndpoint */
globalThis.fetch = (globalThis.$fetch as $Fetch).native
[!TIP]
This plugin that runs only in our test environment was a convenient way to mock the RUM SDK.
__tests__/__plugins__/mockDatadogRum.ts
```ts import { vi } from 'vitest' import { defineNuxtPlugin } from 'nuxt/app' export default defineNuxtPlugin(() => { vi.mock('@datadog/browser-rum', () => ({ datadogRum: { init: vi.fn(), addAction: vi.fn(), addError: vi.fn(), }, })) }) ```vitest.config.ts
```ts import { defineVitestConfig } from '@nuxt/test-utils/config' export default defineVitestConfig({ test: { environment: 'nuxt', environmentOptions: { nuxt: { overrides: { plugins: ['__tests__/__plugins__/mockDatadogRum.ts'], }, }, }, setupFiles: ['__tests__/setup.ts'], }, }) ```
Environment
Reproduction
Not sure if I can set up an example as this might be a paid product only.
Describe the bug
I have the integration set up as a Nuxt plugin, and datadog attempts to override window.fetch, but at that point, fetch has already been stored as the original function in ofetch, so none of the callbacks get intercepted with appropriate headers. I don't see a manual way to override fetch after the fact. Is there a way to override this fetch in Nuxt?
Additional context
No response
Logs
No response