klaviyo / klaviyo-node-sdk

Node SDK for developers to use Klaviyo's Public API
https://developers.klaviyo.com/
14 stars 2 forks source link

Using ApiClient.instance with dynamic keys per each request #7

Closed oleksandr-popov-exco closed 1 year ago

oleksandr-popov-exco commented 2 years ago

Hi Klaviyo Team,

Based on the code - ApiClient.instance basically is a singleton. I'm wondering how I can use ApiClient.instance with a dynamic Auth key for each request. E.g. For request 1 api_key = pk_1 ; or request 2 api_key = pk_2, etc. Giving that my application is multi-threaded and has multiple instances, could you suggest how I can setup ApiClient Keys per each request and do not overlap with other parallel requests?

It feels like it can be achieved by sending new instance of ApiClient to APIs (e.g. new CampaignsApi(new ApiClient(..))) So this.instance will be added in constructor https://github.com/klaviyo/klaviyo-node-sdk/blob/main/src/api/CampaignsApi.js#L37 But the issue is that CampainsApi always uses ApiClient.instance and not this.instance - https://github.com/klaviyo/klaviyo-node-sdk/blob/main/src/api/CampaignsApi.js#L74

Thanks in advance.

Ian-Montgomery-Klaviyo commented 2 years ago

This is something that needs to be fixed. For now I have come up with a work around. You can set the correct API key as the key for the singleton before each calls and it will swap between the two. There are a million ways to make this look less weird but I wrote this basic example in Express to show how its done. If you aren't familiar with Express the code just creates two rest endpoints that both create a klaviyo campaign but setting the key before each call will change the account the campaign is created on.

const defaultClient = ApiClient.instance;

app.get('/',async function(req, res, next) {
  try {
    const ApiKeyAuth = defaultClient.authentications['ApiKeyAuth'];
    ApiKeyAuth.apiKey = "ACCOUNT 1 PK";
    const resp = await Campaigns.createCampaign({}) // Make call as normal I just left the fields out for this example
    res.send(resp)
  } catch (e) {
    res.send(e)
  }
});

app.get('/1',async function(req, res, next) {
  try {
    const ApiKeyAuth = defaultClient.authentications['ApiKeyAuth'];
    ApiKeyAuth.apiKey = "ACCOUNT 2 PK";
    const resp = await Campaigns.createCampaign({}) // Make call as normal I just left the fields out for this example
    res.send(resp)
  } catch (e) {
    res.send(e)
  }
});
oleksandr-popov-exco commented 2 years ago

Thanks @Ian-Montgomery-Klaviyo for detailed explanation and example.

Do you have any ETA for this to be fixed?

It seems like changing in all places like this https://github.com/klaviyo/klaviyo-node-sdk/blob/main/src/api/CampaignsApi.js#L74

Before ` return ApiClient.instance.callApi( .. )`
After    ` return this.apiClient.callApi( .. ) ` 

should do the trick automatically. In this case usage would be something like this.

Before ` await Campaigns.createCampaign({}) `
After  ` await new CampaignsApi(client).createCampaign({}) `

And if user wants to use predefined client - no changes required, (await Campaigns.createCampaign({}) still should work) What do you think?

Ian-Montgomery-Klaviyo commented 1 year ago

The new node sdk fixes this issue and will be continually updated.

Read our Migrating from V1/V2 to the new Klaviyo APIs and Comparison between v1/v2 and new APIs.