Using LazyLoad to load the ky http module

I’m looking to implement an http library in my app that has built-in retries (having way too many failed Knack API requests). I’ve used “got” in node.js for this purpose which works great, and it looks like “ky” is the equivalent for browser-based environments. But as with many libraries I’ve tried, it does not seem to load with LazyLoad in Knack. I get the error “Cannot use import outside module”. Any ideas?

Here’s my loading code:

LazyLoad.js(['https://cdn.jsdelivr.net/npm/ky@0.28.5/distribution/index.min.js'], function () {
  console.log('loaded');
});

Hi Eric,

The problem is that the CDN that you are trying to load is using import statements. It’s trying to import resources that don’t exist. See image:

image

Notice how it’s trying to import from ./core/Ky.js. That resource doesn’t exist.

1 Like

Hey Eric,

As @Kelson alluded to, the ky package is only distributed as an ES module. That means in order to use it your app’s JS, you would need to use something like const ky = await import('https://cdn.jsdelivr.net/npm/ky@latest/index.js'); from within an async function.

I personally use wretch for the same purpose, which you would you use in your app like so (also using dynamic import instead of LazyLoading).

const useKnackApi = async () => {
  const { Wretcher } = await import('https://cdn.skypack.dev/wretch');
  const { retry } = await import('https://cdn.skypack.dev/wretch-middlewares');
  return Wretcher.factory('https://api.knack.com/v1', {
    headers: {
      'X-Knack-Application-Id': Knack.app.id,
      'X-Knack-REST-API-Key': 'knack',
      Authorization: Knack.getUserToken(),
    }
  }).middlewares([
    retry({
      maxAttempts: 3,
      retryOnNetworkError: true,
      until: async (res) => {
        if (res) {
          const statusCode = res.status !== undefined ? res.status : 0;
          return res.ok || (statusCode > 400 && statusCode < 500);
        }
        return false;
      },
    }),
  ]);
};

$(document).on('knack-scene-render.any', async () => {
  const knackApi = await useKnackApi();
  const { records } = await knackApi.url('/scenes/scene_7/views/view_95/records').get().json();
  console.log(records);
});
1 Like

@Kelson Thanks

And @hmnd - wow, thanks very much, that is very helpful!

@hmnd Hmm - I tried adding this to an async function:

const ky = await import('https://cdn.jsdelivr.net/npm/ky@latest/index.js');
const response = ky.put(URL, options);

And I get an “TypeError: ky.put is not a function” error, so somehow it’s not finding the ky library. I can see it’s working for you with wretch though, maybe there’s something different about ky?

I also tried it with curly braces like you have it:

const { ky } = await import('https://cdn.jsdelivr.net/npm/ky@latest/index.js');

But then I get “TypeError: Cannot read property ‘put’ of undefined”.

I’m probably missing something important…

Sorry, I should’ve tested this with ky first.

It seems jsdelivr’s version is broken, so you’ll have to use a different cdn such as cdnjs. ky also provides the main code as a default export. That means you’ll have to access ky using the default property, like so:

const { default: ky } = await import('https://cdnjs.cloudflare.com/ajax/libs/ky/0.27.0/index.min.js');

@hmnd Works great, thanks so much!

1 Like

@hmnd Does your solution with wretch handle retries with PUT/POST calls? I am having a problem with ky where a PUT call automatically generates a pre-flight OPTIONS call (because CORS), and Knack of course occasionally returns a 503 - Service Unavailable on that OPTIONS call – but the retries mechanism of ky isn’t catching those for some reason.

@EricAlderman Yes, wretch would handle those with its retryOnNetworkError option. ky probably just doesn’t catch generic network errors.
Unfortunately, there’s no way to distinguish between an actual network error, such as the internet being out, and a cors error so it’s all or nothing.

1 Like

Whoops, used the wrong account there haha

Great, thanks - again! I’ll give wretch a try.