@bauer-group/n8n-nodes-http-throttled-request 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -146
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.d.ts +2 -1
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.d.ts.map +1 -1
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.js +204 -293
- package/dist/nodes/HttpRequest/HttpRequestThrottled.node.js.map +1 -1
- package/dist/nodes/HttpRequest/throttle-wrapper.d.ts +15 -0
- package/dist/nodes/HttpRequest/throttle-wrapper.d.ts.map +1 -0
- package/dist/nodes/HttpRequest/throttle-wrapper.js +68 -0
- package/dist/nodes/HttpRequest/throttle-wrapper.js.map +1 -0
- package/dist/nodes/HttpRequest/throttling-props.d.ts +3 -0
- package/dist/nodes/HttpRequest/throttling-props.d.ts.map +1 -0
- package/dist/nodes/HttpRequest/throttling-props.js +57 -0
- package/dist/nodes/HttpRequest/throttling-props.js.map +1 -0
- package/dist/nodes/HttpRequest/translations/de/httpRequestThrottled.json +30 -0
- package/dist/nodes/HttpRequest/v3-loader.d.ts +7 -0
- package/dist/nodes/HttpRequest/v3-loader.d.ts.map +1 -0
- package/dist/nodes/HttpRequest/v3-loader.js +31 -0
- package/dist/nodes/HttpRequest/v3-loader.js.map +1 -0
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
-
# n8n-nodes-http-throttled-request
|
|
1
|
+
# @bauer-group/n8n-nodes-http-throttled-request
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An n8n community node that adds intelligent rate-limit throttling to HTTP requests. It automatically detects rate-limit responses (HTTP 429, 503, 504) and waits the appropriate time before retrying, using information from response headers.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Automatic Rate Limit Detection
|
|
8
|
-
- **Smart Wait Time Calculation
|
|
9
|
-
- **Jitter Support
|
|
10
|
-
- **
|
|
11
|
-
- **Shadow-Override Ready**: Can replace the core HTTP Request node transparently
|
|
12
|
-
- **Full Authentication Support**: None, Basic Auth, Header Auth, OAuth1, OAuth2
|
|
7
|
+
- **Automatic Rate Limit Detection** — Detects HTTP 429, 503, and 504 status codes
|
|
8
|
+
- **Smart Wait Time Calculation** — Parses `Retry-After`, `X-RateLimit-*`, and HubSpot-specific headers
|
|
9
|
+
- **Jitter Support** — Prevents thundering herd with configurable random variance
|
|
10
|
+
- **Full Authentication Support** — None, Basic Auth, Header Auth, OAuth1, OAuth2
|
|
13
11
|
|
|
14
12
|
## Installation
|
|
15
13
|
|
|
16
|
-
###
|
|
14
|
+
### n8n Community Nodes (Recommended)
|
|
17
15
|
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
1. Open your self-hosted n8n instance
|
|
17
|
+
2. Go to **Settings** → **Community Nodes**
|
|
18
|
+
3. Enter `@bauer-group/n8n-nodes-http-throttled-request`
|
|
19
|
+
4. Click **Install**
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
The node appears immediately in the node panel — no restart required.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
> Community Nodes are only available on self-hosted n8n instances.
|
|
24
|
+
|
|
25
|
+
### Manual Installation
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
For local development or environments without Community Nodes support:
|
|
29
28
|
|
|
30
|
-
1. Clone
|
|
29
|
+
1. Clone this repository
|
|
31
30
|
2. Build the package:
|
|
32
31
|
|
|
33
32
|
```bash
|
|
@@ -38,144 +37,110 @@ npm run build
|
|
|
38
37
|
3. Link to your n8n installation:
|
|
39
38
|
|
|
40
39
|
```bash
|
|
41
|
-
# Navigate to your n8n custom nodes directory
|
|
42
40
|
cd ~/.n8n/nodes
|
|
43
|
-
|
|
44
|
-
# Link the package
|
|
45
|
-
npm link n8n-nodes-http-throttled-request
|
|
41
|
+
npm link @bauer-group/n8n-nodes-http-throttled-request
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
4. Restart
|
|
44
|
+
4. Restart n8n
|
|
45
|
+
|
|
46
|
+
### Docker
|
|
47
|
+
|
|
48
|
+
Install the community node via `N8N_COMMUNITY_PACKAGES`:
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
```yaml
|
|
51
|
+
services:
|
|
52
|
+
n8n:
|
|
53
|
+
image: n8nio/n8n
|
|
54
|
+
environment:
|
|
55
|
+
- N8N_COMMUNITY_PACKAGES=@bauer-group/n8n-nodes-http-throttled-request
|
|
56
|
+
```
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
Alternatively, mount the built package as a volume:
|
|
53
59
|
|
|
54
60
|
```yaml
|
|
55
|
-
# docker-compose.yml
|
|
56
61
|
services:
|
|
57
62
|
n8n:
|
|
58
63
|
image: n8nio/n8n
|
|
59
64
|
volumes:
|
|
60
65
|
- ./n8n-nodes-http-throttled-request:/home/node/.n8n/nodes/n8n-nodes-http-throttled-request
|
|
61
|
-
environment:
|
|
62
|
-
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/nodes
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
## Configuration
|
|
66
69
|
|
|
67
70
|
### Node Parameters
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|-----------|------|---------|-------------|
|
|
71
|
-
| **Method** | Options | GET | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
72
|
-
| **URL** | String | - | Target URL for the request |
|
|
73
|
-
| **Authentication** | Options | None | Authentication type |
|
|
74
|
-
| **Send Headers** | Boolean | false | Enable custom headers |
|
|
75
|
-
| **Send Body** | Boolean | false | Enable request body |
|
|
76
|
-
| **Throttling aktivieren** | Boolean | true | Enable automatic rate-limit handling |
|
|
77
|
-
|
|
78
|
-
### Throttling Settings
|
|
79
|
-
|
|
80
|
-
When throttling is enabled, additional options become available:
|
|
81
|
-
|
|
82
|
-
| Setting | Type | Default | Description |
|
|
83
|
-
|---------|------|---------|-------------|
|
|
84
|
-
| **HTTP-Codes** | Multi-select | 429 | Status codes that trigger throttling (429, 503, 504) |
|
|
85
|
-
| **Standard-Wartezeit (ms)** | Number | 10000 | Default wait time when no header provides guidance |
|
|
86
|
-
| **Zufaellige Abweichung (+/-%)** | Number | 25 | Jitter percentage to prevent thundering herd |
|
|
87
|
-
| **Max. Throttle-Versuche** | Number | 10 | Maximum retry attempts before failing |
|
|
88
|
-
|
|
89
|
-
### Authentication Types
|
|
72
|
+
This node inherits **all** parameters from the built-in HTTP Request node (V3), including:
|
|
90
73
|
|
|
91
|
-
|
|
74
|
+
- All HTTP methods, URL, authentication (50+ credential types), headers, query parameters, body options
|
|
75
|
+
- Response format, pagination, proxy, timeout, SSL, redirect, and batching settings
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
- **Basic Auth**: HTTP Basic Authentication (username/password)
|
|
95
|
-
- **Header Auth**: Custom header-based authentication
|
|
96
|
-
- **OAuth1**: OAuth 1.0 authentication
|
|
97
|
-
- **OAuth2**: OAuth 2.0 authentication
|
|
98
|
-
|
|
99
|
-
## Usage Examples
|
|
100
|
-
|
|
101
|
-
### Basic GET Request with Throttling
|
|
102
|
-
|
|
103
|
-
1. Add the "HTTP Request" node to your workflow
|
|
104
|
-
2. Set the URL to your API endpoint
|
|
105
|
-
3. Enable "Throttling aktivieren" (enabled by default)
|
|
106
|
-
4. Configure throttling settings as needed
|
|
107
|
-
5. Execute the workflow
|
|
77
|
+
In addition, the following throttling parameter is appended:
|
|
108
78
|
|
|
109
|
-
|
|
79
|
+
| Parameter | Type | Default | Description |
|
|
80
|
+
| ---------------------- | ------- | ------- | ------------------------------------ |
|
|
81
|
+
| **Enable Throttling** | Boolean | true | Enable automatic rate-limit handling |
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
2. Set Method to "POST"
|
|
113
|
-
3. Enter the target URL
|
|
114
|
-
4. Enable "Send Body"
|
|
115
|
-
5. Select "JSON" as Body Content Type
|
|
116
|
-
6. Enter your JSON payload in the Body field
|
|
117
|
-
|
|
118
|
-
### API Request with Rate Limit Handling
|
|
83
|
+
### Throttling Settings
|
|
119
84
|
|
|
120
|
-
|
|
85
|
+
When throttling is enabled, the following settings become available under *Throttling Settings*:
|
|
121
86
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
87
|
+
| Setting | Type | Default | Description |
|
|
88
|
+
| ---------------------------- | ------------ | ------- | ---------------------------------------------------- |
|
|
89
|
+
| **HTTP Codes** | Multi-select | 429 | Status codes that trigger throttling (429, 503, 504) |
|
|
90
|
+
| **Default Wait Time (ms)** | Number | 5000 | Wait time when no response header provides guidance |
|
|
91
|
+
| **Random Jitter (±%)** | Number | 25 | Jitter percentage to prevent thundering herd |
|
|
92
|
+
| **Max Throttle Retries** | Number | 5 | Maximum retry attempts before failing |
|
|
126
93
|
|
|
127
94
|
## How It Works
|
|
128
95
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
When the node receives a response with a configured throttle status code (429, 503, or 504), it:
|
|
96
|
+
When the node receives a response with a configured throttle status code, it:
|
|
132
97
|
|
|
133
|
-
1. Extracts wait time from response headers
|
|
134
|
-
2. Applies jitter to
|
|
98
|
+
1. Extracts the wait time from response headers
|
|
99
|
+
2. Applies jitter to distribute retry attempts
|
|
135
100
|
3. Waits the calculated time
|
|
136
101
|
4. Retries the request
|
|
137
102
|
5. Repeats until success or max retries reached
|
|
138
103
|
|
|
139
104
|
### Header Priority
|
|
140
105
|
|
|
141
|
-
The
|
|
106
|
+
The wait time is determined using this priority (highest first):
|
|
142
107
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
108
|
+
| Priority | Source | Example |
|
|
109
|
+
| -------- | --------------------------------------------- | --------------------------------------------- |
|
|
110
|
+
| 1 | `Retry-After` (seconds) | `Retry-After: 30` |
|
|
111
|
+
| 1 | `Retry-After` (HTTP-Date) | `Retry-After: Wed, 19 Feb 2025 12:00:00 GMT` |
|
|
112
|
+
| 2 | `X-RateLimit-Remaining: 0` + reset timestamp | `X-RateLimit-Reset: 1739966400` |
|
|
113
|
+
| 3 | Reset timestamp alone | `X-RateLimit-Reset: 1739966400` |
|
|
114
|
+
| 4 | Default fallback | Configured *Default Wait Time* |
|
|
146
115
|
|
|
147
|
-
|
|
148
|
-
- `X-RateLimit-Remaining: 0` combined with reset timestamp
|
|
116
|
+
### Supported Headers
|
|
149
117
|
|
|
150
|
-
|
|
151
|
-
|
|
118
|
+
- `Retry-After` — seconds or HTTP-Date
|
|
119
|
+
- `X-RateLimit-Reset` / `X-RateLimit-Remaining`
|
|
120
|
+
- `X-HubSpot-RateLimit-Reset` / `X-HubSpot-RateLimit-Remaining`
|
|
121
|
+
- `RateLimit-Reset` / `RateLimit-Remaining`
|
|
152
122
|
|
|
153
|
-
|
|
154
|
-
- Uses configured default wait time
|
|
123
|
+
### Safety Limits
|
|
155
124
|
|
|
156
|
-
|
|
125
|
+
- **Maximum wait time**: 5 minutes (300,000 ms)
|
|
126
|
+
- **Jitter range**: 0–100%
|
|
127
|
+
- **Minimum jitter result**: 0 ms (never negative)
|
|
157
128
|
|
|
158
|
-
|
|
159
|
-
|--------|--------|---------|
|
|
160
|
-
| `Retry-After` | Seconds or HTTP-Date | `30` or `Wed, 19 Feb 2025 12:00:00 GMT` |
|
|
161
|
-
| `X-RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
162
|
-
| `X-RateLimit-Remaining` | Integer | `0` |
|
|
163
|
-
| `X-HubSpot-RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
164
|
-
| `X-HubSpot-RateLimit-Remaining` | Integer | `0` |
|
|
165
|
-
| `RateLimit-Reset` | Unix timestamp | `1739966400` |
|
|
166
|
-
| `RateLimit-Remaining` | Integer | `0` |
|
|
129
|
+
## Usage Examples
|
|
167
130
|
|
|
168
|
-
###
|
|
131
|
+
### Basic GET with Throttling
|
|
169
132
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
133
|
+
1. Add the **HTTP Request (Throttled)** node to your workflow
|
|
134
|
+
2. Set the URL to your API endpoint
|
|
135
|
+
3. Throttling is enabled by default — configure settings as needed
|
|
136
|
+
4. Execute the workflow
|
|
173
137
|
|
|
174
|
-
###
|
|
138
|
+
### API with Rate Limits (e.g. HubSpot, GitHub, Stripe)
|
|
175
139
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
140
|
+
1. Enable throttling with appropriate HTTP codes (429, 503)
|
|
141
|
+
2. Set a reasonable default wait time (e.g. 5,000 ms)
|
|
142
|
+
3. Configure max retries based on your workflow timeout
|
|
143
|
+
4. Add jitter (25%) to distribute retry attempts
|
|
179
144
|
|
|
180
145
|
## Development
|
|
181
146
|
|
|
@@ -200,61 +165,52 @@ npm test -- --coverage
|
|
|
200
165
|
### Project Structure
|
|
201
166
|
|
|
202
167
|
```
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
├──
|
|
207
|
-
├──
|
|
208
|
-
│
|
|
209
|
-
│
|
|
210
|
-
│
|
|
211
|
-
└──
|
|
212
|
-
|
|
168
|
+
├── src/
|
|
169
|
+
│ └── nodes/
|
|
170
|
+
│ └── HttpRequest/
|
|
171
|
+
│ ├── HttpRequestThrottled.node.ts # Main node (V3 composition + fallback)
|
|
172
|
+
│ ├── v3-loader.ts # Dynamic V3 node loader
|
|
173
|
+
│ ├── throttle-wrapper.ts # Helper interception for throttling
|
|
174
|
+
│ ├── throttling.ts # Wait time calculation logic
|
|
175
|
+
│ ├── throttling-props.ts # Throttling UI properties
|
|
176
|
+
│ └── translations/de/ # German translation
|
|
177
|
+
├── test/
|
|
178
|
+
│ └── throttling.test.ts # Unit tests
|
|
179
|
+
├── package.json
|
|
180
|
+
└── tsconfig.json
|
|
213
181
|
```
|
|
214
182
|
|
|
215
183
|
## Troubleshooting
|
|
216
184
|
|
|
217
185
|
### Node not appearing in n8n
|
|
218
186
|
|
|
219
|
-
1.
|
|
220
|
-
2.
|
|
221
|
-
3.
|
|
222
|
-
4. Restart n8n after installation
|
|
187
|
+
1. Check n8n logs for loading errors
|
|
188
|
+
2. Verify the package is installed correctly (Settings → Community Nodes)
|
|
189
|
+
3. For manual installs: ensure `npm run build` completed and restart n8n
|
|
223
190
|
|
|
224
191
|
### Throttling not working
|
|
225
192
|
|
|
226
|
-
1. Verify
|
|
193
|
+
1. Verify *Enable Throttling* is enabled (default: on)
|
|
227
194
|
2. Check that the API returns one of the configured HTTP codes
|
|
228
195
|
3. Review n8n execution logs for throttling messages
|
|
229
196
|
|
|
230
197
|
### Maximum retries exceeded
|
|
231
198
|
|
|
232
|
-
If you see
|
|
233
|
-
|
|
234
|
-
|
|
199
|
+
If you see `Throttling: max retries (…) exceeded`:
|
|
200
|
+
|
|
201
|
+
1. Increase *Max Throttle Retries*
|
|
202
|
+
2. Increase *Default Wait Time* to wait longer between retries
|
|
235
203
|
3. Check if the API requires authentication or has other restrictions
|
|
236
204
|
|
|
237
205
|
## API Reference
|
|
238
206
|
|
|
239
|
-
### Throttling Module Exports
|
|
240
|
-
|
|
241
207
|
```typescript
|
|
242
|
-
// Maximum wait time cap (5 minutes)
|
|
243
208
|
export const MAX_THROTTLE_WAIT_MS = 300_000;
|
|
244
209
|
|
|
245
|
-
// Normalize headers to lowercase keys
|
|
246
210
|
export function normalizeHeaders(raw: Record<string, unknown>): Record<string, string>;
|
|
247
|
-
|
|
248
|
-
// Parse Retry-After header to milliseconds
|
|
249
211
|
export function parseRetryAfterToMs(v: string): number | null;
|
|
250
|
-
|
|
251
|
-
// Parse reset timestamp headers to wait milliseconds
|
|
252
212
|
export function parseResetToWaitMs(h: Record<string, string>): number | null;
|
|
253
|
-
|
|
254
|
-
// Compute wait time from response headers
|
|
255
213
|
export function computeWaitMs(rawHeaders: Record<string, unknown>, defaultWaitMs: number): number;
|
|
256
|
-
|
|
257
|
-
// Apply jitter to wait time
|
|
258
214
|
export function applyJitter(baseMs: number, jitterPct: number): number;
|
|
259
215
|
```
|
|
260
216
|
|
|
@@ -266,7 +222,5 @@ MIT
|
|
|
266
222
|
|
|
267
223
|
1. Fork the repository
|
|
268
224
|
2. Create a feature branch
|
|
269
|
-
3.
|
|
270
|
-
4.
|
|
271
|
-
5. Submit a pull request
|
|
272
|
-
|
|
225
|
+
3. Run tests: `npm test`
|
|
226
|
+
4. Submit a pull request
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
2
2
|
export declare class HttpRequestThrottled implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
|
+
constructor();
|
|
4
5
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
6
|
}
|
|
6
7
|
//# sourceMappingURL=HttpRequestThrottled.node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpRequestThrottled.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HttpRequestThrottled.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAGrB,MAAM,cAAc,CAAC;AAsGtB,qBAAa,oBAAqB,YAAW,SAAS;IACpD,WAAW,EAAE,oBAAoB,CAAC;;IAqB5B,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAoBxE"}
|
|
@@ -1,317 +1,228 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// nodes/HttpRequest/HttpRequestThrottled.node.ts
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.HttpRequestThrottled = void 0;
|
|
5
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const v3_loader_1 = require("./v3-loader");
|
|
6
|
+
const throttle_wrapper_1 = require("./throttle-wrapper");
|
|
7
|
+
const throttling_props_1 = require("./throttling-props");
|
|
6
8
|
const throttling_1 = require("./throttling");
|
|
7
|
-
// ──
|
|
9
|
+
// ── V3 node loading (runs once at module load) ────────────────────────────────
|
|
10
|
+
const v3Ref = (0, v3_loader_1.loadV3Node)();
|
|
11
|
+
// ── Fallback description (used when n8n-nodes-base is not available) ──────────
|
|
12
|
+
const fallbackDescription = {
|
|
13
|
+
name: "httpRequestThrottled",
|
|
14
|
+
displayName: "HTTP Request (Throttled)",
|
|
15
|
+
icon: "fa:globe",
|
|
16
|
+
group: ["output"],
|
|
17
|
+
version: 1,
|
|
18
|
+
subtitle: '={{$parameter["method"] + ": " + $parameter["url"]}}',
|
|
19
|
+
description: "Makes an HTTP request with automatic rate-limit throttling",
|
|
20
|
+
defaults: { name: "HTTP Request (Throttled)", color: "#FF8500" },
|
|
21
|
+
inputs: ["main"],
|
|
22
|
+
outputs: ["main"],
|
|
23
|
+
credentials: [
|
|
24
|
+
{ name: "httpBasicAuth", required: false, displayOptions: { show: { authentication: ["basicAuth"] } } },
|
|
25
|
+
{ name: "httpHeaderAuth", required: false, displayOptions: { show: { authentication: ["headerAuth"] } } },
|
|
26
|
+
{ name: "oAuth1Api", required: false, displayOptions: { show: { authentication: ["oAuth1"] } } },
|
|
27
|
+
{ name: "oAuth2Api", required: false, displayOptions: { show: { authentication: ["oAuth2"] } } },
|
|
28
|
+
],
|
|
29
|
+
properties: [
|
|
30
|
+
{
|
|
31
|
+
displayName: "Method", name: "method", type: "options", default: "GET",
|
|
32
|
+
options: [
|
|
33
|
+
{ name: "DELETE", value: "DELETE" }, { name: "GET", value: "GET" },
|
|
34
|
+
{ name: "HEAD", value: "HEAD" }, { name: "OPTIONS", value: "OPTIONS" },
|
|
35
|
+
{ name: "PATCH", value: "PATCH" }, { name: "POST", value: "POST" },
|
|
36
|
+
{ name: "PUT", value: "PUT" },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{ displayName: "URL", name: "url", type: "string", default: "", placeholder: "https://example.com", required: true },
|
|
40
|
+
{
|
|
41
|
+
displayName: "Authentication", name: "authentication", type: "options", default: "none",
|
|
42
|
+
options: [
|
|
43
|
+
{ name: "None", value: "none" }, { name: "Basic Auth", value: "basicAuth" },
|
|
44
|
+
{ name: "Header Auth", value: "headerAuth" }, { name: "OAuth1", value: "oAuth1" },
|
|
45
|
+
{ name: "OAuth2", value: "oAuth2" },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{ displayName: "Send Headers", name: "sendHeaders", type: "boolean", default: false },
|
|
49
|
+
{
|
|
50
|
+
displayName: "Header Parameters", name: "headerParameters", type: "fixedCollection",
|
|
51
|
+
typeOptions: { multipleValues: true }, default: { parameters: [] },
|
|
52
|
+
displayOptions: { show: { sendHeaders: [true] } },
|
|
53
|
+
options: [{ name: "parameters", displayName: "Header", values: [
|
|
54
|
+
{ displayName: "Name", name: "name", type: "string", default: "" },
|
|
55
|
+
{ displayName: "Value", name: "value", type: "string", default: "" },
|
|
56
|
+
] }],
|
|
57
|
+
},
|
|
58
|
+
{ displayName: "Send Body", name: "sendBody", type: "boolean", default: false },
|
|
59
|
+
{
|
|
60
|
+
displayName: "Body Content Type", name: "contentType", type: "options",
|
|
61
|
+
displayOptions: { show: { sendBody: [true] } }, default: "json",
|
|
62
|
+
options: [
|
|
63
|
+
{ name: "JSON", value: "json" }, { name: "Form Urlencoded", value: "form-urlencoded" },
|
|
64
|
+
{ name: "Raw", value: "raw" },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
displayName: "Body", name: "body", type: "string",
|
|
69
|
+
displayOptions: { show: { sendBody: [true] } }, default: "", typeOptions: { rows: 4 },
|
|
70
|
+
},
|
|
71
|
+
...throttling_props_1.throttlingProperties,
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
// ── Helper ────────────────────────────────────────────────────────────────────
|
|
8
75
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
|
-
|
|
76
|
+
function readThrottleConfig(ctx) {
|
|
77
|
+
// Read from first item — throttling config applies to the entire execution
|
|
78
|
+
const throttlingParams = ctx.getNodeParameter("throttling", 0, {});
|
|
79
|
+
return {
|
|
80
|
+
codes: new Set((throttlingParams.throttleCodes ?? ["429"]).map(String)),
|
|
81
|
+
defaultWaitMs: throttlingParams.defaultWaitMs ?? 5000,
|
|
82
|
+
jitterPercent: throttlingParams.jitterPercent ?? 25,
|
|
83
|
+
maxRetries: Math.max(1, throttlingParams.maxThrottleTries ?? 5),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// ── Node class ────────────────────────────────────────────────────────────────
|
|
10
87
|
class HttpRequestThrottled {
|
|
11
88
|
constructor() {
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
name: "httpBasicAuth",
|
|
30
|
-
required: false,
|
|
31
|
-
displayOptions: { show: { authentication: ["basicAuth"] } },
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: "httpHeaderAuth",
|
|
35
|
-
required: false,
|
|
36
|
-
displayOptions: { show: { authentication: ["headerAuth"] } },
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: "oAuth1Api",
|
|
40
|
-
required: false,
|
|
41
|
-
displayOptions: { show: { authentication: ["oAuth1"] } },
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "oAuth2Api",
|
|
45
|
-
required: false,
|
|
46
|
-
displayOptions: { show: { authentication: ["oAuth2"] } },
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
properties: [
|
|
50
|
-
// ── Kern-Properties (minimal, für Shadow-Override vollständig aus Core übernehmen) ──
|
|
51
|
-
{
|
|
52
|
-
displayName: "Method",
|
|
53
|
-
name: "method",
|
|
54
|
-
type: "options",
|
|
55
|
-
options: [
|
|
56
|
-
{ name: "DELETE", value: "DELETE" },
|
|
57
|
-
{ name: "GET", value: "GET" },
|
|
58
|
-
{ name: "HEAD", value: "HEAD" },
|
|
59
|
-
{ name: "OPTIONS", value: "OPTIONS" },
|
|
60
|
-
{ name: "PATCH", value: "PATCH" },
|
|
61
|
-
{ name: "POST", value: "POST" },
|
|
62
|
-
{ name: "PUT", value: "PUT" },
|
|
63
|
-
],
|
|
64
|
-
default: "GET",
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
displayName: "URL",
|
|
68
|
-
name: "url",
|
|
69
|
-
type: "string",
|
|
70
|
-
default: "",
|
|
71
|
-
placeholder: "https://example.com",
|
|
72
|
-
required: true,
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
displayName: "Authentication",
|
|
76
|
-
name: "authentication",
|
|
77
|
-
type: "options",
|
|
78
|
-
options: [
|
|
79
|
-
{ name: "None", value: "none" },
|
|
80
|
-
{ name: "Basic Auth", value: "basicAuth" },
|
|
81
|
-
{ name: "Header Auth", value: "headerAuth" },
|
|
82
|
-
{ name: "OAuth1", value: "oAuth1" },
|
|
83
|
-
{ name: "OAuth2", value: "oAuth2" },
|
|
84
|
-
],
|
|
85
|
-
default: "none",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
displayName: "Send Headers",
|
|
89
|
-
name: "sendHeaders",
|
|
90
|
-
type: "boolean",
|
|
91
|
-
default: false,
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
displayName: "Header Parameters",
|
|
95
|
-
name: "headerParameters",
|
|
96
|
-
type: "fixedCollection",
|
|
97
|
-
typeOptions: { multipleValues: true },
|
|
98
|
-
default: { parameters: [] },
|
|
99
|
-
displayOptions: { show: { sendHeaders: [true] } },
|
|
100
|
-
options: [
|
|
101
|
-
{
|
|
102
|
-
name: "parameters",
|
|
103
|
-
displayName: "Header",
|
|
104
|
-
values: [
|
|
105
|
-
{ displayName: "Name", name: "name", type: "string", default: "" },
|
|
106
|
-
{ displayName: "Value", name: "value", type: "string", default: "" },
|
|
107
|
-
],
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
displayName: "Send Body",
|
|
113
|
-
name: "sendBody",
|
|
114
|
-
type: "boolean",
|
|
115
|
-
default: false,
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
displayName: "Body Content Type",
|
|
119
|
-
name: "contentType",
|
|
120
|
-
type: "options",
|
|
121
|
-
displayOptions: { show: { sendBody: [true] } },
|
|
122
|
-
options: [
|
|
123
|
-
{ name: "JSON", value: "json" },
|
|
124
|
-
{ name: "Form Urlencoded", value: "form-urlencoded" },
|
|
125
|
-
{ name: "Raw", value: "raw" },
|
|
126
|
-
],
|
|
127
|
-
default: "json",
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
displayName: "Body",
|
|
131
|
-
name: "body",
|
|
132
|
-
type: "string",
|
|
133
|
-
displayOptions: { show: { sendBody: [true] } },
|
|
134
|
-
default: "",
|
|
135
|
-
typeOptions: { rows: 4 },
|
|
136
|
-
},
|
|
137
|
-
// ── Throttling-Erweiterung ────────────────────────────────────────────────
|
|
138
|
-
{
|
|
139
|
-
displayName: "Throttling aktivieren",
|
|
140
|
-
name: "throttlingEnabled",
|
|
141
|
-
type: "boolean",
|
|
142
|
-
default: true,
|
|
143
|
-
description: "Wartet automatisch bei Rate-Limit-Antworten (429 etc.) und wertet Response-Header aus",
|
|
144
|
-
noDataExpression: true,
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
displayName: "Throttling-Einstellungen",
|
|
148
|
-
name: "throttling",
|
|
149
|
-
type: "collection",
|
|
150
|
-
placeholder: "Einstellung hinzufügen",
|
|
151
|
-
default: {},
|
|
152
|
-
displayOptions: { show: { throttlingEnabled: [true] } },
|
|
153
|
-
options: [
|
|
154
|
-
{
|
|
155
|
-
displayName: "HTTP-Codes",
|
|
156
|
-
name: "throttleCodes",
|
|
157
|
-
type: "multiOptions",
|
|
158
|
-
default: ["429"],
|
|
159
|
-
description: "Bei diesen HTTP-Statuscodes wird Throttling ausgelöst",
|
|
160
|
-
options: [
|
|
161
|
-
{ name: "429 Too Many Requests", value: "429" },
|
|
162
|
-
{ name: "503 Service Unavailable", value: "503" },
|
|
163
|
-
{ name: "504 Gateway Timeout", value: "504" },
|
|
164
|
-
],
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
displayName: "Standard-Wartezeit (ms)",
|
|
168
|
-
name: "defaultWaitMs",
|
|
169
|
-
type: "number",
|
|
170
|
-
default: 10000,
|
|
171
|
-
description: "Wartezeit in Millisekunden, wenn kein passender Response-Header vorhanden ist",
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
displayName: "Zufällige Abweichung (±%)",
|
|
175
|
-
name: "jitterPercent",
|
|
176
|
-
type: "number",
|
|
177
|
-
default: 25,
|
|
178
|
-
description: "Streut die Wartezeit um ±N%, um Thundering-Herd-Effekte bei parallelen Executions zu vermeiden",
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
displayName: "Max. Throttle-Versuche",
|
|
182
|
-
name: "maxThrottleTries",
|
|
183
|
-
type: "number",
|
|
184
|
-
default: 10,
|
|
185
|
-
description: "Maximale Anzahl Throttling-Retries bevor ein Fehler geworfen wird",
|
|
186
|
-
},
|
|
187
|
-
],
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
};
|
|
89
|
+
if (v3Ref) {
|
|
90
|
+
// Inherit everything from V3, override identity + append throttling
|
|
91
|
+
this.description = {
|
|
92
|
+
...v3Ref.description,
|
|
93
|
+
name: "httpRequestThrottled",
|
|
94
|
+
displayName: "HTTP Request (Throttled)",
|
|
95
|
+
icon: "fa:globe",
|
|
96
|
+
defaults: { name: "HTTP Request (Throttled)", color: "#FF8500" },
|
|
97
|
+
properties: [
|
|
98
|
+
...v3Ref.description.properties,
|
|
99
|
+
...throttling_props_1.throttlingProperties,
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.description = fallbackDescription;
|
|
105
|
+
}
|
|
191
106
|
}
|
|
192
107
|
async execute() {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const method = this.getNodeParameter("method", itemIndex, "GET");
|
|
206
|
-
const url = this.getNodeParameter("url", itemIndex);
|
|
207
|
-
const sendHeaders = this.getNodeParameter("sendHeaders", itemIndex, false);
|
|
208
|
-
const sendBody = this.getNodeParameter("sendBody", itemIndex, false);
|
|
209
|
-
// v2: IHttpRequestOptions – kein freies Objekt mehr
|
|
210
|
-
const requestOptions = {
|
|
211
|
-
method: method,
|
|
212
|
-
url,
|
|
213
|
-
returnFullResponse: true, // v2: Gibt { statusCode, headers, body } zurück
|
|
214
|
-
ignoreHttpStatusErrors: true, // verhindert automatisches Throw bei 4xx/5xx
|
|
215
|
-
headers: {},
|
|
216
|
-
};
|
|
217
|
-
if (sendHeaders) {
|
|
218
|
-
const headerParams = this.getNodeParameter("headerParameters.parameters", itemIndex, []);
|
|
219
|
-
for (const h of headerParams) {
|
|
220
|
-
requestOptions.headers[h.name] = h.value;
|
|
221
|
-
}
|
|
108
|
+
let throttlingEnabled;
|
|
109
|
+
try {
|
|
110
|
+
throttlingEnabled = this.getNodeParameter("throttlingEnabled", 0, true);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
throttlingEnabled = true;
|
|
114
|
+
}
|
|
115
|
+
// ── V3 path: delegate to the original node with throttled helpers ──────
|
|
116
|
+
if (v3Ref?.execute) {
|
|
117
|
+
if (throttlingEnabled) {
|
|
118
|
+
const config = readThrottleConfig(this);
|
|
119
|
+
(0, throttle_wrapper_1.wrapHelpersWithThrottling)(this, config);
|
|
222
120
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
121
|
+
return (await v3Ref.execute.call(this));
|
|
122
|
+
}
|
|
123
|
+
// ── Fallback path: minimal implementation ─────────────────────────────
|
|
124
|
+
return fallbackExecute.call(this, throttlingEnabled);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.HttpRequestThrottled = HttpRequestThrottled;
|
|
128
|
+
// ── Fallback execute (when n8n-nodes-base is not available) ───────────────────
|
|
129
|
+
async function fallbackExecute(throttlingEnabled) {
|
|
130
|
+
const items = this.getInputData();
|
|
131
|
+
const returnData = [];
|
|
132
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
133
|
+
const throttlingParams = throttlingEnabled
|
|
134
|
+
? this.getNodeParameter("throttling", itemIndex, {})
|
|
135
|
+
: {};
|
|
136
|
+
const throttleCodes = new Set((throttlingParams.throttleCodes ?? ["429"]).map(String));
|
|
137
|
+
const defaultWaitMs = throttlingParams.defaultWaitMs ?? 5000;
|
|
138
|
+
const jitterPercent = throttlingParams.jitterPercent ?? 25;
|
|
139
|
+
const maxThrottleTries = Math.max(1, throttlingParams.maxThrottleTries ?? 5);
|
|
140
|
+
const method = this.getNodeParameter("method", itemIndex, "GET");
|
|
141
|
+
const url = this.getNodeParameter("url", itemIndex);
|
|
142
|
+
const sendHeaders = this.getNodeParameter("sendHeaders", itemIndex, false);
|
|
143
|
+
const sendBody = this.getNodeParameter("sendBody", itemIndex, false);
|
|
144
|
+
const requestOptions = {
|
|
145
|
+
method: method,
|
|
146
|
+
url,
|
|
147
|
+
returnFullResponse: true,
|
|
148
|
+
ignoreHttpStatusErrors: true,
|
|
149
|
+
headers: {},
|
|
150
|
+
};
|
|
151
|
+
if (sendHeaders) {
|
|
152
|
+
const headerParams = this.getNodeParameter("headerParameters.parameters", itemIndex, []);
|
|
153
|
+
for (const h of headerParams) {
|
|
154
|
+
requestOptions.headers[h.name] = h.value;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (sendBody) {
|
|
158
|
+
const contentType = this.getNodeParameter("contentType", itemIndex, "json");
|
|
159
|
+
const bodyRaw = this.getNodeParameter("body", itemIndex, "");
|
|
160
|
+
if (contentType === "json") {
|
|
161
|
+
try {
|
|
162
|
+
requestOptions.body = JSON.parse(bodyRaw);
|
|
235
163
|
}
|
|
236
|
-
|
|
164
|
+
catch {
|
|
237
165
|
requestOptions.body = bodyRaw;
|
|
238
166
|
}
|
|
167
|
+
requestOptions.headers["content-type"] = "application/json";
|
|
239
168
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
|
-
response = await this.helpers.httpRequestWithAuthentication(credMap[authentication] ?? authentication, requestOptions);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
// v2: this.helpers.httpRequest
|
|
261
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
262
|
-
response = await this.helpers.httpRequest(requestOptions);
|
|
263
|
-
}
|
|
169
|
+
else {
|
|
170
|
+
requestOptions.body = bodyRaw;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let throttleAttempt = 0;
|
|
174
|
+
while (true) {
|
|
175
|
+
const authentication = this.getNodeParameter("authentication", itemIndex, "none");
|
|
176
|
+
let response;
|
|
177
|
+
try {
|
|
178
|
+
if (authentication !== "none") {
|
|
179
|
+
const credMap = {
|
|
180
|
+
basicAuth: "httpBasicAuth", headerAuth: "httpHeaderAuth",
|
|
181
|
+
oAuth1: "oAuth1Api", oAuth2: "oAuth2Api",
|
|
182
|
+
};
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
|
+
response = await this.helpers.httpRequestWithAuthentication(credMap[authentication] ?? authentication, requestOptions);
|
|
264
185
|
}
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
throw err;
|
|
269
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Netzwerkfehler: ${err.message}`, { itemIndex });
|
|
186
|
+
else {
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
+
response = await this.helpers.httpRequest(requestOptions);
|
|
270
189
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
this.logger.info(`[Throttling] Status ${response.statusCode} – Item ${itemIndex}, Versuch ${throttleAttempt}/${maxThrottleTries}, warte ${Math.round(wait)}ms`);
|
|
283
|
-
await sleep(wait);
|
|
284
|
-
continue; // Retry – KEIN Retry-on-Fail
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
if (err instanceof n8n_workflow_1.NodeApiError)
|
|
193
|
+
throw err;
|
|
194
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Network error: ${err.message}`, { itemIndex });
|
|
195
|
+
}
|
|
196
|
+
const statusStr = String(response.statusCode);
|
|
197
|
+
if (throttlingEnabled && throttleCodes.has(statusStr)) {
|
|
198
|
+
throttleAttempt++;
|
|
199
|
+
if (throttleAttempt >= maxThrottleTries) {
|
|
200
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Throttling: max retries (${maxThrottleTries}) exceeded. Last status: ${response.statusCode}`, { itemIndex });
|
|
285
201
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `HTTP ${response.statusCode}: ${JSON.stringify(response.body)}`, { itemIndex });
|
|
202
|
+
const baseWait = (0, throttling_1.computeWaitMs)(response.headers, defaultWaitMs);
|
|
203
|
+
const wait = (0, throttling_1.applyJitter)(baseWait, jitterPercent);
|
|
204
|
+
this.logger.info(`[Throttling] Status ${response.statusCode} – item ${itemIndex}, attempt ${throttleAttempt}/${maxThrottleTries}, waiting ${Math.round(wait)}ms`);
|
|
205
|
+
await sleep(wait);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (response.statusCode >= 400) {
|
|
209
|
+
if (this.continueOnFail()) {
|
|
210
|
+
returnData.push({
|
|
211
|
+
json: { error: `HTTP ${response.statusCode}`, body: response.body },
|
|
212
|
+
pairedItem: { item: itemIndex },
|
|
213
|
+
});
|
|
214
|
+
break;
|
|
300
215
|
}
|
|
301
|
-
|
|
302
|
-
const body = response.body;
|
|
303
|
-
const json = typeof body === "object" && body !== null
|
|
304
|
-
? body
|
|
305
|
-
: { data: body };
|
|
306
|
-
returnData.push({
|
|
307
|
-
json,
|
|
308
|
-
pairedItem: { item: itemIndex },
|
|
309
|
-
});
|
|
310
|
-
break; // Erfolg → Loop verlassen
|
|
216
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `HTTP ${response.statusCode}: ${JSON.stringify(response.body)}`, { itemIndex });
|
|
311
217
|
}
|
|
218
|
+
const body = response.body;
|
|
219
|
+
const json = typeof body === "object" && body !== null
|
|
220
|
+
? body
|
|
221
|
+
: { data: body };
|
|
222
|
+
returnData.push({ json, pairedItem: { item: itemIndex } });
|
|
223
|
+
break;
|
|
312
224
|
}
|
|
313
|
-
return [returnData];
|
|
314
225
|
}
|
|
226
|
+
return [returnData];
|
|
315
227
|
}
|
|
316
|
-
exports.HttpRequestThrottled = HttpRequestThrottled;
|
|
317
228
|
//# sourceMappingURL=HttpRequestThrottled.node.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpRequestThrottled.node.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HttpRequestThrottled.node.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/HttpRequestThrottled.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IExecuteFunctions } from "n8n-workflow";
|
|
2
|
+
export interface ThrottleConfig {
|
|
3
|
+
codes: Set<string>;
|
|
4
|
+
defaultWaitMs: number;
|
|
5
|
+
jitterPercent: number;
|
|
6
|
+
maxRetries: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Wraps `this.helpers.httpRequest` and `this.helpers.httpRequestWithAuthentication`
|
|
10
|
+
* with transparent throttling. When a response has a throttle status code,
|
|
11
|
+
* the wrapper waits and retries automatically. The caller (V3 execute) never
|
|
12
|
+
* sees the throttle response.
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapHelpersWithThrottling(ctx: IExecuteFunctions, config: ThrottleConfig): void;
|
|
15
|
+
//# sourceMappingURL=throttle-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-wrapper.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttle-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,cAAc,CAAC;AAItB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,cAAc,GACrB,IAAI,CAoCN"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapHelpersWithThrottling = wrapHelpersWithThrottling;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const throttling_1 = require("./throttling");
|
|
6
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
/**
|
|
8
|
+
* Wraps `this.helpers.httpRequest` and `this.helpers.httpRequestWithAuthentication`
|
|
9
|
+
* with transparent throttling. When a response has a throttle status code,
|
|
10
|
+
* the wrapper waits and retries automatically. The caller (V3 execute) never
|
|
11
|
+
* sees the throttle response.
|
|
12
|
+
*/
|
|
13
|
+
function wrapHelpersWithThrottling(ctx, config) {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
const helpers = ctx.helpers;
|
|
16
|
+
const originalHttpRequest = helpers.httpRequest.bind(helpers);
|
|
17
|
+
const originalHttpRequestWithAuth = helpers.httpRequestWithAuthentication.bind(ctx);
|
|
18
|
+
helpers.httpRequest = async (requestOptions) => {
|
|
19
|
+
return throttledCall(ctx, config, requestOptions, (opts) => originalHttpRequest(opts));
|
|
20
|
+
};
|
|
21
|
+
helpers.httpRequestWithAuthentication = async (credentialsType, requestOptions, additionalCredentialOptions) => {
|
|
22
|
+
return throttledCall(ctx, config, requestOptions, (opts) => originalHttpRequestWithAuth(credentialsType, opts, additionalCredentialOptions));
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function throttledCall(ctx, config, requestOptions, doRequest) {
|
|
26
|
+
const wantFullResponse = requestOptions.returnFullResponse === true;
|
|
27
|
+
const wantIgnoreErrors = requestOptions.ignoreHttpStatusErrors === true;
|
|
28
|
+
// Force full response so we can inspect the status code
|
|
29
|
+
const patchedOptions = {
|
|
30
|
+
...requestOptions,
|
|
31
|
+
returnFullResponse: true,
|
|
32
|
+
ignoreHttpStatusErrors: true,
|
|
33
|
+
};
|
|
34
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
35
|
+
const response = await doRequest(patchedOptions);
|
|
36
|
+
const statusCode = typeof response === "object" && response !== null
|
|
37
|
+
? response.statusCode ?? response.status ?? 200
|
|
38
|
+
: 200;
|
|
39
|
+
const statusStr = String(statusCode);
|
|
40
|
+
if (config.codes.has(statusStr)) {
|
|
41
|
+
if (attempt >= config.maxRetries) {
|
|
42
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Throttling: max retries (${config.maxRetries}) exceeded. Last status: ${statusCode}`);
|
|
43
|
+
}
|
|
44
|
+
const headers = typeof response === "object" && response !== null
|
|
45
|
+
? response.headers ?? {}
|
|
46
|
+
: {};
|
|
47
|
+
const baseWait = (0, throttling_1.computeWaitMs)(headers, config.defaultWaitMs);
|
|
48
|
+
const wait = (0, throttling_1.applyJitter)(baseWait, config.jitterPercent);
|
|
49
|
+
ctx.logger.info(`[Throttling] Status ${statusCode}, attempt ${attempt + 1}/${config.maxRetries}, waiting ${Math.round(wait)}ms`);
|
|
50
|
+
await sleep(wait);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Not a throttle response — restore original behavior
|
|
54
|
+
if (!wantIgnoreErrors && statusCode >= 400) {
|
|
55
|
+
// The original httpRequest would have thrown; re-throw with original options
|
|
56
|
+
// so n8n's error formatting applies
|
|
57
|
+
return doRequest(requestOptions);
|
|
58
|
+
}
|
|
59
|
+
if (!wantFullResponse) {
|
|
60
|
+
// Caller only wanted the body
|
|
61
|
+
return typeof response === "object" && response !== null
|
|
62
|
+
? response.body
|
|
63
|
+
: response;
|
|
64
|
+
}
|
|
65
|
+
return response;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=throttle-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-wrapper.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttle-wrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttling-props.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttling-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,eAAO,MAAM,oBAAoB,EAAE,eAAe,EAwDjD,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.throttlingProperties = void 0;
|
|
4
|
+
exports.throttlingProperties = [
|
|
5
|
+
{
|
|
6
|
+
displayName: "Enable Throttling",
|
|
7
|
+
name: "throttlingEnabled",
|
|
8
|
+
type: "boolean",
|
|
9
|
+
default: true,
|
|
10
|
+
description: "Automatically wait and retry on rate-limit responses (429 etc.) using response headers",
|
|
11
|
+
noDataExpression: true,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
displayName: "Throttling Settings",
|
|
15
|
+
name: "throttling",
|
|
16
|
+
type: "collection",
|
|
17
|
+
placeholder: "Add Setting",
|
|
18
|
+
default: {},
|
|
19
|
+
displayOptions: { show: { throttlingEnabled: [true] } },
|
|
20
|
+
options: [
|
|
21
|
+
{
|
|
22
|
+
displayName: "HTTP Codes",
|
|
23
|
+
name: "throttleCodes",
|
|
24
|
+
type: "multiOptions",
|
|
25
|
+
default: ["429"],
|
|
26
|
+
description: "HTTP status codes that trigger throttling",
|
|
27
|
+
options: [
|
|
28
|
+
{ name: "429 Too Many Requests", value: "429" },
|
|
29
|
+
{ name: "503 Service Unavailable", value: "503" },
|
|
30
|
+
{ name: "504 Gateway Timeout", value: "504" },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
displayName: "Default Wait Time (ms)",
|
|
35
|
+
name: "defaultWaitMs",
|
|
36
|
+
type: "number",
|
|
37
|
+
default: 5000,
|
|
38
|
+
description: "Wait time in milliseconds when no response header provides guidance",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
displayName: "Random Jitter (±%)",
|
|
42
|
+
name: "jitterPercent",
|
|
43
|
+
type: "number",
|
|
44
|
+
default: 25,
|
|
45
|
+
description: "Randomize wait time by ±N% to prevent thundering herd effects across parallel executions",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
displayName: "Max Throttle Retries",
|
|
49
|
+
name: "maxThrottleTries",
|
|
50
|
+
type: "number",
|
|
51
|
+
default: 5,
|
|
52
|
+
description: "Maximum number of throttling retries before throwing an error",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
//# sourceMappingURL=throttling-props.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttling-props.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/throttling-props.ts"],"names":[],"mappings":";;;AAEa,QAAA,oBAAoB,GAAsB;IACrD;QACE,WAAW,EAAE,mBAAmB;QAChC,IAAI,EAAE,mBAAmB;QACzB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;QACb,WAAW,EACT,wFAAwF;QAC1F,gBAAgB,EAAE,IAAI;KACvB;IACD;QACE,WAAW,EAAE,qBAAqB;QAClC,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,aAAa;QAC1B,OAAO,EAAE,EAAE;QACX,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,iBAAiB,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE;QACvD,OAAO,EAAE;YACP;gBACE,WAAW,EAAE,YAAY;gBACzB,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,CAAC,KAAK,CAAC;gBAChB,WAAW,EAAE,2CAA2C;gBACxD,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,KAAK,EAAE;oBAC/C,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,KAAK,EAAE;oBACjD,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,KAAK,EAAE;iBAC9C;aACF;YACD;gBACE,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,IAAK;gBACd,WAAW,EACT,qEAAqE;aACxE;YACD;gBACE,WAAW,EAAE,oBAAoB;gBACjC,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EACT,0FAA0F;aAC7F;YACD;gBACE,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,kBAAkB;gBACxB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,CAAC;gBACV,WAAW,EACT,+DAA+D;aAClE;SACF;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"properties": {
|
|
3
|
+
"throttlingEnabled": {
|
|
4
|
+
"displayName": "Throttling aktivieren",
|
|
5
|
+
"description": "Wartet automatisch bei Rate-Limit-Antworten (429 etc.) und wertet Response-Header aus"
|
|
6
|
+
},
|
|
7
|
+
"throttling": {
|
|
8
|
+
"displayName": "Throttling-Einstellungen",
|
|
9
|
+
"placeholder": "Einstellung hinzufügen",
|
|
10
|
+
"options": {
|
|
11
|
+
"throttleCodes": {
|
|
12
|
+
"displayName": "HTTP-Codes",
|
|
13
|
+
"description": "Bei diesen HTTP-Statuscodes wird Throttling ausgelöst"
|
|
14
|
+
},
|
|
15
|
+
"defaultWaitMs": {
|
|
16
|
+
"displayName": "Standard-Wartezeit (ms)",
|
|
17
|
+
"description": "Wartezeit in Millisekunden, wenn kein passender Response-Header vorhanden ist"
|
|
18
|
+
},
|
|
19
|
+
"jitterPercent": {
|
|
20
|
+
"displayName": "Zufällige Abweichung (±%)",
|
|
21
|
+
"description": "Streut die Wartezeit um ±N%, um Thundering-Herd-Effekte bei parallelen Executions zu vermeiden"
|
|
22
|
+
},
|
|
23
|
+
"maxThrottleTries": {
|
|
24
|
+
"displayName": "Max. Throttle-Versuche",
|
|
25
|
+
"description": "Maximale Anzahl Throttling-Retries bevor ein Fehler geworfen wird"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { INodeTypeDescription, IExecuteFunctions, INodeExecutionData } from "n8n-workflow";
|
|
2
|
+
export interface V3NodeRef {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute: (this: IExecuteFunctions) => Promise<INodeExecutionData[][] | null>;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadV3Node(): V3NodeRef | null;
|
|
7
|
+
//# sourceMappingURL=v3-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-loader.d.ts","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/v3-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,oBAAoB,CAAC;IAClC,OAAO,EAAE,CACP,IAAI,EAAE,iBAAiB,KACpB,OAAO,CAAC,kBAAkB,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;CAC7C;AAQD,wBAAgB,UAAU,IAAI,SAAS,GAAG,IAAI,CAqB7C"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadV3Node = loadV3Node;
|
|
4
|
+
// Paths to try, ordered from newest n8n layout to oldest
|
|
5
|
+
const V3_REQUIRE_PATHS = [
|
|
6
|
+
"n8n-nodes-base/dist/nodes/HttpRequest/V3/HttpRequestV3.node",
|
|
7
|
+
"n8n-nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node",
|
|
8
|
+
];
|
|
9
|
+
function loadV3Node() {
|
|
10
|
+
for (const modulePath of V3_REQUIRE_PATHS) {
|
|
11
|
+
try {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
+
const mod = require(modulePath);
|
|
14
|
+
const Ctor = mod.HttpRequestV3;
|
|
15
|
+
if (!Ctor)
|
|
16
|
+
continue;
|
|
17
|
+
const instance = new Ctor();
|
|
18
|
+
if (!instance.description || !instance.execute)
|
|
19
|
+
continue;
|
|
20
|
+
return {
|
|
21
|
+
description: instance.description,
|
|
22
|
+
execute: instance.execute,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Path not found in this n8n installation, try next
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=v3-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-loader.js","sourceRoot":"","sources":["../../../src/nodes/HttpRequest/v3-loader.ts"],"names":[],"mappings":";;AAoBA,gCAqBC;AA3BD,yDAAyD;AACzD,MAAM,gBAAgB,GAAG;IACvB,6DAA6D;IAC7D,wDAAwD;CACzD,CAAC;AAEF,SAAgB,UAAU;IACxB,KAAK,MAAM,UAAU,IAAI,gBAAgB,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,8DAA8D;YAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,GAAG,CAAC,aAAoC,CAAC;YACtD,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,SAAS;YAEzD,OAAO;gBACL,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,OAAO,EAAE,QAAQ,CAAC,OAA+B;aAClD,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;QACtD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bauer-group/n8n-nodes-http-throttled-request",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "n8n community node with built-in HTTP request throttling (rate limiting)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
"main": "dist/nodes/HttpRequest/HttpRequestThrottled.node.js",
|
|
11
11
|
"n8n": {
|
|
12
12
|
"nodes": [
|
|
13
|
-
|
|
14
|
-
"file": "dist/nodes/HttpRequest/HttpRequestThrottled.node.js"
|
|
15
|
-
}
|
|
13
|
+
"dist/nodes/HttpRequest/HttpRequestThrottled.node.js"
|
|
16
14
|
]
|
|
17
15
|
},
|
|
18
16
|
"files": [
|
|
@@ -21,13 +19,20 @@
|
|
|
21
19
|
"README.md",
|
|
22
20
|
"LICENSE"
|
|
23
21
|
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"n8n-community-node-package",
|
|
24
|
+
"n8n",
|
|
25
|
+
"http",
|
|
26
|
+
"throttling",
|
|
27
|
+
"rate-limit"
|
|
28
|
+
],
|
|
24
29
|
"publishConfig": {
|
|
25
30
|
"access": "public",
|
|
26
31
|
"registry": "https://registry.npmjs.org/"
|
|
27
32
|
},
|
|
28
33
|
"scripts": {
|
|
29
34
|
"clean": "rimraf dist",
|
|
30
|
-
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"build": "tsc -p tsconfig.json && cp -r src/nodes/HttpRequest/translations dist/nodes/HttpRequest/",
|
|
31
36
|
"rebuild": "npm run clean && npm run build",
|
|
32
37
|
"test": "jest",
|
|
33
38
|
"prepublishOnly": "npm run build"
|