effect-requests-rate-limiter
v1.0.11
Published
A rate limiter for Effect requests which supports headers parsing
Downloads
3
Readme
Requests Rate Limiter
Description
The Requests Rate Limiter is an effectful function that provides rate limiting functionality for web applications. It helps to prevent going over usage limits for a given resource, such as an API endpoint, and can be configured to detect 429
responses when the rate limit is exceeded for any reason.
This is the type of a Requests Rate Limiter:
(req: Http.request.ClientRequest) => Http.request.ClientRequest
Features
- Configurable rate limiter (e.g., fixed window, token bucket)
- Customizable retry policy
- Customizable maximum of concurrent requests
- Customizable schema for headers parsing
Installation
pnpm i effect-requests-rate-limiter
Example
import { makeRequestsRateLimiter, type RetryPolicy } from "effect-requests-rate-limiter"
// retryAfterMillis is needed to know how much time to wait before letting other requests pass after a 429 response
//
// remainingRequestsQuota and resetAfterMillis are needed to know how many requests are left before reaching the limit of the current window and when the limit will be reset
const RateLimitHeadersSchema = S.Schema.Struct({
"retryAfterMillis": S.Schema.transform(
S.Schema.NumberFromString,
S.Schema.Number,
// from seconds to milliseconds
{ encode: (n) => n / 1000, decode: (n) => n * 1000 }
).pipe(
S.Schema.optional(),
S.Schema.fromKey("retry-after")
),
"remainingRequestsQuota": S.Schema.NumberFromString.pipe(
S.Schema.optional(),
S.Schema.fromKey("x-ratelimit-remaining")
),
"resetAfterMillis": S.Schema.transform(
S.Schema.NumberFromString,
S.Schema.Number,
// from seconds to milliseconds
{ encode: (n) => n / 1000, decode: (n) => n * 1000 }
).pipe(
S.Schema.optional(),
S.Schema.fromKey("x-ratelimit-reset")
)
})
const MyRetryPolicy = Effect.retry({
schedule: Schedule.jittered(Schedule.exponential("200 millis")),
while: (err) => err._tag === "ResponseError" && err.response.status === 429,
times: 5
}) satisfies RetryPolicy
const MyRateLimiter = RateLimiter.make({
limit: 5,
algorithm: "fixed-window",
interval: Duration.seconds(3)
})
const main = Effect.gen(function*($) {
const requestsRateLimiter = yield* makeRequestsRateLimiter({
rateLimitHeadersSchema: RateLimitHeadersSchema,
retryPolicy: MyRetryPolicy,
rateLimiter: yield* MyRateLimiter,
maxConcurrentRequests: 4
})
const aRequest = Http.request.get("https://jsonplaceholder.typicode.com/todos/1")
const rateLimitedRequest = requestRateLimiter(aRequest)
// ...
})
NodeRuntime.runMain(main.pipe(
// fetchOk or similar must be used so that non 2xx responses are considered errors
Effect.provideService(Http.client.Client, Http.client.fetchOk)
))
Peer dependencies
"@effect/platform", "@effect/schema", "effect"