Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): WebSocket limiter #18

Merged
merged 15 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 0 additions & 100 deletions .github/ISSUE_TEMPLATE/bug_report.yml

This file was deleted.

8 changes: 0 additions & 8 deletions .github/ISSUE_TEMPLATE/config.yml

This file was deleted.

37 changes: 0 additions & 37 deletions .github/workflows/release.yml

This file was deleted.

50 changes: 48 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ limit repeated requests to public APIs and/or endpoints such as password reset.
> [!NOTE]
> The `keyGenerator` function needs to be defined for `hono-rate-limiter` to work properly in your environment. Please ensure that you define the `keyGenerator` function according to the documentation before using the library.

## Installation

```sh
# Using npm/yarn/pnpm/bun
npm add hono-rate-limiter
```

## Usage

### Rest APIs

```ts
import { rateLimiter } from "hono-rate-limiter";

Expand All @@ -32,6 +41,43 @@ const limiter = rateLimiter({
app.use(limiter);
```

### WebSocket APIs

```ts
import { webSocketLimiter } from "hono-rate-limiter";
import { upgradeWebSocket } from "hono/cloudflare-workers";
import { RedisStore } from "@hono-rate-limiter/redis";
import { Redis } from "@upstash/redis/cloudflare";

const limiter = webSocketLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
keyGenerator: (c) => "<unique_key>", // Method to generate custom identifiers for clients.
store: new RedisStore({ client }), // Define your DataStore. See below.
});

// Apply the rate limiting middleware to ws requests.
app.get(
"/",
upgradeWebSocket(
limiter((c) => {
return {
onOpen: () => {
console.log("Connection opened");
},
async onMessage(event, ws) {
console.log(`Message from client: ${event.data}`);
ws.send("Hello from server!");
},
onClose: () => {
console.log("Connection closed");
},
};
})
)
);
```

## Data Stores

`hono-rate-limit` supports external data stores to synchronize hit counts across multiple processes and servers.
Expand All @@ -47,8 +93,8 @@ Here is a list of stores:
| MemoryStore | (default) Simple in-memory option. Does not share state when the app has multiple processes or servers. |
| [`@hono-rate-limiter/redis`](https://www.npm.im/@hono-rate-limiter/redis) | A [Redis](https://redis.io/)-backed store, used with [`@vercel/kv`](https://www.npmjs.com/package/@vercel/kv) and [`@upstash/redis`](https://www.npmjs.com/package/@upstash/redis) |
| [`rate-limit-redis`](https://npm.im/rate-limit-redis) | A [Redis](https://redis.io/)-backed store, more suitable for large or demanding deployments. |
| [`rate-limit-postgresql`](https://www.npm.im/@acpr/rate-limit-postgresql) | A [PostgreSQL](https://www.postgresql.org/)-backed store. |
| [`rate-limit-memcached`](https://npmjs.org/package/rate-limit-memcached) | A [Memcached](https://memcached.org/)-backed store. |
| [`rate-limit-postgresql`](https://www.npm.im/@acpr/rate-limit-postgresql) | A [PostgreSQL](https://www.postgresql.org/)-backed store. |
| [`rate-limit-memcached`](https://npmjs.org/package/rate-limit-memcached) | A [Memcached](https://memcached.org/)-backed store. |
| [`cluster-memory-store`](https://npm.im/@express-rate-limit/cluster-memory-store) | A memory-store wrapper that shares state across all processes on a single server via the [node:cluster](https://nodejs.org/api/cluster.html) module. Does not share state across multiple servers. |
| [`precise-memory-rate-limit`](https://www.npm.im/precise-memory-rate-limit) | A memory store similar to the built-in one, except that it stores a distinct timestamp for each key. |
| [`typeorm-rate-limit-store`](https://www.npmjs.com/package/typeorm-rate-limit-store) | Supports a variety of databases via [TypeORM](https://typeorm.io/): MySQL, MariaDB, CockroachDB, SQLite, Microsoft SQL Server, Oracle, SAP Hana, and more. |
Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
},
"private": true,
"dependencies": {
"@hono/node-server": "^1.9.1",
"@hono/node-server": "^1.11.1",
"@vercel/kv": "^1.0.1",
"hono": "^4.2.0"
"hono": "^4.3.7"
},
"devDependencies": {
"@biomejs/biome": "^1.6.3",
"@biomejs/biome": "^1.7.3",
"@jscutlery/semver": "^5.2.2",
"@nx/devkit": "18.1.2",
"@nx/eslint": "18.1.2",
Expand All @@ -26,6 +26,7 @@
"@nx/vite": "18.1.2",
"@nx/web": "18.1.2",
"@nx/workspace": "18.1.2",
"@rollup/plugin-terser": "^0.4.4",
"@swc-node/register": "~1.8.0",
"@swc/cli": "~0.1.62",
"@swc/core": "~1.3.85",
Expand All @@ -34,9 +35,9 @@
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@upstash/redis": "^1.29.0",
"@vitest/coverage-v8": "^1.0.4",
"@vitest/ui": "^1.3.1",
"@upstash/redis": "^1.31.1",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/ui": "^1.6.0",
"chalk": "^5.3.0",
"czg": "^1.9.1",
"eslint": "~8.48.0",
Expand All @@ -50,12 +51,12 @@
"supertest": "^6.3.4",
"swc-loader": "0.1.15",
"tslib": "^2.6.2",
"typescript": "~5.4.3",
"typescript": "~5.4.5",
"verdaccio": "^5.0.4",
"vite": "~5.2.2",
"vitest": "^1.3.1"
"vite": "~5.2.11",
"vitest": "^1.6.0"
},
"nx": {
"includedScripts": []
}
}
}
14 changes: 14 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).

## [0.4.0-rc.1](https://github.com/rhinobase/hono-rate-limiter/compare/core-0.4.0-rc.0...core-0.4.0-rc.1) (2024-05-20)


### Bug Fixes

* **core:** correct try catch of websocket limiter ([5517f2b](https://github.com/rhinobase/hono-rate-limiter/commit/5517f2b693aae463646075f06f068f3732458666))

## [0.4.0-rc.0](https://github.com/rhinobase/hono-rate-limiter/compare/core-0.3.0...core-0.4.0-rc.0) (2024-05-19)


### Features

* **core:** websockets limiter ([1ddd912](https://github.com/rhinobase/hono-rate-limiter/commit/1ddd9127bb1ef29b24d1eb6a027953728ff9239d))

## [0.3.0](https://github.com/rhinobase/hono-rate-limiter/compare/core-0.2.3...core-0.3.0) (2024-05-01)


Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hono-rate-limiter",
"version": "0.3.0",
"version": "0.4.0-rc.1",
"license": "MIT",
"keywords": [
"hono",
Expand Down
1 change: 1 addition & 0 deletions packages/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"tsConfig": "packages/core/tsconfig.lib.json",
"compiler": "swc",
"project": "packages/core/package.json",
"rollupConfig": "rollup.config.js",
"format": ["esm", "cjs"],
"assets": [
{
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/helpers/createServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function createServer<
// Register test routes
.get("/", (c) => c.text("Hi there!"))
.get("/error", (c) => c.text("Error!", { status: 400 }))
.post("/crash", (c) => {
.post("/crash", () => {
throw new HTTPException(400, { message: "Oops!" });
});

Expand Down
Loading