Skip to content

feat: add fetch override#245

Merged
lukeocodes merged 6 commits into
deepgram:release/3.x.x-20240425from
BartoszJarocki:feat/add-fetch-override
Apr 27, 2024
Merged

feat: add fetch override#245
lukeocodes merged 6 commits into
deepgram:release/3.x.x-20240425from
BartoszJarocki:feat/add-fetch-override

Conversation

@BartoszJarocki

@BartoszJarocki BartoszJarocki commented Feb 2, 2024

Copy link
Copy Markdown
Contributor

This PR renames fetch to fetchOptions and adds an additional option to provide own fetch method.

fixes #243

@lukeocodes lukeocodes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I really appreciate the effort here.

The only issue with this is that it is a breaking change and we're not ready to add another major version to our release channel. I would encourage you to add a new property (not breaking) like fetchClient.

I would then replace fetchWithAuth with a function that switched Fetch if it was supplied, and then called fetchWithAuth to return a preconfigured fetch instance using the right class and the auth headers.

Superbase has a good example, and they use cross-fetch too https://github.com/supabase/supabase-js/blob/master/src/lib/fetch.ts#L6

Also any tests you're able to provide would be very helpful

@davidvonthenen davidvonthenen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clicked approve on the wrong PR. Should have been this one:
#246 (review)

@BartoszJarocki

Copy link
Copy Markdown
Contributor Author

@lukeocodes understood, my thinking was that it'll be a bit confusing to have fetch as fetch options and something different to actually override fetch. now, I changed the implementation to be similar to supabase example, thank you! would you mind taking a look again?

regarding tests, I personally am not a fan of testing protected methods so the only option I see is to add tests like that to each client type

  it("should use custom fetch when provided", async () => {
    const fetch = async () => {
      return new Response(JSON.stringify({ customFetch: true }));
    };
 
    const client = createClient(faker.string.alphanumeric(40), {
      global: { url: "https://api.mock.deepgram.com" },
      customFetch: fetch,
    });
 
    const { result, error } = await client.manage.getProjectBalances(faker.string.uuid());
 
    assert.isNull(error);
    assert.isNotNull(result);
    assert.containsAllDeepKeys(result, ["customFetch"]);
  });

wdyt?

@lukeocodes

Copy link
Copy Markdown
Member

Looks good to me! I'm going to personally test this on browsers, edge functions and Node environments to ensure we're not introducing a regression. I'll try and get this reviewed by the end of the week. Really appreciate you coming back with fixes for the PR

@BartoszJarocki

Copy link
Copy Markdown
Contributor Author

@lukeocodes awesome, please let me know if there's anything missing!

@lukeocodes lukeocodes self-assigned this Feb 17, 2024
@lukeocodes lukeocodes added the enhancement New feature or request label Feb 17, 2024
@pkpio

pkpio commented Mar 4, 2024

Copy link
Copy Markdown

Any update on this? Would be great to also see an example somewhere of a custom fetch impl to use - trying to workaround the 5 mins timeout errors of the default nodejs fetch

@BartoszJarocki

Copy link
Copy Markdown
Contributor Author

@myselfhimself

Copy link
Copy Markdown

@lukeocodes understood, my thinking was that it'll be a bit confusing to have fetch as fetch options and something different to actually override fetch. now, I changed the implementation to be similar to supabase example, thank you! would you mind taking a look again?

regarding tests, I personally am not a fan of testing protected methods so the only option I see is to add tests like that to each client type

  it("should use custom fetch when provided", async () => {
    const fetch = async () => {
      return new Response(JSON.stringify({ customFetch: true }));
    };
 
    const client = createClient(faker.string.alphanumeric(40), {
      global: { url: "https://api.mock.deepgram.com" },
      customFetch: fetch,
    });
 
    const { result, error } = await client.manage.getProjectBalances(faker.string.uuid());
 
    assert.isNull(error);
    assert.isNotNull(result);
    assert.containsAllDeepKeys(result, ["customFetch"]);
  });

wdyt?

Your test looks good, it could be added to https://github.com/deepgram/deepgram-js-sdk/blob/main/test/client.test.ts if I am not mistaken..

@lukeocodes

Copy link
Copy Markdown
Member

I'll calve out time to review and test this next week!

@myselfhimself

myselfhimself commented Apr 10, 2024

Copy link
Copy Markdown

Hello here is some example working code using an AbortController .signal-enabled custom fetch with @BartoszJarocki 's pull request:

import { DeepgramError, createClient } from "@deepgram/sdk";
import { SettingsManagerCookies as SettingsManager } from "./settingsManager";

export class DeepgramClientCancellationError extends DeepgramError {
    originalError: unknown;

    constructor(message: string, originalError: unknown) {
        super(message);
        this.name = "DeepgramClientCancellationError";
        this.originalError = originalError;
    }
}

export const createDeepgramClient = (apiKey?: string, signal?: AbortSignal) => {
    async function cancellableFetch(
        input: RequestInfo | URL, init?: RequestInit
    ): Promise<Response> {
        // Use cross-fetch with the AbortController signal
        try {
            const response = await fetch(input, { ...init, signal });
            console.log("got response in custom Fetch, with signal" + JSON.stringify(signal));
            return response;
        } catch (error) {
            if ((error as DOMException).name === "AbortError") {
                // Handle fetch abort due to timeout
                throw new DeepgramClientCancellationError("Deepgram query aborted by user", error);
            } else {
                // Re-throw other errors
                throw error;
            }
        }
    }



    var proxyUrl = "http://127.0.0.1:8889";

    console.log("creating client with Deepgram experimental customFetch parameter");
    return createClient("proxy", {
        restProxy: { url: proxyUrl },
        fetch: { headers: { forceApiKey: apiKey ?? (SettingsManager.getApiKey()) } },
        customFetch: cancellableFetch
    });
}

// Example usage:
var controller = new AbortController();
var client = createDeepgramClient(undefined /* using my settings manager */, controller.signal);
client.listen.prerecorded.transcribeFile(binFile, options).then((result) => {}).catch((err) => { if(err.name == "AbortController") { console.log("was cancelled"); })
// Some cancelling somewhere, on some click() or interrupt.
controller.abort();

To use the custom fetch pull request, I used the following package.json line: "@deepgram/sdk": "github:deepgram/deepgram-js-sdk#pull/245", and I had to cd into node_modules/@deepgram/sdk and run npm i followed by npm run build.

The forceApikey mechanism allows a deepgram client to tell a deepgram proxy to use a different API key, it is described in the deepgram http proxy project., though no deepgram proxy implementation has been published yet IMHO.

@myselfhimself

Copy link
Copy Markdown

I am about to release a product with this fork...
Here are some information for people wanting to use the fork with package.json in a CI/CD pipeline:
package.json:

...
"scripts": {
"prebuild": "ls node_modules/@deepgram/sdk | grep -q tsconfig.json && npm i --prefix node_modules/@deepgram/sdk && npm run build --prefix node_modules/@deepgram/sdk
....}
...
"dependencies": {
...
    "@deepgram/sdk": "https://github.com/BartoszJarocki/deepgram-js-sdk/tarball/feat/add-fetch-override",
...
}
...

i want to avoid taking any new namespaces while i think about the best way to do this
@lukeocodes

Copy link
Copy Markdown
Member

@BartoszJarocki thanks for your patience on this. If you could please add the test just as you had it, that will be great.

FYI, I have modified the namespace of the custom fetch to _experimentalCustomFetch. I am keen not to take up any more namespaces before I figure out how I want to allow settings to differ between products on the same instance. We have some thinking to do (as we've added more products to the SDK).

Once we have the test, this is good to go. You can add it to client.test.ts. Thanks again for your patience and help.

@BartoszJarocki

Copy link
Copy Markdown
Contributor Author

@lukeocodes I'm glad it's good to go! I just added a test 84a4e27.

@lukeocodes

lukeocodes commented Apr 25, 2024 via email

Copy link
Copy Markdown
Member

@BartoszJarocki

Copy link
Copy Markdown
Contributor Author

awesome, sounds good!

@lukeocodes lukeocodes changed the base branch from main to release/3.x.x-20240425 April 27, 2024 10:46
@lukeocodes lukeocodes merged commit bc3031d into deepgram:release/3.x.x-20240425 Apr 27, 2024
lukeocodes added a commit that referenced this pull request Apr 27, 2024
* feat: add _experimentalCustomFetch config to override fetch (#245)

* feat: allow to provide custom fetch method to AbstractRestfulClient

* fix: allow to provide both fetch and fetch options

* refactor: init custom fetch in resolveFetch

* rename `customFetch` to `_experimentalCustomFetch`

i want to avoid taking any new namespaces while i think about the best way to do this

* use `_experimentalCustomFetch` instead of `customFetch` for fetch override

* test: added custom fetch test

---------

Co-authored-by: Luke Oliff <luke@lukeoliff.com>

* chore: Bump es5-ext from 0.10.62 to 0.10.64 (#250)

Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.62 to 0.10.64.
- [Release notes](https://github.com/medikoo/es5-ext/releases)
- [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
- [Commits](medikoo/es5-ext@v0.10.62...v0.10.64)

---
updated-dependencies:
- dependency-name: es5-ext
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: add get auth token details method to manage client (#262)

* chore: edit example for is_final and endpointing together with utterance end (#264)

* fix: add missing expiration_date to CreateProjectKeyResponse (#265)

* fix: update user agent to include JS or node (#269)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Bartosz Jarocki <bartosz.jarocki@hey.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: DamienDeepgram <138720050+DamienDeepgram@users.noreply.github.com>
Co-authored-by: Lachlan Donald <lachlan@ljd.cc>
@myselfhimself

Copy link
Copy Markdown

Now using the 3.3.0 version, with a unit test to ensure the experimental custom fetch parameter keeps working before releasing myself. Thank you @BartoszJarocki @lukeocodes !!

@lukeocodes

Copy link
Copy Markdown
Member

Thanks again!

I have now (now that I have some time) been looking at a long term solution for transmitter overrides (websocket and fetch), custom headers, namespaced config per product (transcription and speech intelligence can operate differently), etc, etc.

I have a good solution that is backwards compatible, and will maintain compatibility with the experimental property we included here.

Mind if I include you in the review of it when it is ready?

@myselfhimself

Copy link
Copy Markdown

Good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Increase in UND_ERR_HEADERS_TIMEOUT errors

5 participants