ecyrbe / zodios

typescript http client and server with zod validation
https://www.zodios.org/
MIT License
1.71k stars 46 forks source link

Skipping plugin for single request #286

Closed JarnoRFB closed 1 year ago

JarnoRFB commented 1 year ago

I am using the pluginToken plugin. When defining getToken I want to use another method of the client that is part of the API definition. However, doing so leads to an infinite recursion, because the plugin is invoked again and again.

Example:

client.use(
  pluginToken({
    getToken: async () => {
      const response = await client.authenticate({
        apiKey,
        apiSecret,
      });
      return response.accessToken;
    },
  }),
);

Would it somehow be possible to skip plugins on individual request to avoid this situation? Right now I can work around that by using the underlying axios instance and making the request directly to the endpoint, but it seems to go againt the purpose of the library to not use the nice client methods. Other options that come to mind are

ecyrbe commented 1 year ago

This will also be problematic. Indeed, you are authenticating for each API call. Making each API call doing two network calls. There is a better way to do this with currently available features:

let accessToken: string;
// do not use token for authenticate endpoint
client.use('authenticate', pluginClearHeader('autorisation'));
client.use(
  pluginToken({
    getToken: async () => accessToken
  }),
);
const response = await client.authenticate({
      apiKey,
      apiSecret,
});
accessToken= response.accessToken;

And you can use the renewal part of the plugin to renew your token if needed

JarnoRFB commented 1 year ago

Thanks for clarifying! I somehow assumed that getToken would actually fetch the token lazily. However, I am stil running into the same problem when implementing refreshToken where I definitely need to access a method that is defined on the client within the plugin.

ecyrbe commented 1 year ago

can you share how you tried to implement refreshToken ? it should not do any recursive calls if you call authenticate in there.

JarnoRFB commented 1 year ago

Ah I see, using the client inside refreshToken is no problem, because it is only called on error. This is my current implemention for a function to instantiate a new client

async (api_key: string, api_secret: string) => {
  const client = new Zodios(
    "https://myapi.com",
    endpoints
  );
  client.use("authenticateApi", pluginClearHeader("Authorization"));

  let {access_token, refresh_token} = await client.authenticateApi({
    api_key,
    api_secret,
  });

  client.use(
    pluginToken({
      getToken: async () => access_token,
      renewToken: async () => {
        if (refresh_token) {
          ({access_token, refresh_token} = await client.authenticateApi({
            refresh_token,
          }));
          return access_token;
        } else {
          throw new Error("No refresh token available");
        }
      },
    })
  );
  return client;
};
ecyrbe commented 1 year ago

Thanks, tell me if you successfully made it work with this implementation.

ecyrbe commented 1 year ago

If so, i'll close this issue and to get back to the original request, i'll create a separate issue to track implementation of skipped plugins as it can be helpful for other use cases