-
Notifications
You must be signed in to change notification settings - Fork 27k
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
Add new example for rate limiting API routes. #19509
Conversation
Stats from current PRDefault Server ModeGeneral
Page Load Tests Overall increase ✓
Client Bundles (main, webpack, commons)
Legacy Client Bundles (polyfills)
Client Pages
Client Build Manifests
Rendered Page Sizes
Serverless ModeGeneral
Client Bundles (main, webpack, commons)
Legacy Client Bundles (polyfills)
Client Pages
Client Build Manifests
Serverless bundles
|
I'm going to update this to use Redis or something else persisted 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM for now
Yeah I think Redis would be nice but this is better than nothing! |
Stats from current PRDefault Server Mode (Increase detected
|
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
buildDuration | 10.7s | 10.6s | -46ms |
nodeModulesSize | 80.6 MB | 80.6 MB | ✓ |
Page Load Tests Overall increase ✓
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
/ failed reqs | 0 | 0 | ✓ |
/ total time (seconds) | 2.07 | 2.132 | |
/ avg req/sec | 1208 | 1172.6 | |
/error-in-render failed reqs | 0 | 0 | ✓ |
/error-in-render total time (seconds) | 1.324 | 1.282 | -0.04 |
/error-in-render avg req/sec | 1888.08 | 1949.36 | +61.28 |
Client Bundles (main, webpack, commons)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
677f882d2ed8..396f.js gzip | 13 kB | 13 kB | ✓ |
framework.HASH.js gzip | 39 kB | 39 kB | ✓ |
main-6b712d3..3360.js gzip | 6.63 kB | 6.63 kB | ✓ |
webpack-50be..df5b.js gzip | 751 B | 751 B | ✓ |
Overall change | 59.3 kB | 59.3 kB | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
polyfills-81..14d7.js gzip | 31.2 kB | 31.2 kB | ✓ |
Overall change | 31.2 kB | 31.2 kB | ✓ |
Client Pages
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_app-b6fc6bc..222c.js gzip | 1.28 kB | 1.28 kB | ✓ |
_error-e2ffa..0f3f.js gzip | 3.46 kB | 3.46 kB | ✓ |
hooks-010c20..8411.js gzip | 887 B | 887 B | ✓ |
index-bbee2f..528b.js gzip | 227 B | 227 B | ✓ |
link-705099c..c35d.js gzip | 1.64 kB | 1.64 kB | ✓ |
routerDirect..bf84.js gzip | 303 B | 303 B | ✓ |
withRouter-a..5826.js gzip | 302 B | 302 B | ✓ |
Overall change | 8.09 kB | 8.09 kB | ✓ |
Client Build Manifests
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_buildManifest.js gzip | 323 B | 323 B | ✓ |
Overall change | 323 B | 323 B | ✓ |
Rendered Page Sizes
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
index.html gzip | 615 B | 615 B | ✓ |
link.html gzip | 620 B | 620 B | ✓ |
withRouter.html gzip | 608 B | 608 B | ✓ |
Overall change | 1.84 kB | 1.84 kB | ✓ |
Serverless Mode
General
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
buildDuration | 12.7s | 12.6s | -162ms |
nodeModulesSize | 80.6 MB | 80.6 MB | ✓ |
Client Bundles (main, webpack, commons)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
677f882d2ed8..396f.js gzip | 13 kB | 13 kB | ✓ |
framework.HASH.js gzip | 39 kB | 39 kB | ✓ |
main-6b712d3..3360.js gzip | 6.63 kB | 6.63 kB | ✓ |
webpack-50be..df5b.js gzip | 751 B | 751 B | ✓ |
Overall change | 59.3 kB | 59.3 kB | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
polyfills-81..14d7.js gzip | 31.2 kB | 31.2 kB | ✓ |
Overall change | 31.2 kB | 31.2 kB | ✓ |
Client Pages
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_app-b6fc6bc..222c.js gzip | 1.28 kB | 1.28 kB | ✓ |
_error-e2ffa..0f3f.js gzip | 3.46 kB | 3.46 kB | ✓ |
hooks-010c20..8411.js gzip | 887 B | 887 B | ✓ |
index-bbee2f..528b.js gzip | 227 B | 227 B | ✓ |
link-705099c..c35d.js gzip | 1.64 kB | 1.64 kB | ✓ |
routerDirect..bf84.js gzip | 303 B | 303 B | ✓ |
withRouter-a..5826.js gzip | 302 B | 302 B | ✓ |
Overall change | 8.09 kB | 8.09 kB | ✓ |
Client Build Manifests
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_buildManifest.js gzip | 323 B | 323 B | ✓ |
Overall change | 323 B | 323 B | ✓ |
Serverless bundles
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_error.js | 1 MB | 1 MB | ✓ |
404.html | 2.67 kB | 2.67 kB | ✓ |
hooks.html | 1.92 kB | 1.92 kB | ✓ |
index.js | 1 MB | 1 MB | ✓ |
link.js | 1.06 MB | 1.06 MB | ✓ |
routerDirect.js | 1.05 MB | 1.05 MB | ✓ |
withRouter.js | 1.05 MB | 1.05 MB | ✓ |
Overall change | 5.17 MB | 5.17 MB | ✓ |
Stats from current PRDefault Server Mode (Decrease detected ✓)General
Page Load Tests Overall decrease
|
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
/ failed reqs | 0 | 0 | ✓ |
/ total time (seconds) | 1.734 | 1.684 | -0.05 |
/ avg req/sec | 1441.92 | 1484.99 | +43.07 |
/error-in-render failed reqs | 0 | 0 | ✓ |
/error-in-render total time (seconds) | 1.03 | 1.149 | |
/error-in-render avg req/sec | 2426.48 | 2175.06 |
Client Bundles (main, webpack, commons)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
677f882d2ed8..396f.js gzip | 13 kB | 13 kB | ✓ |
framework.HASH.js gzip | 39 kB | 39 kB | ✓ |
main-6b712d3..3360.js gzip | 6.63 kB | 6.63 kB | ✓ |
webpack-50be..df5b.js gzip | 751 B | 751 B | ✓ |
Overall change | 59.3 kB | 59.3 kB | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
polyfills-81..14d7.js gzip | 31.2 kB | 31.2 kB | ✓ |
Overall change | 31.2 kB | 31.2 kB | ✓ |
Client Pages
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_app-b6fc6bc..222c.js gzip | 1.28 kB | 1.28 kB | ✓ |
_error-e2ffa..0f3f.js gzip | 3.46 kB | 3.46 kB | ✓ |
hooks-010c20..8411.js gzip | 887 B | 887 B | ✓ |
index-bbee2f..528b.js gzip | 227 B | 227 B | ✓ |
link-705099c..c35d.js gzip | 1.64 kB | 1.64 kB | ✓ |
routerDirect..bf84.js gzip | 303 B | 303 B | ✓ |
withRouter-a..5826.js gzip | 302 B | 302 B | ✓ |
Overall change | 8.09 kB | 8.09 kB | ✓ |
Client Build Manifests
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_buildManifest.js gzip | 323 B | 323 B | ✓ |
Overall change | 323 B | 323 B | ✓ |
Rendered Page Sizes
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
index.html gzip | 615 B | 615 B | ✓ |
link.html gzip | 620 B | 620 B | ✓ |
withRouter.html gzip | 608 B | 608 B | ✓ |
Overall change | 1.84 kB | 1.84 kB | ✓ |
Serverless Mode
General
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
buildDuration | 10.1s | 10s | -31ms |
nodeModulesSize | 80.6 MB | 80.6 MB | ✓ |
Client Bundles (main, webpack, commons)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
677f882d2ed8..396f.js gzip | 13 kB | 13 kB | ✓ |
framework.HASH.js gzip | 39 kB | 39 kB | ✓ |
main-6b712d3..3360.js gzip | 6.63 kB | 6.63 kB | ✓ |
webpack-50be..df5b.js gzip | 751 B | 751 B | ✓ |
Overall change | 59.3 kB | 59.3 kB | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
polyfills-81..14d7.js gzip | 31.2 kB | 31.2 kB | ✓ |
Overall change | 31.2 kB | 31.2 kB | ✓ |
Client Pages
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_app-b6fc6bc..222c.js gzip | 1.28 kB | 1.28 kB | ✓ |
_error-e2ffa..0f3f.js gzip | 3.46 kB | 3.46 kB | ✓ |
hooks-010c20..8411.js gzip | 887 B | 887 B | ✓ |
index-bbee2f..528b.js gzip | 227 B | 227 B | ✓ |
link-705099c..c35d.js gzip | 1.64 kB | 1.64 kB | ✓ |
routerDirect..bf84.js gzip | 303 B | 303 B | ✓ |
withRouter-a..5826.js gzip | 302 B | 302 B | ✓ |
Overall change | 8.09 kB | 8.09 kB | ✓ |
Client Build Manifests
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_buildManifest.js gzip | 323 B | 323 B | ✓ |
Overall change | 323 B | 323 B | ✓ |
Serverless bundles
vercel/next.js canary | leerob/next.js rate-limit | Change | |
---|---|---|---|
_error.js | 1 MB | 1 MB | ✓ |
404.html | 2.67 kB | 2.67 kB | ✓ |
hooks.html | 1.92 kB | 1.92 kB | ✓ |
index.js | 1 MB | 1 MB | ✓ |
link.js | 1.06 MB | 1.06 MB | ✓ |
routerDirect.js | 1.05 MB | 1.05 MB | ✓ |
withRouter.js | 1.05 MB | 1.05 MB | ✓ |
Overall change | 5.17 MB | 5.17 MB | ✓ |
Thanks @leerob . I guess I'll be implementing something similar. I have some questions, though:
What does "500 users per second" mean? What would that equate to in total number of requests/interval? Because from what I've tested, both my PC and my phone (with different IPs) were sharing the same 10 req/60s limit. Is this IP-based? Would you suggest using Are serverless functions on Vercel auto-scalable? For example, if a DDOS attack is happening, wouldn't it spin multiple instances of that API function? Will the attacker get an extra rate-limit per instance? How would that work? Hope my question is making sense. |
const tokenCount = tokenCache.get(token) || [0] | ||
if (tokenCount[0] === 0) { | ||
tokenCache.set(token, tokenCount) | ||
} | ||
tokenCount[0] += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An observation for other passers-by looking to implement something similar. 😅
This bit looks a little hacky to me. (To the point where I don't understand how using get
rather than peek
doesn't actually seem to break this, while it kind of should?) If we're avoiding updating the "recently used"-ness of the values, wouldn't a plain Map
work, given that the elements are in insertion order? Looks like we're asking an LRU cache to be a FIFO cache (with some timestamp-related behavior on get
and set
). Something like the following might be enough for this use-case?
type WithTimestamp<Value> = { value: Value, timestamp: number }
type Options = { max: number, maxAge: number }
const Cache = <Key, Value>(options: Options) => {
const map = new Map<Key, WithTimestamp<Value>>()
return {
get: (key: Key): Value | undefined => {
const element = map.get(key)
if (!element) return undefined
if (Date.now() - element.timestamp > options.maxAge) {
map.delete(key)
return undefined
}
return element.value
},
set: (key: Key, value: Value): void => {
if (map.size === options.max) map.delete(map.keys().next().value)
const timestamp = map.get(key)?.timestamp ?? Date.now()
map.set(key, { value, timestamp })
},
clear: (): void => map.clear(),
size: (): number => map.size
}
}
Adds an example using
lru-cache
to implement a simple rate limiter for API routes (Serverless Functions).Demo: https://nextjs-rate-limit.vercel.app/