reqon-dsl 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/.claude/settings.local.json +31 -0
- package/.claude/skills/api-integration.md +125 -0
- package/.claude/skills/database-schema.md +51 -0
- package/.claude/skills/dsl-design.md +80 -0
- package/.claude/skills/property-testing.md +143 -0
- package/.claude/skills/reqon/SKILL.md +44 -0
- package/.claude/skills/reqon/references/examples.md +206 -0
- package/.claude/skills/reqon/references/syntax.md +263 -0
- package/.claude/skills/vscode-extension.md +113 -0
- package/.github/dependabot.yml +32 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/ci.yml +174 -0
- package/.github/workflows/release.yml +73 -0
- package/CLAUDE.md +72 -0
- package/CONTRIBUTING.md +161 -0
- package/README.md +235 -0
- package/TODO.md +51 -0
- package/dist/ast/index.d.ts +1 -0
- package/dist/ast/index.js +1 -0
- package/dist/ast/nodes.d.ts +237 -0
- package/dist/ast/nodes.js +12 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +255 -0
- package/dist/auth/circuit-breaker.d.ts +115 -0
- package/dist/auth/circuit-breaker.js +267 -0
- package/dist/auth/credentials.d.ts +91 -0
- package/dist/auth/credentials.js +169 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/oauth2-provider.d.ts +41 -0
- package/dist/auth/oauth2-provider.js +131 -0
- package/dist/auth/rate-limiter.d.ts +61 -0
- package/dist/auth/rate-limiter.js +380 -0
- package/dist/auth/token-store.d.ts +30 -0
- package/dist/auth/token-store.js +148 -0
- package/dist/auth/types.d.ts +142 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +270 -0
- package/dist/errors/errors.test.d.ts +1 -0
- package/dist/errors/errors.test.js +165 -0
- package/dist/errors/index.d.ts +83 -0
- package/dist/errors/index.js +159 -0
- package/dist/execution/execution.test.d.ts +1 -0
- package/dist/execution/execution.test.js +246 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/state.d.ts +136 -0
- package/dist/execution/state.js +82 -0
- package/dist/execution/store.d.ts +52 -0
- package/dist/execution/store.js +120 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +57 -0
- package/dist/integration.test.d.ts +1 -0
- package/dist/integration.test.js +168 -0
- package/dist/interpreter/context.d.ts +15 -0
- package/dist/interpreter/context.js +29 -0
- package/dist/interpreter/evaluator.d.ts +5 -0
- package/dist/interpreter/evaluator.js +223 -0
- package/dist/interpreter/evaluator.test.d.ts +1 -0
- package/dist/interpreter/evaluator.test.js +512 -0
- package/dist/interpreter/executor.d.ts +131 -0
- package/dist/interpreter/executor.js +663 -0
- package/dist/interpreter/fetch-handler.d.ts +43 -0
- package/dist/interpreter/fetch-handler.js +203 -0
- package/dist/interpreter/http.d.ts +57 -0
- package/dist/interpreter/http.js +210 -0
- package/dist/interpreter/http.test.d.ts +1 -0
- package/dist/interpreter/http.test.js +299 -0
- package/dist/interpreter/index.d.ts +7 -0
- package/dist/interpreter/index.js +7 -0
- package/dist/interpreter/pagination.d.ts +63 -0
- package/dist/interpreter/pagination.js +155 -0
- package/dist/interpreter/progress.test.d.ts +1 -0
- package/dist/interpreter/progress.test.js +216 -0
- package/dist/interpreter/schema-matcher.d.ts +16 -0
- package/dist/interpreter/schema-matcher.js +136 -0
- package/dist/interpreter/schema-matcher.test.d.ts +1 -0
- package/dist/interpreter/schema-matcher.test.js +122 -0
- package/dist/interpreter/signals.d.ts +57 -0
- package/dist/interpreter/signals.js +73 -0
- package/dist/interpreter/step-handlers/for-handler.d.ts +17 -0
- package/dist/interpreter/step-handlers/for-handler.js +51 -0
- package/dist/interpreter/step-handlers/index.d.ts +8 -0
- package/dist/interpreter/step-handlers/index.js +8 -0
- package/dist/interpreter/step-handlers/map-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/map-handler.js +20 -0
- package/dist/interpreter/step-handlers/match-handler.d.ts +27 -0
- package/dist/interpreter/step-handlers/match-handler.js +61 -0
- package/dist/interpreter/step-handlers/store-handler.d.ts +13 -0
- package/dist/interpreter/step-handlers/store-handler.js +66 -0
- package/dist/interpreter/step-handlers/types.d.ts +15 -0
- package/dist/interpreter/step-handlers/types.js +1 -0
- package/dist/interpreter/step-handlers/validate-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/validate-handler.js +26 -0
- package/dist/interpreter/step-handlers/webhook-handler.d.ts +36 -0
- package/dist/interpreter/step-handlers/webhook-handler.js +104 -0
- package/dist/lexer/index.d.ts +10 -0
- package/dist/lexer/index.js +12 -0
- package/dist/lexer/lexer.d.ts +24 -0
- package/dist/lexer/lexer.js +264 -0
- package/dist/lexer/lexer.test.d.ts +1 -0
- package/dist/lexer/lexer.test.js +259 -0
- package/dist/lexer/tokens.d.ts +69 -0
- package/dist/lexer/tokens.js +146 -0
- package/dist/loader/index.d.ts +36 -0
- package/dist/loader/index.js +220 -0
- package/dist/loader/loader.test.d.ts +1 -0
- package/dist/loader/loader.test.js +287 -0
- package/dist/oas/index.d.ts +4 -0
- package/dist/oas/index.js +2 -0
- package/dist/oas/loader.d.ts +21 -0
- package/dist/oas/loader.js +82 -0
- package/dist/oas/oas.test.d.ts +1 -0
- package/dist/oas/oas.test.js +218 -0
- package/dist/oas/validator.d.ts +12 -0
- package/dist/oas/validator.js +227 -0
- package/dist/parser/base.d.ts +33 -0
- package/dist/parser/base.js +97 -0
- package/dist/parser/expressions.d.ts +27 -0
- package/dist/parser/expressions.js +248 -0
- package/dist/parser/expressions.test.d.ts +1 -0
- package/dist/parser/expressions.test.js +378 -0
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.js +3 -0
- package/dist/parser/match.test.d.ts +1 -0
- package/dist/parser/match.test.js +254 -0
- package/dist/parser/parser.d.ts +68 -0
- package/dist/parser/parser.js +1229 -0
- package/dist/parser/parser.test.d.ts +1 -0
- package/dist/parser/parser.test.js +333 -0
- package/dist/parser/schedule.test.d.ts +1 -0
- package/dist/parser/schedule.test.js +241 -0
- package/dist/plugin.d.ts +35 -0
- package/dist/plugin.js +68 -0
- package/dist/scheduler/cron-parser.d.ts +32 -0
- package/dist/scheduler/cron-parser.js +198 -0
- package/dist/scheduler/cron-parser.test.d.ts +1 -0
- package/dist/scheduler/cron-parser.test.js +188 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/scheduler.d.ts +81 -0
- package/dist/scheduler/scheduler.js +376 -0
- package/dist/scheduler/types.d.ts +65 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/stores/factory.d.ts +36 -0
- package/dist/stores/factory.js +73 -0
- package/dist/stores/file.d.ts +60 -0
- package/dist/stores/file.js +173 -0
- package/dist/stores/file.test.d.ts +1 -0
- package/dist/stores/file.test.js +165 -0
- package/dist/stores/index.d.ts +6 -0
- package/dist/stores/index.js +5 -0
- package/dist/stores/memory.d.ts +19 -0
- package/dist/stores/memory.js +51 -0
- package/dist/stores/memory.test.d.ts +1 -0
- package/dist/stores/memory.test.js +157 -0
- package/dist/stores/postgrest.d.ts +55 -0
- package/dist/stores/postgrest.js +217 -0
- package/dist/stores/stores.test.d.ts +1 -0
- package/dist/stores/stores.test.js +158 -0
- package/dist/stores/types.d.ts +31 -0
- package/dist/stores/types.js +26 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/state.d.ts +69 -0
- package/dist/sync/state.js +66 -0
- package/dist/sync/store.d.ts +49 -0
- package/dist/sync/store.js +93 -0
- package/dist/sync/sync.test.d.ts +1 -0
- package/dist/sync/sync.test.js +221 -0
- package/dist/utils/async.d.ts +7 -0
- package/dist/utils/async.js +9 -0
- package/dist/utils/file.d.ts +38 -0
- package/dist/utils/file.js +92 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/path.d.ts +12 -0
- package/dist/utils/path.js +41 -0
- package/dist/webhook/index.d.ts +8 -0
- package/dist/webhook/index.js +7 -0
- package/dist/webhook/server.d.ts +84 -0
- package/dist/webhook/server.js +319 -0
- package/dist/webhook/store.d.ts +67 -0
- package/dist/webhook/store.js +193 -0
- package/dist/webhook/types.d.ts +88 -0
- package/dist/webhook/types.js +6 -0
- package/docusaurus/README.md +41 -0
- package/docusaurus/docs/advanced/execution-state.md +283 -0
- package/docusaurus/docs/advanced/extending-reqon.md +388 -0
- package/docusaurus/docs/advanced/multi-file-missions.md +250 -0
- package/docusaurus/docs/advanced/parallel-execution.md +353 -0
- package/docusaurus/docs/api-reference.md +443 -0
- package/docusaurus/docs/authentication/api-key.md +339 -0
- package/docusaurus/docs/authentication/basic.md +276 -0
- package/docusaurus/docs/authentication/bearer.md +282 -0
- package/docusaurus/docs/authentication/oauth2.md +317 -0
- package/docusaurus/docs/authentication/overview.md +251 -0
- package/docusaurus/docs/cli.md +229 -0
- package/docusaurus/docs/core-concepts/actions.md +286 -0
- package/docusaurus/docs/core-concepts/missions.md +264 -0
- package/docusaurus/docs/core-concepts/schemas.md +353 -0
- package/docusaurus/docs/core-concepts/sources.md +339 -0
- package/docusaurus/docs/core-concepts/stores.md +332 -0
- package/docusaurus/docs/dsl-syntax/expressions.md +361 -0
- package/docusaurus/docs/dsl-syntax/fetch.md +293 -0
- package/docusaurus/docs/dsl-syntax/for-loops.md +324 -0
- package/docusaurus/docs/dsl-syntax/map.md +345 -0
- package/docusaurus/docs/dsl-syntax/match.md +387 -0
- package/docusaurus/docs/dsl-syntax/pipelines.md +397 -0
- package/docusaurus/docs/dsl-syntax/validate.md +401 -0
- package/docusaurus/docs/error-handling/dead-letter-queues.md +399 -0
- package/docusaurus/docs/error-handling/flow-control.md +337 -0
- package/docusaurus/docs/error-handling/retry-strategies.md +368 -0
- package/docusaurus/docs/examples.md +488 -0
- package/docusaurus/docs/getting-started.md +256 -0
- package/docusaurus/docs/http/circuit-breaker.md +401 -0
- package/docusaurus/docs/http/incremental-sync.md +394 -0
- package/docusaurus/docs/http/pagination.md +361 -0
- package/docusaurus/docs/http/rate-limiting.md +383 -0
- package/docusaurus/docs/http/requests.md +328 -0
- package/docusaurus/docs/http/retry.md +402 -0
- package/docusaurus/docs/intro.md +90 -0
- package/docusaurus/docs/openapi/loading-specs.md +305 -0
- package/docusaurus/docs/openapi/operation-calls.md +314 -0
- package/docusaurus/docs/openapi/overview.md +212 -0
- package/docusaurus/docs/openapi/response-validation.md +344 -0
- package/docusaurus/docs/scheduling/cron.md +305 -0
- package/docusaurus/docs/scheduling/daemon-mode.md +317 -0
- package/docusaurus/docs/scheduling/intervals.md +289 -0
- package/docusaurus/docs/scheduling/overview.md +231 -0
- package/docusaurus/docs/stores/custom-adapters.md +376 -0
- package/docusaurus/docs/stores/file.md +236 -0
- package/docusaurus/docs/stores/memory.md +193 -0
- package/docusaurus/docs/stores/overview.md +274 -0
- package/docusaurus/docs/stores/postgrest.md +316 -0
- package/docusaurus/docusaurus.config.ts +148 -0
- package/docusaurus/package-lock.json +18029 -0
- package/docusaurus/package.json +47 -0
- package/docusaurus/sidebars.ts +155 -0
- package/docusaurus/src/components/HomepageFeatures/index.tsx +105 -0
- package/docusaurus/src/components/HomepageFeatures/styles.module.css +12 -0
- package/docusaurus/src/css/custom.css +169 -0
- package/docusaurus/src/pages/index.module.css +48 -0
- package/docusaurus/src/pages/index.tsx +110 -0
- package/docusaurus/src/pages/markdown-page.md +7 -0
- package/docusaurus/static/.nojekyll +0 -0
- package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
- package/docusaurus/static/img/docusaurus.png +0 -0
- package/docusaurus/static/img/favicon.ico +0 -0
- package/docusaurus/static/img/logo.svg +10 -0
- package/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
- package/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
- package/docusaurus/tsconfig.json +8 -0
- package/examples/README.md +112 -0
- package/examples/error-handling/README.md +150 -0
- package/examples/error-handling/payment-processor.vague +287 -0
- package/examples/github-sync/README.md +74 -0
- package/examples/github-sync/fetch-issues.vague +47 -0
- package/examples/github-sync/fetch-prs.vague +40 -0
- package/examples/github-sync/mission.vague +101 -0
- package/examples/github-sync/normalize.vague +70 -0
- package/examples/jsonplaceholder/README.md +28 -0
- package/examples/jsonplaceholder/posts.vague +48 -0
- package/examples/petstore/README.md +35 -0
- package/examples/petstore/openapi.yaml +97 -0
- package/examples/petstore/sync.vague +52 -0
- package/examples/temporal-comparison/README.md +297 -0
- package/examples/temporal-comparison/reconciliation.vague +355 -0
- package/examples/temporal-comparison/temporal/activities/index.ts +8 -0
- package/examples/temporal-comparison/temporal/activities/shipstation.ts +225 -0
- package/examples/temporal-comparison/temporal/activities/shopify.ts +257 -0
- package/examples/temporal-comparison/temporal/activities/storage.ts +198 -0
- package/examples/temporal-comparison/temporal/activities/stripe.ts +169 -0
- package/examples/temporal-comparison/temporal/activities/validation.ts +205 -0
- package/examples/temporal-comparison/temporal/client/schedule.ts +218 -0
- package/examples/temporal-comparison/temporal/config/retry.ts +63 -0
- package/examples/temporal-comparison/temporal/types/index.ts +129 -0
- package/examples/temporal-comparison/temporal/workers/main.ts +130 -0
- package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +262 -0
- package/examples/xero/README.md +88 -0
- package/examples/xero/invoices.vague +189 -0
- package/package.json +40 -0
- package/src/api-integration.test.ts +954 -0
- package/src/ast/index.ts +1 -0
- package/src/ast/nodes.ts +310 -0
- package/src/auth/auth.test.ts +326 -0
- package/src/auth/circuit-breaker.test.ts +390 -0
- package/src/auth/circuit-breaker.ts +379 -0
- package/src/auth/credentials.test.ts +273 -0
- package/src/auth/credentials.ts +246 -0
- package/src/auth/index.ts +40 -0
- package/src/auth/oauth2-provider.ts +177 -0
- package/src/auth/rate-limiter.ts +459 -0
- package/src/auth/token-store.ts +177 -0
- package/src/auth/types.ts +159 -0
- package/src/benchmark/e2e.bench.ts +288 -0
- package/src/benchmark/evaluator.bench.ts +331 -0
- package/src/benchmark/fixtures.ts +295 -0
- package/src/benchmark/index.ts +108 -0
- package/src/benchmark/lexer.bench.ts +69 -0
- package/src/benchmark/parser.bench.ts +103 -0
- package/src/benchmark/resilience.bench.ts +193 -0
- package/src/benchmark/store.bench.ts +147 -0
- package/src/benchmark/utils.ts +230 -0
- package/src/cli.ts +313 -0
- package/src/errors/errors.test.ts +234 -0
- package/src/errors/index.ts +223 -0
- package/src/execution/execution.test.ts +307 -0
- package/src/execution/index.ts +21 -0
- package/src/execution/state.ts +207 -0
- package/src/execution/store.ts +188 -0
- package/src/index.ts +169 -0
- package/src/integration.test.ts +192 -0
- package/src/interpreter/context.ts +57 -0
- package/src/interpreter/evaluator.test.ts +796 -0
- package/src/interpreter/evaluator.ts +245 -0
- package/src/interpreter/executor.ts +946 -0
- package/src/interpreter/fetch-handler.ts +302 -0
- package/src/interpreter/http.test.ts +423 -0
- package/src/interpreter/http.ts +308 -0
- package/src/interpreter/index.ts +32 -0
- package/src/interpreter/pagination.ts +207 -0
- package/src/interpreter/progress.test.ts +276 -0
- package/src/interpreter/schema-matcher.test.ts +160 -0
- package/src/interpreter/schema-matcher.ts +168 -0
- package/src/interpreter/signals.ts +73 -0
- package/src/interpreter/step-handlers/for-handler.ts +65 -0
- package/src/interpreter/step-handlers/index.ts +17 -0
- package/src/interpreter/step-handlers/map-handler.ts +24 -0
- package/src/interpreter/step-handlers/match-handler.ts +101 -0
- package/src/interpreter/step-handlers/store-handler.ts +78 -0
- package/src/interpreter/step-handlers/types.ts +17 -0
- package/src/interpreter/step-handlers/validate-handler.ts +30 -0
- package/src/interpreter/step-handlers/webhook-handler.ts +142 -0
- package/src/lexer/index.ts +18 -0
- package/src/lexer/lexer.test.ts +316 -0
- package/src/lexer/tokens.ts +179 -0
- package/src/loader/index.ts +288 -0
- package/src/loader/loader.test.ts +360 -0
- package/src/oas/index.ts +4 -0
- package/src/oas/loader.ts +126 -0
- package/src/oas/oas.test.ts +254 -0
- package/src/oas/validator.ts +299 -0
- package/src/parser/base.ts +124 -0
- package/src/parser/expressions.test.ts +525 -0
- package/src/parser/expressions.ts +314 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/match.test.ts +296 -0
- package/src/parser/parser.test.ts +739 -0
- package/src/parser/parser.ts +1469 -0
- package/src/parser/schedule.test.ts +287 -0
- package/src/parser/webhook.test.ts +248 -0
- package/src/plugin.ts +83 -0
- package/src/scheduler/cron-parser.test.ts +236 -0
- package/src/scheduler/cron-parser.ts +236 -0
- package/src/scheduler/index.ts +10 -0
- package/src/scheduler/scheduler.ts +443 -0
- package/src/scheduler/types.ts +71 -0
- package/src/stores/factory.ts +104 -0
- package/src/stores/file.test.ts +276 -0
- package/src/stores/file.ts +211 -0
- package/src/stores/index.ts +6 -0
- package/src/stores/memory.test.ts +238 -0
- package/src/stores/memory.ts +63 -0
- package/src/stores/postgrest.test.ts +488 -0
- package/src/stores/postgrest.ts +263 -0
- package/src/stores/stores.test.ts +197 -0
- package/src/stores/types.ts +58 -0
- package/src/sync/index.ts +16 -0
- package/src/sync/state.ts +126 -0
- package/src/sync/store.ts +139 -0
- package/src/sync/sync.test.ts +271 -0
- package/src/utils/async.ts +10 -0
- package/src/utils/file.ts +106 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/logger.ts +53 -0
- package/src/utils/path.ts +47 -0
- package/src/webhook/index.ts +15 -0
- package/src/webhook/server.test.ts +253 -0
- package/src/webhook/server.ts +389 -0
- package/src/webhook/store.ts +239 -0
- package/src/webhook/types.ts +93 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Pagination
|
|
6
|
+
|
|
7
|
+
Reqon provides built-in support for the three most common pagination strategies: offset-based, page number-based, and cursor-based.
|
|
8
|
+
|
|
9
|
+
## Pagination Strategies
|
|
10
|
+
|
|
11
|
+
### Offset-Based Pagination
|
|
12
|
+
|
|
13
|
+
Uses an offset value that increments by page size:
|
|
14
|
+
|
|
15
|
+
```vague
|
|
16
|
+
get "/users" {
|
|
17
|
+
paginate: offset(offset, 100),
|
|
18
|
+
until: length(response.data) == 0
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
How it works:
|
|
23
|
+
- First request: `?offset=0`
|
|
24
|
+
- Second request: `?offset=100`
|
|
25
|
+
- Third request: `?offset=200`
|
|
26
|
+
- ...continues until `until` condition is true
|
|
27
|
+
|
|
28
|
+
Parameters:
|
|
29
|
+
- `offset` - Query parameter name for the offset value
|
|
30
|
+
- `100` - Page size (items per request)
|
|
31
|
+
|
|
32
|
+
### Page Number-Based Pagination
|
|
33
|
+
|
|
34
|
+
Uses a page number starting from 1:
|
|
35
|
+
|
|
36
|
+
```vague
|
|
37
|
+
get "/users" {
|
|
38
|
+
paginate: page(page, 50),
|
|
39
|
+
until: response.meta.hasNext == false
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
How it works:
|
|
44
|
+
- First request: `?page=1`
|
|
45
|
+
- Second request: `?page=2`
|
|
46
|
+
- Third request: `?page=3`
|
|
47
|
+
- ...continues until `until` condition is true
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
- `page` - Query parameter name
|
|
51
|
+
- `50` - Page size
|
|
52
|
+
|
|
53
|
+
### Cursor-Based Pagination
|
|
54
|
+
|
|
55
|
+
Uses a cursor token from the previous response:
|
|
56
|
+
|
|
57
|
+
```vague
|
|
58
|
+
get "/users" {
|
|
59
|
+
paginate: cursor(cursor, 100, "meta.nextCursor"),
|
|
60
|
+
until: response.meta.nextCursor == null
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
How it works:
|
|
65
|
+
- First request: no cursor parameter
|
|
66
|
+
- Response: `{ data: [...], meta: { nextCursor: "abc123" } }`
|
|
67
|
+
- Second request: `?cursor=abc123`
|
|
68
|
+
- ...continues until cursor is null
|
|
69
|
+
|
|
70
|
+
Parameters:
|
|
71
|
+
- `cursor` - Query parameter name
|
|
72
|
+
- `100` - Page size
|
|
73
|
+
- `"meta.nextCursor"` - Path to next cursor in response
|
|
74
|
+
|
|
75
|
+
## Termination Conditions
|
|
76
|
+
|
|
77
|
+
The `until` option specifies when to stop paginating:
|
|
78
|
+
|
|
79
|
+
### Empty Response
|
|
80
|
+
|
|
81
|
+
```vague
|
|
82
|
+
get "/items" {
|
|
83
|
+
paginate: offset(skip, 100),
|
|
84
|
+
until: length(response) == 0
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Empty Data Array
|
|
89
|
+
|
|
90
|
+
```vague
|
|
91
|
+
get "/items" {
|
|
92
|
+
paginate: offset(skip, 100),
|
|
93
|
+
until: length(response.data) == 0
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Boolean Flag
|
|
98
|
+
|
|
99
|
+
```vague
|
|
100
|
+
get "/items" {
|
|
101
|
+
paginate: page(p, 50),
|
|
102
|
+
until: response.hasMore == false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Or
|
|
106
|
+
get "/items" {
|
|
107
|
+
paginate: page(p, 50),
|
|
108
|
+
until: response.pagination.hasNext == false
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Null Cursor
|
|
113
|
+
|
|
114
|
+
```vague
|
|
115
|
+
get "/items" {
|
|
116
|
+
paginate: cursor(after, 100, "cursor.next"),
|
|
117
|
+
until: response.cursor.next == null
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Maximum Pages
|
|
122
|
+
|
|
123
|
+
```vague
|
|
124
|
+
get "/items" {
|
|
125
|
+
paginate: page(p, 100),
|
|
126
|
+
until: length(response.items) == 0 or p > 10
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Item Count Threshold
|
|
131
|
+
|
|
132
|
+
```vague
|
|
133
|
+
get "/items" {
|
|
134
|
+
paginate: offset(skip, 100),
|
|
135
|
+
until: length(response) < 100 // Less than full page
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Combining with Other Options
|
|
140
|
+
|
|
141
|
+
### With Query Parameters
|
|
142
|
+
|
|
143
|
+
```vague
|
|
144
|
+
get "/users" {
|
|
145
|
+
params: {
|
|
146
|
+
status: "active",
|
|
147
|
+
sort: "created_at"
|
|
148
|
+
},
|
|
149
|
+
paginate: offset(offset, 100),
|
|
150
|
+
until: length(response.users) == 0
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### With Retry
|
|
155
|
+
|
|
156
|
+
```vague
|
|
157
|
+
get "/users" {
|
|
158
|
+
paginate: cursor(cursor, 100, "nextCursor"),
|
|
159
|
+
until: response.nextCursor == null,
|
|
160
|
+
retry: {
|
|
161
|
+
maxAttempts: 3,
|
|
162
|
+
backoff: exponential
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### With Incremental Sync
|
|
168
|
+
|
|
169
|
+
```vague
|
|
170
|
+
get "/users" {
|
|
171
|
+
paginate: page(page, 100),
|
|
172
|
+
until: response.hasMore == false,
|
|
173
|
+
since: lastSync
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Processing Paginated Results
|
|
178
|
+
|
|
179
|
+
### Accumulative Processing
|
|
180
|
+
|
|
181
|
+
All pages are accumulated in `response`:
|
|
182
|
+
|
|
183
|
+
```vague
|
|
184
|
+
action FetchAll {
|
|
185
|
+
get "/items" {
|
|
186
|
+
paginate: offset(offset, 100),
|
|
187
|
+
until: length(response) == 0
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// response now contains ALL items from all pages
|
|
191
|
+
for item in response {
|
|
192
|
+
store item -> items { key: .id }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Per-Page Processing
|
|
198
|
+
|
|
199
|
+
Process each page as it arrives:
|
|
200
|
+
|
|
201
|
+
```vague
|
|
202
|
+
action ProcessPages {
|
|
203
|
+
get "/items" {
|
|
204
|
+
paginate: offset(offset, 100),
|
|
205
|
+
until: length(response) == 0
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// For each page fetched, items are accumulated
|
|
209
|
+
// After pagination completes, process all
|
|
210
|
+
for item in response {
|
|
211
|
+
// Each item is processed
|
|
212
|
+
store item -> items { key: .id }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Common API Patterns
|
|
218
|
+
|
|
219
|
+
### Standard REST API
|
|
220
|
+
|
|
221
|
+
```vague
|
|
222
|
+
// API: GET /api/users?limit=100&offset=0
|
|
223
|
+
get "/api/users" {
|
|
224
|
+
params: { limit: 100 },
|
|
225
|
+
paginate: offset(offset, 100),
|
|
226
|
+
until: length(response.data) == 0
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### GraphQL-Style Cursor
|
|
231
|
+
|
|
232
|
+
```vague
|
|
233
|
+
// API uses cursor-based pagination
|
|
234
|
+
get "/api/items" {
|
|
235
|
+
paginate: cursor(after, 50, "pageInfo.endCursor"),
|
|
236
|
+
until: response.pageInfo.hasNextPage == false
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Link Header Pagination
|
|
241
|
+
|
|
242
|
+
For APIs using Link headers, use cursor pagination:
|
|
243
|
+
|
|
244
|
+
```vague
|
|
245
|
+
get "/api/items" {
|
|
246
|
+
paginate: cursor(page, 100, "links.next"),
|
|
247
|
+
until: response.links.next == null
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Best Practices
|
|
252
|
+
|
|
253
|
+
### Choose the Right Strategy
|
|
254
|
+
|
|
255
|
+
| API Type | Recommended Strategy |
|
|
256
|
+
|----------|---------------------|
|
|
257
|
+
| Stable datasets | Offset or Page |
|
|
258
|
+
| Real-time data | Cursor |
|
|
259
|
+
| Large datasets | Cursor |
|
|
260
|
+
| Simple APIs | Page |
|
|
261
|
+
|
|
262
|
+
### Handle Partial Pages
|
|
263
|
+
|
|
264
|
+
```vague
|
|
265
|
+
get "/items" {
|
|
266
|
+
paginate: offset(offset, 100),
|
|
267
|
+
until: length(response.items) < 100 // Partial page = last page
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Set Reasonable Page Sizes
|
|
272
|
+
|
|
273
|
+
```vague
|
|
274
|
+
// Good: reasonable page size
|
|
275
|
+
get "/items" {
|
|
276
|
+
paginate: offset(offset, 100),
|
|
277
|
+
until: length(response) == 0
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Avoid: too large (may timeout or exceed limits)
|
|
281
|
+
get "/items" {
|
|
282
|
+
paginate: offset(offset, 10000),
|
|
283
|
+
until: length(response) == 0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Avoid: too small (too many requests)
|
|
287
|
+
get "/items" {
|
|
288
|
+
paginate: offset(offset, 10),
|
|
289
|
+
until: length(response) == 0
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Add Safety Limits
|
|
294
|
+
|
|
295
|
+
```vague
|
|
296
|
+
get "/items" {
|
|
297
|
+
paginate: page(page, 100),
|
|
298
|
+
until: length(response.items) == 0 or page > 100 // Max 100 pages
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Combine with Rate Limiting
|
|
303
|
+
|
|
304
|
+
```vague
|
|
305
|
+
source API {
|
|
306
|
+
auth: bearer,
|
|
307
|
+
base: "https://api.example.com",
|
|
308
|
+
rateLimit: {
|
|
309
|
+
requestsPerMinute: 60,
|
|
310
|
+
strategy: "pause"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
action FetchAll {
|
|
315
|
+
get "/items" {
|
|
316
|
+
paginate: offset(offset, 100),
|
|
317
|
+
until: length(response) == 0
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Troubleshooting
|
|
323
|
+
|
|
324
|
+
### Infinite Pagination Loop
|
|
325
|
+
|
|
326
|
+
If pagination never stops:
|
|
327
|
+
|
|
328
|
+
```vague
|
|
329
|
+
// Add a safety limit
|
|
330
|
+
get "/items" {
|
|
331
|
+
paginate: page(page, 100),
|
|
332
|
+
until: length(response.data) == 0 or page > 50 // Stop after 50 pages
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Duplicate Items
|
|
337
|
+
|
|
338
|
+
Some APIs return overlapping results. Use upsert:
|
|
339
|
+
|
|
340
|
+
```vague
|
|
341
|
+
get "/items" {
|
|
342
|
+
paginate: cursor(cursor, 100, "next"),
|
|
343
|
+
until: response.next == null
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for item in response {
|
|
347
|
+
store item -> items { key: .id, upsert: true }
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Missing Items
|
|
352
|
+
|
|
353
|
+
If items are being missed, check your termination condition:
|
|
354
|
+
|
|
355
|
+
```vague
|
|
356
|
+
// May miss items if last page has exactly 100 items
|
|
357
|
+
until: length(response.items) == 0
|
|
358
|
+
|
|
359
|
+
// Better: check for less than full page
|
|
360
|
+
until: length(response.items) < 100
|
|
361
|
+
```
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rate Limiting
|
|
6
|
+
|
|
7
|
+
Reqon provides adaptive rate limiting that learns from API responses and respects rate limit headers.
|
|
8
|
+
|
|
9
|
+
## Source-Level Configuration
|
|
10
|
+
|
|
11
|
+
```vague
|
|
12
|
+
source API {
|
|
13
|
+
auth: bearer,
|
|
14
|
+
base: "https://api.example.com",
|
|
15
|
+
rateLimit: {
|
|
16
|
+
requestsPerMinute: 60,
|
|
17
|
+
strategy: "pause"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Rate Limit Options
|
|
23
|
+
|
|
24
|
+
| Option | Description | Default |
|
|
25
|
+
|--------|-------------|---------|
|
|
26
|
+
| `requestsPerMinute` | Maximum requests per minute | 60 |
|
|
27
|
+
| `strategy` | How to handle limits | `"pause"` |
|
|
28
|
+
| `maxWait` | Maximum wait time (ms) | 60000 |
|
|
29
|
+
|
|
30
|
+
## Strategies
|
|
31
|
+
|
|
32
|
+
### Pause Strategy
|
|
33
|
+
|
|
34
|
+
Wait when rate limit is reached:
|
|
35
|
+
|
|
36
|
+
```vague
|
|
37
|
+
source API {
|
|
38
|
+
auth: bearer,
|
|
39
|
+
base: "https://api.example.com",
|
|
40
|
+
rateLimit: {
|
|
41
|
+
requestsPerMinute: 60,
|
|
42
|
+
strategy: "pause"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When limit is reached:
|
|
48
|
+
1. Reqon pauses execution
|
|
49
|
+
2. Waits until rate limit window resets
|
|
50
|
+
3. Continues with next request
|
|
51
|
+
|
|
52
|
+
### Throttle Strategy
|
|
53
|
+
|
|
54
|
+
Slow down requests proactively:
|
|
55
|
+
|
|
56
|
+
```vague
|
|
57
|
+
source API {
|
|
58
|
+
auth: bearer,
|
|
59
|
+
base: "https://api.example.com",
|
|
60
|
+
rateLimit: {
|
|
61
|
+
requestsPerMinute: 60,
|
|
62
|
+
strategy: "throttle"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Automatically spaces requests to stay within limits.
|
|
68
|
+
|
|
69
|
+
### Fail Strategy
|
|
70
|
+
|
|
71
|
+
Throw error when limit is reached:
|
|
72
|
+
|
|
73
|
+
```vague
|
|
74
|
+
source API {
|
|
75
|
+
auth: bearer,
|
|
76
|
+
base: "https://api.example.com",
|
|
77
|
+
rateLimit: {
|
|
78
|
+
requestsPerMinute: 60,
|
|
79
|
+
strategy: "fail"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Use with error handling:
|
|
85
|
+
|
|
86
|
+
```vague
|
|
87
|
+
action FetchWithRateLimitHandling {
|
|
88
|
+
get "/data"
|
|
89
|
+
|
|
90
|
+
match response {
|
|
91
|
+
{ error: "rate_limit" } -> retry { delay: 60000 },
|
|
92
|
+
_ -> continue
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Response Header Support
|
|
98
|
+
|
|
99
|
+
Reqon automatically reads standard rate limit headers:
|
|
100
|
+
|
|
101
|
+
| Header | Description |
|
|
102
|
+
|--------|-------------|
|
|
103
|
+
| `X-RateLimit-Limit` | Maximum requests allowed |
|
|
104
|
+
| `X-RateLimit-Remaining` | Requests remaining in window |
|
|
105
|
+
| `X-RateLimit-Reset` | When the window resets |
|
|
106
|
+
| `Retry-After` | Seconds to wait before retrying |
|
|
107
|
+
|
|
108
|
+
### Header Parsing
|
|
109
|
+
|
|
110
|
+
```http
|
|
111
|
+
HTTP/1.1 429 Too Many Requests
|
|
112
|
+
X-RateLimit-Limit: 100
|
|
113
|
+
X-RateLimit-Remaining: 0
|
|
114
|
+
X-RateLimit-Reset: 1705752000
|
|
115
|
+
Retry-After: 60
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Reqon will automatically:
|
|
119
|
+
1. Pause for 60 seconds (from `Retry-After`)
|
|
120
|
+
2. Update internal limit tracking
|
|
121
|
+
3. Retry the request
|
|
122
|
+
|
|
123
|
+
## Adaptive Rate Limiting
|
|
124
|
+
|
|
125
|
+
Reqon learns from API responses:
|
|
126
|
+
|
|
127
|
+
```vague
|
|
128
|
+
source API {
|
|
129
|
+
auth: bearer,
|
|
130
|
+
base: "https://api.example.com",
|
|
131
|
+
rateLimit: {
|
|
132
|
+
requestsPerMinute: 100, // Initial estimate
|
|
133
|
+
strategy: "pause",
|
|
134
|
+
adaptive: true // Learn from responses
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
With `adaptive: true`:
|
|
140
|
+
- Reqon monitors response headers
|
|
141
|
+
- Adjusts request pacing dynamically
|
|
142
|
+
- Backs off before hitting limits
|
|
143
|
+
|
|
144
|
+
## Per-Endpoint Rate Limits
|
|
145
|
+
|
|
146
|
+
Some APIs have different limits per endpoint:
|
|
147
|
+
|
|
148
|
+
```vague
|
|
149
|
+
mission APISync {
|
|
150
|
+
source API {
|
|
151
|
+
auth: bearer,
|
|
152
|
+
base: "https://api.example.com",
|
|
153
|
+
rateLimit: { requestsPerMinute: 100 }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
action FetchUsers {
|
|
157
|
+
// Standard endpoint - uses default limit
|
|
158
|
+
get "/users"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
action FetchReports {
|
|
162
|
+
// Heavy endpoint - add delay
|
|
163
|
+
get "/reports" {
|
|
164
|
+
rateLimit: { requestsPerMinute: 10 }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Combining with Pagination
|
|
171
|
+
|
|
172
|
+
```vague
|
|
173
|
+
get "/items" {
|
|
174
|
+
paginate: offset(offset, 100),
|
|
175
|
+
until: length(response.items) == 0
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Rate limiting applies to each page request, not just the action.
|
|
180
|
+
|
|
181
|
+
## Combining with Retry
|
|
182
|
+
|
|
183
|
+
```vague
|
|
184
|
+
source API {
|
|
185
|
+
auth: bearer,
|
|
186
|
+
base: "https://api.example.com",
|
|
187
|
+
rateLimit: {
|
|
188
|
+
requestsPerMinute: 60,
|
|
189
|
+
strategy: "pause"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
action Fetch {
|
|
194
|
+
get "/data" {
|
|
195
|
+
retry: {
|
|
196
|
+
maxAttempts: 5,
|
|
197
|
+
backoff: exponential
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Order of operations:
|
|
204
|
+
1. Rate limiter checks if request is allowed
|
|
205
|
+
2. If not, pauses (based on strategy)
|
|
206
|
+
3. Request is made
|
|
207
|
+
4. If fails, retry logic kicks in
|
|
208
|
+
|
|
209
|
+
## Handling 429 Responses
|
|
210
|
+
|
|
211
|
+
Even with rate limiting, you might hit limits. Handle gracefully:
|
|
212
|
+
|
|
213
|
+
```vague
|
|
214
|
+
action RobustFetch {
|
|
215
|
+
get "/data"
|
|
216
|
+
|
|
217
|
+
match response {
|
|
218
|
+
{ code: 429 } -> retry {
|
|
219
|
+
maxAttempts: 5,
|
|
220
|
+
backoff: exponential,
|
|
221
|
+
initialDelay: 60000 // Wait 1 minute
|
|
222
|
+
},
|
|
223
|
+
_ -> continue
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Multiple Sources with Different Limits
|
|
229
|
+
|
|
230
|
+
```vague
|
|
231
|
+
mission MultiSourceSync {
|
|
232
|
+
source HighVolumeAPI {
|
|
233
|
+
auth: bearer,
|
|
234
|
+
base: "https://high-volume.api.com",
|
|
235
|
+
rateLimit: { requestsPerMinute: 1000 }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
source LowVolumeAPI {
|
|
239
|
+
auth: bearer,
|
|
240
|
+
base: "https://limited.api.com",
|
|
241
|
+
rateLimit: { requestsPerMinute: 10 }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
action FetchBoth {
|
|
245
|
+
// These respect their respective limits
|
|
246
|
+
get HighVolumeAPI "/items"
|
|
247
|
+
get LowVolumeAPI "/items"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Monitoring Rate Limits
|
|
253
|
+
|
|
254
|
+
Track rate limit status:
|
|
255
|
+
|
|
256
|
+
```vague
|
|
257
|
+
action MonitoredFetch {
|
|
258
|
+
get "/data"
|
|
259
|
+
|
|
260
|
+
match response {
|
|
261
|
+
{ code: 429, headers: h } -> {
|
|
262
|
+
store {
|
|
263
|
+
endpoint: "/data",
|
|
264
|
+
hitLimit: true,
|
|
265
|
+
retryAfter: h["Retry-After"],
|
|
266
|
+
timestamp: now()
|
|
267
|
+
} -> rateLimitLogs
|
|
268
|
+
retry { delay: h["Retry-After"] * 1000 }
|
|
269
|
+
},
|
|
270
|
+
_ -> continue
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Best Practices
|
|
276
|
+
|
|
277
|
+
### Start Conservative
|
|
278
|
+
|
|
279
|
+
```vague
|
|
280
|
+
// Good: start below the actual limit
|
|
281
|
+
source API {
|
|
282
|
+
rateLimit: { requestsPerMinute: 50 } // API allows 60
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Risky: at or above the limit
|
|
286
|
+
source API {
|
|
287
|
+
rateLimit: { requestsPerMinute: 60 } // Exactly at limit
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Use Pause for Critical Syncs
|
|
292
|
+
|
|
293
|
+
```vague
|
|
294
|
+
source API {
|
|
295
|
+
rateLimit: {
|
|
296
|
+
requestsPerMinute: 60,
|
|
297
|
+
strategy: "pause" // Ensures completion
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Use Throttle for Background Jobs
|
|
303
|
+
|
|
304
|
+
```vague
|
|
305
|
+
source API {
|
|
306
|
+
rateLimit: {
|
|
307
|
+
requestsPerMinute: 60,
|
|
308
|
+
strategy: "throttle" // Smooth, predictable pacing
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Set Reasonable maxWait
|
|
314
|
+
|
|
315
|
+
```vague
|
|
316
|
+
source API {
|
|
317
|
+
rateLimit: {
|
|
318
|
+
requestsPerMinute: 60,
|
|
319
|
+
strategy: "pause",
|
|
320
|
+
maxWait: 300000 // 5 minutes max wait
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Combine with Circuit Breaker
|
|
326
|
+
|
|
327
|
+
```vague
|
|
328
|
+
source API {
|
|
329
|
+
auth: bearer,
|
|
330
|
+
base: "https://api.example.com",
|
|
331
|
+
rateLimit: {
|
|
332
|
+
requestsPerMinute: 60,
|
|
333
|
+
strategy: "pause"
|
|
334
|
+
},
|
|
335
|
+
circuitBreaker: {
|
|
336
|
+
failureThreshold: 5,
|
|
337
|
+
resetTimeout: 30000
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Troubleshooting
|
|
343
|
+
|
|
344
|
+
### Still Hitting Rate Limits
|
|
345
|
+
|
|
346
|
+
Lower your configured limit:
|
|
347
|
+
|
|
348
|
+
```vague
|
|
349
|
+
source API {
|
|
350
|
+
rateLimit: {
|
|
351
|
+
requestsPerMinute: 30, // Lower than API limit
|
|
352
|
+
strategy: "pause"
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Requests Too Slow
|
|
358
|
+
|
|
359
|
+
Check if throttle strategy is too aggressive:
|
|
360
|
+
|
|
361
|
+
```vague
|
|
362
|
+
// If using throttle, switch to pause
|
|
363
|
+
source API {
|
|
364
|
+
rateLimit: {
|
|
365
|
+
requestsPerMinute: 60,
|
|
366
|
+
strategy: "pause" // Only waits when needed
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Inconsistent API Limits
|
|
372
|
+
|
|
373
|
+
Use adaptive mode:
|
|
374
|
+
|
|
375
|
+
```vague
|
|
376
|
+
source API {
|
|
377
|
+
rateLimit: {
|
|
378
|
+
requestsPerMinute: 60,
|
|
379
|
+
strategy: "pause",
|
|
380
|
+
adaptive: true
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|