Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bugfix] Implement next.js fetch compatibility #126

Merged
merged 6 commits into from
Sep 27, 2023
Merged

Conversation

jhamon
Copy link
Collaborator

@jhamon jhamon commented Sep 26, 2023

Problem

Next.js seems to stub out the cross-fetch polyfill which has the effect of silently breaking code which depends on cross-fetch to make API requests.

Related bugs:

Solution

I have added a getFetch utility which looks for a fetch implementation already loaded in the global scope and returns the cross-fetch implementation only as a last resort.

  • Now when using our client library in a next.js project, this should result in the @vercel/fetch implementation being used in favor of the disabled polyfill.
  • When using this client library in other environments where next.js is not being used, it still activates the cross-fetch polyfill.
  • Also, I've exposed an experimental configuration option for anyone who wants to pass in a different fetch implementation to use. This could open up some flexibility for people with specific network configurations who need the ability to configure the http client being used in some way. If the user provides a fetch implementation through the fetchApi config option, it will take priority over a fetch in the global scope or the cross-fetch polyfill.

Other changes

  • Added additional integration tests for query method
  • Added an explicit dependency on the encoding module to resolve a warning message similar to this one

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Test Plan

I am currently testing this change in the context of work-in-progress to migrate our vercel-starter example to using the v1 version of the client. pinecone-io/pinecone-vercel-starter#14

  • Checkout the PR branch from pinecone-vercel-starter in a sibling directory
  • Install my local changes to this client with npm install ../pinecone-ts-client
  • Try to run the starter project with npm run dev
  • Attempt to use the sample app and observe it progresses beyond the error related broken cross-fetch

@jhamon jhamon force-pushed the jhamon/nextjs-fetch-fix branch from a1090d0 to ba9deb7 Compare September 27, 2023 11:25
@jhamon jhamon changed the title [Bugfix] Add getFetch utility for next.js compatibility [Bugfix] Implement next.js fetch compatibility Sep 27, 2023
@@ -10,7 +10,7 @@ module.exports = {
testPathIgnorePatterns: ['src/integration'],
testTimeout: 100000,
verbose: true,
detectOpenHandles: true,
detectOpenHandles: false,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I started getting an error related to TLSWRAP after adding some more integration tests. I should follow up on this later, but for now I don't want to block on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

According to docs it's a large performance hit and should be generally disabled anyways outside of debugging. Seems fine to me. 👍

@jhamon jhamon marked this pull request as ready for review September 27, 2023 13:05
// other implementations are stubbed out.
return global.fetch;
} else {
// Use ponyfill as last resort
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Use ponyfill as last resort
// Use polyfill as last resort

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is an intentional spelling, actually. cross-fetch can be imported globally ("polyfill") or locally ("ponyfill") according to their readme. Seems smart not to make global changes in a library that could interfere with other stuff in peoples environments.

return global.fetch;
} else {
// Use ponyfill as last resort
return crossFetch;
Copy link
Contributor

Choose a reason for hiding this comment

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

So just to confirm my understanding:

  1. If the user provides fetch, use that
  2. In Vercel, the global.fetch if statement will be true, so we're good in Vercel land (or do they actually provide config.fetchApi?)
  3. When running locally in a Pinecone example app, cross-fetch will be used, so that network requests the client needs to initialize will succeed

Do I have that right?

Copy link
Contributor

Choose a reason for hiding this comment

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

That matches my understanding as well, although I don't think Vercel will be providing anything via config.fetchApi. Unless of course someone using Vercel opts to provide their own fetch.

Copy link
Collaborator Author

@jhamon jhamon Sep 27, 2023

Choose a reason for hiding this comment

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

My understanding is that next.js loads @vercel/fetch and it will be available as a global without the need to specifically import it. So the next.js scenario would be the second logical branch here. My testing with the vercel starter showed that this now seems to work as expected.

The ability to pass it through config is just an escape hatch for anyone that has weird/specific needs around the http client configuration that I don't have bandwidth to address at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Roger, thanks!

Copy link
Contributor

@austin-denoble austin-denoble left a comment

Choose a reason for hiding this comment

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

Nice fix, Jen. Overall the code LGTM, I did run into issues while trying to test via the Vercel starter directly though:

Screenshot 2023-09-27 at 11 12 42 AM
Screenshot 2023-09-27 at 11 12 32 AM

This may be an issue on my end, still playing with it a bit. Let me know if there's anything obvious.

@@ -10,7 +10,7 @@ module.exports = {
testPathIgnorePatterns: ['src/integration'],
testTimeout: 100000,
verbose: true,
detectOpenHandles: true,
detectOpenHandles: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

According to docs it's a large performance hit and should be generally disabled anyways outside of debugging. Seems fine to me. 👍

return global.fetch;
} else {
// Use ponyfill as last resort
return crossFetch;
Copy link
Contributor

Choose a reason for hiding this comment

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

That matches my understanding as well, although I don't think Vercel will be providing anything via config.fetchApi. Unless of course someone using Vercel opts to provide their own fetch.

@@ -12,7 +8,12 @@ describe('deleteMany', () => {
beforeAll(async () => {
pinecone = new Pinecone();

await createIndexIfDoesNotExist(pinecone, INDEX_NAME);
Copy link
Contributor

Choose a reason for hiding this comment

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

This was pulled out since we can use the new suppressConflicts and just call the create regardless, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep, sorry I didn't explain that better.

Comment on lines +10 to +15
// fetchApi is a complex type that I don't really want to recreate in the
// form of a json schema (seems difficult and error prone). So we will
// rely on TypeScript to guide people in the right direction here.
// But declaring it here as Type.Any() is needed to avoid getting caught
// in the additionalProperties check.
fetchApi: Type.Optional(Type.Any()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense, thanks for the comment.

import crossFetch from 'cross-fetch';
import type { PineconeConfiguration } from '../data';

export const getFetch = (config: PineconeConfiguration) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Is it worth adding a tiny unit test file for this utility?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is simple enough that I don't think unit-testing would tell us much. I think what would be more valuable here would be to have some next.js-specific integration testing. But I think it's too much additional scope to add as part of this PR. Will have to look into it as a follow-up item.

@jhamon jhamon merged commit b34f41b into main Sep 27, 2023
18 checks passed
@jhamon jhamon deleted the jhamon/nextjs-fetch-fix branch September 27, 2023 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants