Skip to content

Commit d530d2f

Browse files
Merge pull request #219 from apivideo/feature/nodejs_rate_limit
Add *WithResponseHeaders() methods
2 parents 9f0c184 + bf54a6b commit d530d2f

16 files changed

+1578
-685
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [2.5.7] - 2024-04-23
5+
- Add *WithResponseHeaders() methods
6+
47
## [2.5.6] - 2024-02-19
58
- Update VideoStatusIngest enum
69

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- [WatermarksApi](#watermarksapi)
2626
- [WebhooksApi](#webhooksapi)
2727
- [Models](#models)
28+
- [Rate Limiting](#rate-limiting)
2829
- [Authorization](#authorization)
2930
- [API key](#api-key)
3031
- [Get the access token](#get-the-access-token)
@@ -280,6 +281,24 @@ Method | Description | HTTP request
280281
- [WebhooksListResponse](https://github.com/apivideo/api.video-nodejs-client/blob/main/docs/model/WebhooksListResponse.md)
281282

282283

284+
### Rate Limiting
285+
286+
api.video implements rate limiting to ensure fair usage and stability of the service. The API provides the rate limit values in the response headers for any API requests you make. The /auth endpoint is the only route without rate limitation.
287+
288+
In this Node.js client, you can access these headers by using the `*WithResponseHeaders()` versions of the methods. These methods return both the response body and the headers, allowing you to check the `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Retry-After` headers to understand your current rate limit status.
289+
290+
Read more about these response headers in the [API reference](https://docs.api.video/reference#limitation).
291+
292+
Here is an example of how to use these methods:
293+
294+
```js
295+
const client = new ApiVideoClient({ apiKey: "YOUR_API_KEY" });
296+
const { body: videos, headers } = await client.videos.listWithResponseHeaders();
297+
console.log('Rate Limit:', headers['x-ratelimit-limit']);
298+
console.log('Rate Limit Remaining:', headers['x-ratelimit-remaining']);
299+
console.log('Rate Limit Retry after:', headers['x-ratelimit-retry-after']);
300+
```
301+
283302
### Authorization
284303

285304
#### API key

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@api.video/nodejs-client",
3-
"version": "2.5.6",
3+
"version": "2.5.7",
44
"description": "api.video nodejs API client",
55
"keywords": [
66
"api.video",

src/HttpClient.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ export type QueryOptions = {
2424
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
2525
};
2626

27+
export type ApiResponseHeaders = {
28+
server: string;
29+
'content-type': string;
30+
'transfer-encoding': string;
31+
connection: string;
32+
'cache-control': string;
33+
date: string;
34+
'x-ratelimit-remaining': string;
35+
'x-ratelimit-retry-after': string;
36+
'x-ratelimit-limit': string;
37+
'x-server': string;
38+
'access-control-allow-origin': string;
39+
'timing-allow-origin': string;
40+
};
41+
2742
export default class HttpClient {
2843
private apiKey?: string;
2944
private baseUri: string;
@@ -44,7 +59,7 @@ export default class HttpClient {
4459
this.chunkSize = params.chunkSize;
4560
this.headers = new AxiosHeaders({
4661
Accept: 'application/json, */*;q=0.8',
47-
'AV-Origin-Client': 'nodejs:2.5.6',
62+
'AV-Origin-Client': 'nodejs:2.5.7',
4863
Authorization: this.apiKey ? `Basic ${encode(`${this.apiKey}:`)}` : '',
4964
...(params.applicationName && params.applicationVersion
5065
? {

src/api/AdvancedAuthenticationApi.ts

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111

1212
import ObjectSerializer from '../ObjectSerializer';
13-
import HttpClient, { QueryOptions } from '../HttpClient';
13+
import HttpClient, { QueryOptions, ApiResponseHeaders } from '../HttpClient';
1414
import AccessToken from '../model/AccessToken';
1515
import AuthenticatePayload from '../model/AuthenticatePayload';
1616
import RefreshTokenPayload from '../model/RefreshTokenPayload';
@@ -33,6 +33,19 @@ export default class AdvancedAuthenticationApi {
3333
public async authenticate(
3434
authenticatePayload: AuthenticatePayload
3535
): Promise<AccessToken> {
36+
return this.authenticateWithResponseHeaders(authenticatePayload).then(
37+
(res) => res.body
38+
);
39+
}
40+
41+
/**
42+
* Returns a bearer token that can be used to authenticate other endpoint. You can find the tutorial on using the disposable bearer token [here](https://docs.api.video/reference/disposable-bearer-token-authentication).
43+
* Get Bearer Token
44+
* @param authenticatePayload
45+
*/
46+
public async authenticateWithResponseHeaders(
47+
authenticatePayload: AuthenticatePayload
48+
): Promise<{ headers: ApiResponseHeaders; body: AccessToken }> {
3649
const queryParams: QueryOptions = {};
3750
queryParams.headers = {};
3851
if (authenticatePayload === null || authenticatePayload === undefined) {
@@ -60,19 +73,19 @@ export default class AdvancedAuthenticationApi {
6073

6174
queryParams.method = 'POST';
6275

63-
return this.httpClient
64-
.call(localVarPath, queryParams)
65-
.then(
66-
(response) =>
67-
ObjectSerializer.deserialize(
68-
ObjectSerializer.parse(
69-
response.body,
70-
response.headers['content-type']
71-
),
72-
'AccessToken',
73-
''
74-
) as AccessToken
75-
);
76+
return this.httpClient.call(localVarPath, queryParams).then((response) => {
77+
return {
78+
headers: response.headers,
79+
body: ObjectSerializer.deserialize(
80+
ObjectSerializer.parse(
81+
response.body,
82+
response.headers['content-type']
83+
),
84+
'AccessToken',
85+
''
86+
) as AccessToken,
87+
};
88+
});
7689
}
7790

7891
/**
@@ -83,6 +96,19 @@ export default class AdvancedAuthenticationApi {
8396
public async refresh(
8497
refreshTokenPayload: RefreshTokenPayload
8598
): Promise<AccessToken> {
99+
return this.refreshWithResponseHeaders(refreshTokenPayload).then(
100+
(res) => res.body
101+
);
102+
}
103+
104+
/**
105+
* Accepts the old bearer token and returns a new bearer token that can be used to authenticate other endpoint. You can find the tutorial on using the disposable bearer token [here](https://docs.api.video/reference/disposable-bearer-token-authentication).
106+
* Refresh Bearer Token
107+
* @param refreshTokenPayload
108+
*/
109+
public async refreshWithResponseHeaders(
110+
refreshTokenPayload: RefreshTokenPayload
111+
): Promise<{ headers: ApiResponseHeaders; body: AccessToken }> {
86112
const queryParams: QueryOptions = {};
87113
queryParams.headers = {};
88114
if (refreshTokenPayload === null || refreshTokenPayload === undefined) {
@@ -110,18 +136,18 @@ export default class AdvancedAuthenticationApi {
110136

111137
queryParams.method = 'POST';
112138

113-
return this.httpClient
114-
.call(localVarPath, queryParams)
115-
.then(
116-
(response) =>
117-
ObjectSerializer.deserialize(
118-
ObjectSerializer.parse(
119-
response.body,
120-
response.headers['content-type']
121-
),
122-
'AccessToken',
123-
''
124-
) as AccessToken
125-
);
139+
return this.httpClient.call(localVarPath, queryParams).then((response) => {
140+
return {
141+
headers: response.headers,
142+
body: ObjectSerializer.deserialize(
143+
ObjectSerializer.parse(
144+
response.body,
145+
response.headers['content-type']
146+
),
147+
'AccessToken',
148+
''
149+
) as AccessToken,
150+
};
151+
});
126152
}
127153
}

src/api/AnalyticsApi.ts

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import { URLSearchParams } from 'url';
1313
import ObjectSerializer from '../ObjectSerializer';
14-
import HttpClient, { QueryOptions } from '../HttpClient';
14+
import HttpClient, { QueryOptions, ApiResponseHeaders } from '../HttpClient';
1515
import AnalyticsPlaysResponse from '../model/AnalyticsPlaysResponse';
1616

1717
/**
@@ -35,7 +35,37 @@ export default class AnalyticsApi {
3535
* @param { number } searchParams.currentPage Choose the number of search results to return per page. Minimum value: 1
3636
* @param { number } searchParams.pageSize Results per page. Allowed values 1-100, default is 25.
3737
*/
38-
public async getLiveStreamsPlays({
38+
public async getLiveStreamsPlays(args: {
39+
from: string;
40+
dimension:
41+
| 'liveStreamId'
42+
| 'emittedAt'
43+
| 'country'
44+
| 'deviceType'
45+
| 'operatingSystem'
46+
| 'browser';
47+
to?: string;
48+
filter?: string;
49+
currentPage?: number;
50+
pageSize?: number;
51+
}): Promise<AnalyticsPlaysResponse> {
52+
return this.getLiveStreamsPlaysWithResponseHeaders(args).then(
53+
(res) => res.body
54+
);
55+
}
56+
57+
/**
58+
* Retrieve filtered analytics about the number of plays for your live streams in a project.
59+
* Get play events for live stream
60+
* @param {Object} searchParams
61+
* @param { string } searchParams.from Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in &#x60;from&#x60;. - The date you set must be **within the last 30 days**. - The value you provide must follow the &#x60;YYYY-MM-DD&#x60; format.
62+
* @param { &#39;liveStreamId&#39; | &#39;emittedAt&#39; | &#39;country&#39; | &#39;deviceType&#39; | &#39;operatingSystem&#39; | &#39;browser&#39; } searchParams.dimension Use this query parameter to define the dimension that you want analytics for. - &#x60;liveStreamId&#x60;: Returns analytics based on the public live stream identifiers. - &#x60;emittedAt&#x60;: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in &#x60;from&#x60; and &#x60;to&#x60; is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - &#x60;country&#x60;: Returns analytics based on the viewers&#39; country. The list of supported country names are based on the [GeoNames public database](https://www.geonames.org/countries/). - &#x60;deviceType&#x60;: Returns analytics based on the type of device used by the viewers during the play event. Possible response values are: &#x60;computer&#x60;, &#x60;phone&#x60;, &#x60;tablet&#x60;, &#x60;tv&#x60;, &#x60;console&#x60;, &#x60;wearable&#x60;, &#x60;unknown&#x60;. - &#x60;operatingSystem&#x60;: Returns analytics based on the operating system used by the viewers during the play event. Response values include &#x60;windows&#x60;, &#x60;mac osx&#x60;, &#x60;android&#x60;, &#x60;ios&#x60;, &#x60;linux&#x60;. - &#x60;browser&#x60;: Returns analytics based on the browser used by the viewers during the play event. Response values include &#x60;chrome&#x60;, &#x60;firefox&#x60;, &#x60;edge&#x60;, &#x60;opera&#x60;.
63+
* @param { string } searchParams.to Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a &#x60;to&#x60; date, the API returns analytics data starting from the &#x60;from&#x60; date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the &#x60;YYYY-MM-DD&#x60; format.
64+
* @param { string } searchParams.filter Use this query parameter to filter your results to a specific live stream in a project that you want analytics for. You must use the &#x60;liveStreamId:&#x60; prefix when specifying a live stream ID.
65+
* @param { number } searchParams.currentPage Choose the number of search results to return per page. Minimum value: 1
66+
* @param { number } searchParams.pageSize Results per page. Allowed values 1-100, default is 25.
67+
*/
68+
public async getLiveStreamsPlaysWithResponseHeaders({
3969
from,
4070
dimension,
4171
to,
@@ -55,7 +85,7 @@ export default class AnalyticsApi {
5585
filter?: string;
5686
currentPage?: number;
5787
pageSize?: number;
58-
}): Promise<AnalyticsPlaysResponse> {
88+
}): Promise<{ headers: ApiResponseHeaders; body: AnalyticsPlaysResponse }> {
5989
const queryParams: QueryOptions = {};
6090
queryParams.headers = {};
6191
if (from === null || from === undefined) {
@@ -119,19 +149,19 @@ export default class AnalyticsApi {
119149

120150
queryParams.method = 'GET';
121151

122-
return this.httpClient
123-
.call(localVarPath, queryParams)
124-
.then(
125-
(response) =>
126-
ObjectSerializer.deserialize(
127-
ObjectSerializer.parse(
128-
response.body,
129-
response.headers['content-type']
130-
),
131-
'AnalyticsPlaysResponse',
132-
''
133-
) as AnalyticsPlaysResponse
134-
);
152+
return this.httpClient.call(localVarPath, queryParams).then((response) => {
153+
return {
154+
headers: response.headers,
155+
body: ObjectSerializer.deserialize(
156+
ObjectSerializer.parse(
157+
response.body,
158+
response.headers['content-type']
159+
),
160+
'AnalyticsPlaysResponse',
161+
''
162+
) as AnalyticsPlaysResponse,
163+
};
164+
});
135165
}
136166

137167
/**
@@ -145,7 +175,35 @@ export default class AnalyticsApi {
145175
* @param { number } searchParams.currentPage Choose the number of search results to return per page. Minimum value: 1
146176
* @param { number } searchParams.pageSize Results per page. Allowed values 1-100, default is 25.
147177
*/
148-
public async getVideosPlays({
178+
public async getVideosPlays(args: {
179+
from: string;
180+
dimension:
181+
| 'videoId'
182+
| 'emittedAt'
183+
| 'country'
184+
| 'deviceType'
185+
| 'operatingSystem'
186+
| 'browser';
187+
to?: string;
188+
filter?: string;
189+
currentPage?: number;
190+
pageSize?: number;
191+
}): Promise<AnalyticsPlaysResponse> {
192+
return this.getVideosPlaysWithResponseHeaders(args).then((res) => res.body);
193+
}
194+
195+
/**
196+
* Retrieve filtered analytics about the number of plays for your videos in a project.
197+
* Get play events for video
198+
* @param {Object} searchParams
199+
* @param { string } searchParams.from Use this query parameter to set the start date for the time period that you want analytics for. - The API returns analytics data including the day you set in &#x60;from&#x60;. - The date you set must be **within the last 30 days**. - The value you provide must follow the &#x60;YYYY-MM-DD&#x60; format.
200+
* @param { &#39;videoId&#39; | &#39;emittedAt&#39; | &#39;country&#39; | &#39;deviceType&#39; | &#39;operatingSystem&#39; | &#39;browser&#39; } searchParams.dimension Use this query parameter to define the dimension that you want analytics for. - &#x60;videoId&#x60;: Returns analytics based on the public video identifiers. - &#x60;emittedAt&#x60;: Returns analytics based on the times of the play events. The API returns data in specific interval groups. When the date period you set in &#x60;from&#x60; and &#x60;to&#x60; is less than or equals to 2 days, the response for this dimension is grouped in hourly intervals. Otherwise, it is grouped in daily intervals. - &#x60;country&#x60;: Returns analytics based on the viewers&#39; country. The list of supported country names are based on the [GeoNames public database](https://www.geonames.org/countries/). - &#x60;deviceType&#x60;: Returns analytics based on the type of device used by the viewers during the play event. Possible response values are: &#x60;computer&#x60;, &#x60;phone&#x60;, &#x60;tablet&#x60;, &#x60;tv&#x60;, &#x60;console&#x60;, &#x60;wearable&#x60;, &#x60;unknown&#x60;. - &#x60;operatingSystem&#x60;: Returns analytics based on the operating system used by the viewers during the play event. Response values include &#x60;windows&#x60;, &#x60;mac osx&#x60;, &#x60;android&#x60;, &#x60;ios&#x60;, &#x60;linux&#x60;. - &#x60;browser&#x60;: Returns analytics based on the browser used by the viewers during the play event. Response values include &#x60;chrome&#x60;, &#x60;firefox&#x60;, &#x60;edge&#x60;, &#x60;opera&#x60;.
201+
* @param { string } searchParams.to Use this optional query parameter to set the end date for the time period that you want analytics for. - If you do not specify a &#x60;to&#x60; date, the API returns analytics data starting from the &#x60;from&#x60; date up until today, and excluding today. - The date you set must be **within the last 30 days**. - The value you provide must follow the &#x60;YYYY-MM-DD&#x60; format.
202+
* @param { string } searchParams.filter Use this query parameter to filter your results to a specific video in a project that you want analytics for. You must use the &#x60;videoId:&#x60; prefix when specifying a video ID.
203+
* @param { number } searchParams.currentPage Choose the number of search results to return per page. Minimum value: 1
204+
* @param { number } searchParams.pageSize Results per page. Allowed values 1-100, default is 25.
205+
*/
206+
public async getVideosPlaysWithResponseHeaders({
149207
from,
150208
dimension,
151209
to,
@@ -165,7 +223,7 @@ export default class AnalyticsApi {
165223
filter?: string;
166224
currentPage?: number;
167225
pageSize?: number;
168-
}): Promise<AnalyticsPlaysResponse> {
226+
}): Promise<{ headers: ApiResponseHeaders; body: AnalyticsPlaysResponse }> {
169227
const queryParams: QueryOptions = {};
170228
queryParams.headers = {};
171229
if (from === null || from === undefined) {
@@ -229,18 +287,18 @@ export default class AnalyticsApi {
229287

230288
queryParams.method = 'GET';
231289

232-
return this.httpClient
233-
.call(localVarPath, queryParams)
234-
.then(
235-
(response) =>
236-
ObjectSerializer.deserialize(
237-
ObjectSerializer.parse(
238-
response.body,
239-
response.headers['content-type']
240-
),
241-
'AnalyticsPlaysResponse',
242-
''
243-
) as AnalyticsPlaysResponse
244-
);
290+
return this.httpClient.call(localVarPath, queryParams).then((response) => {
291+
return {
292+
headers: response.headers,
293+
body: ObjectSerializer.deserialize(
294+
ObjectSerializer.parse(
295+
response.body,
296+
response.headers['content-type']
297+
),
298+
'AnalyticsPlaysResponse',
299+
''
300+
) as AnalyticsPlaysResponse,
301+
};
302+
});
245303
}
246304
}

0 commit comments

Comments
 (0)