134 lines
7.2 KiB
Markdown
134 lines
7.2 KiB
Markdown
# @suaveplan/api-plugin-rate-limit - Rate-limit plugin for @suaveplan/api. Wraps @suaveplan/rate-limiter as a definePlugin descriptor: per-route + per-tenant configuration, parameterised path matcher, IP whitelist, tenant multiplier applied to numeric limit, Hono middleware factory, 429 + Retry-After + X-RateLimit-* / RateLimit-* headers, OTel counters, RFC 7807 Problem+JSON body. Registers `rateLimitService` in the host ServiceRegistry.
|
|
|
|
Rate-limit plugin for `@suaveplan/api`. Wraps `@suaveplan/rate-limiter`
|
|
as a `definePlugin` descriptor exposing per-route + per-tenant
|
|
configuration, parameterised path matching, IP whitelist, applied
|
|
tenant multiplier, Hono middleware, `X-RateLimit-*` / `RateLimit-*` /
|
|
`Retry-After` headers, RFC 7807 Problem+JSON 429 body, and OTel
|
|
counters / histogram. Registers a typed `rateLimitService` slot in the
|
|
host `ServiceRegistry` so sibling plugins can run manual checks.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [error-codes](./src/error-codes/error-codes.md) — Provides the stable, namespaced error-code registry every failure path in `@suaveplan/api-plugin-rate-limit` reports th…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Provides the stable, namespaced error-code registry every failure path in `@suaveplan/api-plugin-rate-limit` reports th…
|
|
- [headers](./src/headers/headers.md) — Builds the HTTP response headers and Problem+JSON body the rate-limit middleware writes onto every response (allowed AN…
|
|
- [headers](./src/headers/headers.md) — Builds the HTTP response headers and Problem+JSON body the rate-limit middleware writes onto every response (allowed AN…
|
|
- [context-augmentation](./src/middleware/context-augmentation.md) — Augments Hono's `ContextVariableMap` interface so handlers calling `c.get("rateLimit")` get a typed `RateLimitDecision`…
|
|
- [rate-limit](./src/middleware/rate-limit.md) — Constructs a Hono `MiddlewareHandler` that consults a `RateLimitService` on every request, emits the configured `X-Rate…
|
|
- [otel](./src/otel/instruments.md) — Builds (and caches) the bundle of OpenTelemetry instruments the rate-limit plugin emits on every check. Operators wirin…
|
|
- [instruments](./src/otel/instruments.md) — Builds (and caches) the bundle of OpenTelemetry instruments the rate-limit plugin emits on every check. Operators wirin…
|
|
- [path-matcher](./src/path-matcher/path-matcher.md) — Compiles a Hono-style path pattern string into a regular expression
|
|
- [path-matcher](./src/path-matcher/path-matcher.md) — Compiles a Hono-style path pattern string into a regular expression
|
|
- [plugin](./src/plugin/plugin.md) — Factory that returns a typed `PluginDescriptor` for the rate-limit plugin, ready to be passed to `@suaveplan/api`'s plu…
|
|
- [plugin](./src/plugin/plugin.md) — Factory that returns a typed `PluginDescriptor` for the rate-limit plugin, ready to be passed to `@suaveplan/api`'s plu…
|
|
- [schemas](./src/schemas/options.md) — Validates the caller-supplied `RateLimitPluginOptions` at the boundary and fills in defaults for every optional field,…
|
|
- [options](./src/schemas/options.md) — Validates the caller-supplied `RateLimitPluginOptions` at the boundary and fills in defaults for every optional field,…
|
|
- [rate-limit-service](./src/services/rate-limit-service.md) — Wraps the universal `@suaveplan/rate-limiter` core in the plugin's policy layer: IP whitelist, tenant bypass, tenant mu…
|
|
- [registry-augmentation](./src/services/registry-augmentation.md) — Augments the `ServiceRegistry` interface owned by `@suaveplan/api-plugin/service-registry` so consumers see the `rateLi…
|
|
- [types](./src/types/types.md) — Defines the public type surface every other module in the package references — the caller-facing `RateLimitPluginOption…
|
|
- [types](./src/types/types.md) — Defines the public type surface every other module in the package references — the caller-facing `RateLimitPluginOption…
|
|
<!-- end:auto-docs -->
|
|
|
|
## Install
|
|
|
|
```bash
|
|
bun add @suaveplan/api-plugin-rate-limit
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
import { createApi } from "@suaveplan/api";
|
|
import { createRateLimitPlugin } from "@suaveplan/api-plugin-rate-limit";
|
|
|
|
const api = createApi();
|
|
api.use(
|
|
createRateLimitPlugin({
|
|
defaultLimit: 100,
|
|
defaultWindowMs: 60_000,
|
|
routes: [
|
|
{
|
|
path: "/api/login",
|
|
method: "POST",
|
|
limit: 5,
|
|
windowMs: 60_000,
|
|
id: "login",
|
|
},
|
|
{ path: "/api/users/:id", limit: 30, windowMs: 60_000 },
|
|
],
|
|
tenants: [
|
|
{ tenantId: "enterprise", multiplier: 10 },
|
|
{ tenantId: "internal", bypass: true },
|
|
],
|
|
ipWhitelist: ["127.0.0.1"],
|
|
headerStyle: "both",
|
|
})
|
|
);
|
|
```
|
|
|
|
Every request flowing through the host Hono app is now rate-limited.
|
|
Responses carry both legacy `X-RateLimit-Limit` / `X-RateLimit-Remaining`
|
|
/ `X-RateLimit-Reset` and IETF `RateLimit-Limit` / `RateLimit-Remaining`
|
|
/ `RateLimit-Reset` headers. Denied requests return `429` with
|
|
`Content-Type: application/problem+json` and a `Retry-After` header.
|
|
|
|
## Manual `service.check(...)` from sibling plugins
|
|
|
|
```ts
|
|
import {
|
|
rateLimitExceeded,
|
|
type RateLimitService,
|
|
} from "@suaveplan/api-plugin-rate-limit";
|
|
|
|
const service = ctx.getService("rateLimitService") as RateLimitService;
|
|
const decision = await service.check({
|
|
ip,
|
|
tenantId,
|
|
path: "/api/billing/invoice",
|
|
method: "POST",
|
|
});
|
|
if (!decision.allowed) throw rateLimitExceeded(decision);
|
|
```
|
|
|
|
## The 3-way plug-and-play contract
|
|
|
|
This package follows the same contract every `@suaveplan/api-plugin-*`
|
|
package follows: it owns its boundary Zod schemas
|
|
(`RateLimitPluginOptionsSchema`), registers a typed slot in the host
|
|
`ServiceRegistry` (`rateLimitService`), and installs a global Hono
|
|
middleware via `app.use("*", rateLimit({ service }))` from its
|
|
canonical 6-step `onInit` lifecycle (resolve services → build storage
|
|
→ construct service → register slot → install middleware → log
|
|
readiness).
|
|
|
|
## Modules
|
|
|
|
- [`src/error-codes/error-codes.md`](src/error-codes/error-codes.md) —
|
|
`RATE_LIMIT_PLUGIN_CODES` registry + `RateLimitPluginError`.
|
|
- [`src/types/types.md`](src/types/types.md) — public type surface.
|
|
- [`src/schemas/options.md`](src/schemas/options.md) — Zod schema for
|
|
caller-supplied options.
|
|
- [`src/path-matcher/path-matcher.md`](src/path-matcher/path-matcher.md)
|
|
— Hono-compatible path-pattern compilation + matching.
|
|
- [`src/headers/headers.md`](src/headers/headers.md) — header bag +
|
|
Problem+JSON body construction.
|
|
- [`src/otel/instruments.md`](src/otel/instruments.md) — OpenTelemetry
|
|
instruments bundle.
|
|
- [`src/services/rate-limit-service.md`](src/services/rate-limit-service.md)
|
|
— `RateLimitService` factory.
|
|
- [`src/services/registry-augmentation.md`](src/services/registry-augmentation.md)
|
|
— `ServiceRegistry` module augmentation.
|
|
- [`src/middleware/rate-limit.md`](src/middleware/rate-limit.md) —
|
|
Hono middleware factory.
|
|
- [`src/middleware/context-augmentation.md`](src/middleware/context-augmentation.md)
|
|
— Hono `ContextVariableMap` augmentation.
|
|
- [`src/plugin/plugin.md`](src/plugin/plugin.md) —
|
|
`createRateLimitPlugin` factory.
|
|
|
|
## Status
|
|
|
|
`0.1.0` — initial ship. Surface is final for the 0.1.x line.
|
|
|