[{"data":1,"prerenderedAt":5427},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":338,"/docs/nuxt-auto/auto-api/rate-limiting-en":444,"/docs/nuxt-auto/auto-api/rate-limiting-surround-en":5422},[4,8,13,28,41,51,64,77,94,110,134,150,157,172],{"title":5,"path":6,"stem":7},"Overview","/docs/landing","0.docs/1.landing/001.index",{"title":9,"path":10,"stem":11,"badge":12},"Built-in Features","/docs/landing/built-in-features","0.docs/1.landing/002.built-in-features","New",{"title":14,"path":15,"stem":16,"children":17,"icon":27},"Content Foundation","/docs/landing/content","0.docs/1.landing/02.content/1.index",[18,19,23],{"title":5,"path":15,"stem":16},{"title":20,"path":21,"stem":22},"Details","/docs/landing/content/details","0.docs/1.landing/02.content/2.details",{"title":24,"path":25,"stem":26},"Technical","/docs/landing/content/technical","0.docs/1.landing/02.content/4.technical","i-heroicons-document-text",{"title":29,"path":30,"stem":31,"children":32,"icon":40},"Regional Content","/docs/landing/regional","0.docs/1.landing/03.regional/1.index",[33,34,37],{"title":5,"path":30,"stem":31},{"title":20,"path":35,"stem":36},"/docs/landing/regional/details","0.docs/1.landing/03.regional/2.details",{"title":24,"path":38,"stem":39},"/docs/landing/regional/technical","0.docs/1.landing/03.regional/4.technical","i-heroicons-globe-alt",{"title":42,"path":43,"stem":44,"children":45,"icon":50},"Multi-language","/docs/landing/multilang","0.docs/1.landing/04.multilang/1.index",[46,47],{"title":5,"path":43,"stem":44},{"title":20,"path":48,"stem":49},"/docs/landing/multilang/details","0.docs/1.landing/04.multilang/2.details","i-heroicons-language",{"title":52,"path":53,"stem":54,"children":55,"icon":63},"Blog","/docs/landing/blog","0.docs/1.landing/05.blog/1.index",[56,57,60],{"title":5,"path":53,"stem":54},{"title":20,"path":58,"stem":59},"/docs/landing/blog/details","0.docs/1.landing/05.blog/2.details",{"title":24,"path":61,"stem":62},"/docs/landing/blog/technical","0.docs/1.landing/05.blog/4.technical","i-heroicons-pencil-square",{"title":65,"path":66,"stem":67,"children":68,"icon":76},"Documentation","/docs/landing/docs","0.docs/1.landing/06.docs/1.index",[69,70,73],{"title":5,"path":66,"stem":67},{"title":20,"path":71,"stem":72},"/docs/landing/docs/details","0.docs/1.landing/06.docs/2.details",{"title":24,"path":74,"stem":75},"/docs/landing/docs/technical","0.docs/1.landing/06.docs/4.technical","i-heroicons-book-open",{"title":78,"path":79,"stem":80,"children":81,"icon":93},"Forms","/docs/landing/forms","0.docs/1.landing/07.forms/1.index",[82,83,86,90],{"title":5,"path":79,"stem":80},{"title":20,"path":84,"stem":85},"/docs/landing/forms/details","0.docs/1.landing/07.forms/2.details",{"title":87,"path":88,"stem":89},"Admin","/docs/landing/forms/admin","0.docs/1.landing/07.forms/3.admin",{"title":24,"path":91,"stem":92},"/docs/landing/forms/technical","0.docs/1.landing/07.forms/4.technical","i-heroicons-clipboard-document-list",{"title":95,"path":96,"stem":97,"children":98,"icon":109},"Email","/docs/landing/email","0.docs/1.landing/08.email/1.index",[99,100,103,106],{"title":5,"path":96,"stem":97},{"title":20,"path":101,"stem":102},"/docs/landing/email/details","0.docs/1.landing/08.email/2.details",{"title":87,"path":104,"stem":105},"/docs/landing/email/admin","0.docs/1.landing/08.email/3.admin",{"title":24,"path":107,"stem":108},"/docs/landing/email/technical","0.docs/1.landing/08.email/4.technical","i-heroicons-envelope",{"title":111,"path":112,"stem":113,"children":114,"icon":133},"Feedback Platform","/docs/landing/feedback","0.docs/1.landing/09.feedback/1.index",[115,116,119,122,125,129],{"title":5,"path":112,"stem":113},{"title":20,"path":117,"stem":118},"/docs/landing/feedback/details","0.docs/1.landing/09.feedback/2.details",{"title":87,"path":120,"stem":121},"/docs/landing/feedback/admin","0.docs/1.landing/09.feedback/3.admin",{"title":24,"path":123,"stem":124},"/docs/landing/feedback/technical","0.docs/1.landing/09.feedback/4.technical",{"title":126,"path":127,"stem":128},"Compare vs SaaS","/docs/landing/feedback/compare","0.docs/1.landing/09.feedback/5.compare",{"title":130,"path":131,"stem":132},"FAQ","/docs/landing/feedback/faq","0.docs/1.landing/09.feedback/6.faq","i-heroicons-chat-bubble-left-right",{"title":135,"path":136,"stem":137,"children":138,"icon":149},"Storage","/docs/landing/storage","0.docs/1.landing/10.storage/1.index",[139,140,143,146],{"title":5,"path":136,"stem":137},{"title":20,"path":141,"stem":142},"/docs/landing/storage/details","0.docs/1.landing/10.storage/2.details",{"title":87,"path":144,"stem":145},"/docs/landing/storage/admin","0.docs/1.landing/10.storage/3.admin",{"title":24,"path":147,"stem":148},"/docs/landing/storage/technical","0.docs/1.landing/10.storage/4.technical","i-heroicons-circle-stack",{"title":151,"path":152,"stem":153,"children":154,"icon":156},"Offline First","/docs/landing/offline-first","0.docs/1.landing/11.offline-first/1.index",[155],{"title":151,"path":152,"stem":153},"i-heroicons-users",{"title":158,"path":159,"stem":160,"children":161,"icon":156},"Yjs Sync","/docs/landing/yjs-sync","0.docs/1.landing/12.yjs-sync/1.index",[162,163,166,169],{"title":5,"path":159,"stem":160},{"title":20,"path":164,"stem":165},"/docs/landing/yjs-sync/details","0.docs/1.landing/12.yjs-sync/2.details",{"title":87,"path":167,"stem":168},"/docs/landing/yjs-sync/admin","0.docs/1.landing/12.yjs-sync/3.admin",{"title":24,"path":170,"stem":171},"/docs/landing/yjs-sync/technical","0.docs/1.landing/12.yjs-sync/4.technical",{"title":173,"path":174,"stem":175,"children":176,"badge":178,"icon":179},"Newsletter","/docs/landing/newsletter","0.docs/1.landing/13.newsletter/index",[177],{"title":173,"path":174,"stem":175,"badge":178},"Coming Soon","i-lucide-send",[181,184,202,298],{"title":5,"path":182,"stem":183},"/docs/nuxt-auto","0.docs/3.nuxt-auto/index",{"title":185,"path":186,"stem":187,"children":188,"page":201},"Getting Started","/docs/nuxt-auto/getting-started","0.docs/3.nuxt-auto/1.getting-started",[189,193,197],{"title":190,"path":191,"stem":192},"Introduction","/docs/nuxt-auto/getting-started/introduction","0.docs/3.nuxt-auto/1.getting-started/1.introduction",{"title":194,"path":195,"stem":196},"Installation","/docs/nuxt-auto/getting-started/installation","0.docs/3.nuxt-auto/1.getting-started/2.installation",{"title":198,"path":199,"stem":200},"Quick Start","/docs/nuxt-auto/getting-started/quick-start","0.docs/3.nuxt-auto/1.getting-started/3.quick-start",false,{"title":203,"path":204,"stem":205,"children":206,"page":201},"Auto API","/docs/nuxt-auto/auto-api","0.docs/3.nuxt-auto/2.auto-api",[207,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294],{"title":185,"path":208,"stem":209},"/docs/nuxt-auto/auto-api/getting-started","0.docs/3.nuxt-auto/2.auto-api/1.getting-started",{"title":211,"path":212,"stem":213},"Aggregations","/docs/nuxt-auto/auto-api/aggregations","0.docs/3.nuxt-auto/2.auto-api/10.aggregations",{"title":215,"path":216,"stem":217},"Lifecycle Hooks","/docs/nuxt-auto/auto-api/lifecycle-hooks","0.docs/3.nuxt-auto/2.auto-api/11.lifecycle-hooks",{"title":219,"path":220,"stem":221},"Many-to-Many (M2M) Relationships","/docs/nuxt-auto/auto-api/m2m-relationships","0.docs/3.nuxt-auto/2.auto-api/12.m2m-relationships",{"title":223,"path":224,"stem":225},"Plugin System","/docs/nuxt-auto/auto-api/plugin-system","0.docs/3.nuxt-auto/2.auto-api/13.plugin-system",{"title":227,"path":228,"stem":229},"Database Adapters","/docs/nuxt-auto/auto-api/database-adapters","0.docs/3.nuxt-auto/2.auto-api/14.database-adapters",{"title":231,"path":232,"stem":233},"Custom Endpoints","/docs/nuxt-auto/auto-api/custom-endpoints","0.docs/3.nuxt-auto/2.auto-api/15.custom-endpoints",{"title":235,"path":236,"stem":237},"Multi-Tenancy","/docs/nuxt-auto/auto-api/multi-tenancy","0.docs/3.nuxt-auto/2.auto-api/16.multi-tenancy",{"title":239,"path":240,"stem":241},"Validation","/docs/nuxt-auto/auto-api/validation","0.docs/3.nuxt-auto/2.auto-api/2.validation",{"title":243,"path":244,"stem":245},"Rate Limiting","/docs/nuxt-auto/auto-api/rate-limiting","0.docs/3.nuxt-auto/2.auto-api/20.rate-limiting",{"title":247,"path":248,"stem":249},"Request Metadata Plugin","/docs/nuxt-auto/auto-api/request-metadata","0.docs/3.nuxt-auto/2.auto-api/21.request-metadata",{"title":251,"path":252,"stem":253},"Plugin Catalog","/docs/nuxt-auto/auto-api/plugin-catalog","0.docs/3.nuxt-auto/2.auto-api/22.plugin-catalog",{"title":255,"path":256,"stem":257},"Handler Overrides","/docs/nuxt-auto/auto-api/handler-overrides","0.docs/3.nuxt-auto/2.auto-api/3.handler-overrides",{"title":259,"path":260,"stem":261},"Cloudflare D1","/docs/nuxt-auto/auto-api/cloudflare-d1","0.docs/3.nuxt-auto/2.auto-api/30.cloudflare-d1",{"title":263,"path":264,"stem":265},"SQLite to D1 Migration","/docs/nuxt-auto/auto-api/migration-sqlite-d1","0.docs/3.nuxt-auto/2.auto-api/31.migration-sqlite-d1",{"title":267,"path":268,"stem":269},"Frontend Composables","/docs/nuxt-auto/auto-api/frontend-composables","0.docs/3.nuxt-auto/2.auto-api/32.frontend-composables",{"title":271,"path":272,"stem":273},"Testing","/docs/nuxt-auto/auto-api/testing","0.docs/3.nuxt-auto/2.auto-api/33.testing",{"title":275,"path":276,"stem":277},"Pagination","/docs/nuxt-auto/auto-api/pagination","0.docs/3.nuxt-auto/2.auto-api/4.pagination",{"title":279,"path":280,"stem":281},"Soft Deletes","/docs/nuxt-auto/auto-api/soft-deletes","0.docs/3.nuxt-auto/2.auto-api/5.soft-deletes",{"title":283,"path":284,"stem":285},"Authentication & Authorization","/docs/nuxt-auto/auto-api/authentication-authorization","0.docs/3.nuxt-auto/2.auto-api/6.authentication-authorization",{"title":287,"path":288,"stem":289},"Better-Auth Integration","/docs/nuxt-auto/auto-api/better-auth","0.docs/3.nuxt-auto/2.auto-api/7.better-auth",{"title":291,"path":292,"stem":293},"Nested Relations","/docs/nuxt-auto/auto-api/nested-relationships","0.docs/3.nuxt-auto/2.auto-api/8.nested-relationships",{"title":295,"path":296,"stem":297},"Bulk Operations","/docs/nuxt-auto/auto-api/bulk-operations","0.docs/3.nuxt-auto/2.auto-api/9.bulk-operations",{"title":299,"path":300,"stem":301,"children":302,"page":201},"Auto Admin","/docs/nuxt-auto/auto-admin","0.docs/3.nuxt-auto/3.auto-admin",[303,306,310,314,318,322,326,330,334],{"title":185,"path":304,"stem":305},"/docs/nuxt-auto/auto-admin/getting-started","0.docs/3.nuxt-auto/3.auto-admin/1.getting-started",{"title":307,"path":308,"stem":309},"Configuration & Theming","/docs/nuxt-auto/auto-admin/configuration-theming","0.docs/3.nuxt-auto/3.auto-admin/2.configuration-theming",{"title":311,"path":312,"stem":313},"Resource Configuration","/docs/nuxt-auto/auto-admin/resource-configuration","0.docs/3.nuxt-auto/3.auto-admin/3.resource-configuration",{"title":315,"path":316,"stem":317},"Form Fields & Widgets","/docs/nuxt-auto/auto-admin/form-fields-widgets","0.docs/3.nuxt-auto/3.auto-admin/4.form-fields-widgets",{"title":319,"path":320,"stem":321},"Permissions","/docs/nuxt-auto/auto-admin/permissions","0.docs/3.nuxt-auto/3.auto-admin/5.permissions",{"title":323,"path":324,"stem":325},"Custom Pages","/docs/nuxt-auto/auto-admin/custom-pages","0.docs/3.nuxt-auto/3.auto-admin/6.custom-pages",{"title":327,"path":328,"stem":329},"M2M Relationships","/docs/nuxt-auto/auto-admin/m2m-relationships","0.docs/3.nuxt-auto/3.auto-admin/7.m2m-relationships",{"title":331,"path":332,"stem":333},"Custom Actions","/docs/nuxt-auto/auto-admin/custom-actions","0.docs/3.nuxt-auto/3.auto-admin/8.custom-actions",{"title":335,"path":336,"stem":337},"Composables","/docs/nuxt-auto/auto-admin/composables","0.docs/3.nuxt-auto/3.auto-admin/9.composables",[339,342,355,382,400,415,425],{"title":5,"path":340,"stem":341},"/docs/nuxt-protokit","0.docs/4.nuxt-protokit/index",{"title":185,"path":343,"stem":344,"children":345,"icon":354},"/docs/nuxt-protokit/getting-started","0.docs/4.nuxt-protokit/1.getting-started/1.index",[346,347,351],{"title":190,"path":343,"stem":344},{"title":348,"path":349,"stem":350},"Core Concepts","/docs/nuxt-protokit/getting-started/concepts","0.docs/4.nuxt-protokit/1.getting-started/2.concepts",{"title":198,"path":352,"stem":353},"/docs/nuxt-protokit/getting-started/quick-start","0.docs/4.nuxt-protokit/1.getting-started/3.quick-start","i-lucide-rocket",{"title":356,"path":357,"stem":358,"children":359,"icon":381},"Schemas","/docs/nuxt-protokit/schemas","0.docs/4.nuxt-protokit/2.schemas/1.index",[360,361,365,369,373,377],{"title":5,"path":357,"stem":358},{"title":362,"path":363,"stem":364},"Field Types","/docs/nuxt-protokit/schemas/fields","0.docs/4.nuxt-protokit/2.schemas/2.fields",{"title":366,"path":367,"stem":368},"Collections","/docs/nuxt-protokit/schemas/collections","0.docs/4.nuxt-protokit/2.schemas/3.collections",{"title":370,"path":371,"stem":372},"Derived & Computed","/docs/nuxt-protokit/schemas/derived-computed","0.docs/4.nuxt-protokit/2.schemas/4.derived-computed",{"title":374,"path":375,"stem":376},"Connections","/docs/nuxt-protokit/schemas/connections","0.docs/4.nuxt-protokit/2.schemas/5.connections",{"title":378,"path":379,"stem":380},"Visualizations & Layouts","/docs/nuxt-protokit/schemas/visualizations","0.docs/4.nuxt-protokit/2.schemas/6.visualizations","i-lucide-file-code",{"title":335,"path":383,"stem":384,"children":385,"icon":399},"/docs/nuxt-protokit/composables","0.docs/4.nuxt-protokit/3.composables/1.index",[386,387,391,395],{"title":5,"path":383,"stem":384},{"title":388,"path":389,"stem":390},"usePrototype","/docs/nuxt-protokit/composables/use-prototype","0.docs/4.nuxt-protokit/3.composables/2.use-prototype",{"title":392,"path":393,"stem":394},"useProtoDoc","/docs/nuxt-protokit/composables/use-proto-doc","0.docs/4.nuxt-protokit/3.composables/3.use-proto-doc",{"title":396,"path":397,"stem":398},"useProtoCollection","/docs/nuxt-protokit/composables/use-proto-collection","0.docs/4.nuxt-protokit/3.composables/4.use-proto-collection","i-lucide-layers",{"title":401,"path":402,"stem":403,"children":404,"icon":414},"Components","/docs/nuxt-protokit/components","0.docs/4.nuxt-protokit/4.components/1.index",[405,406,410],{"title":5,"path":402,"stem":403},{"title":407,"path":408,"stem":409},"ProtoTool","/docs/nuxt-protokit/components/proto-tool","0.docs/4.nuxt-protokit/4.components/2.proto-tool",{"title":411,"path":412,"stem":413},"ProtoCrudModal","/docs/nuxt-protokit/components/proto-crud-modal","0.docs/4.nuxt-protokit/4.components/3.proto-crud-modal","i-lucide-puzzle",{"title":151,"path":416,"stem":417,"children":418,"icon":424},"/docs/nuxt-protokit/offline-first","0.docs/4.nuxt-protokit/5.offline-first/1.index",[419,420],{"title":151,"path":416,"stem":417},{"title":421,"path":422,"stem":423},"Corruption Recovery","/docs/nuxt-protokit/offline-first/corruption-recovery","0.docs/4.nuxt-protokit/5.offline-first/2.corruption-recovery","i-lucide-wifi-off",{"title":426,"icon":427,"path":428,"stem":429,"children":430},"Advanced","i-lucide-graduation-cap","/docs/nuxt-protokit/advanced","0.docs/4.nuxt-protokit/6.advanced/1.index",[431,432,436,440],{"title":5,"path":428,"stem":429},{"title":433,"path":434,"stem":435},"Multi-Tool Apps","/docs/nuxt-protokit/advanced/building-a-toolkit","0.docs/4.nuxt-protokit/6.advanced/1.building-a-toolkit",{"title":437,"path":438,"stem":439},"Schema Patterns","/docs/nuxt-protokit/advanced/custom-schema-patterns","0.docs/4.nuxt-protokit/6.advanced/2.custom-schema-patterns",{"title":441,"path":442,"stem":443},"Custom Fields & Viz","/docs/nuxt-protokit/advanced/extensibility","0.docs/4.nuxt-protokit/6.advanced/3.extensibility",{"page":445,"fallbackPage":5418},{"id":446,"title":243,"body":447,"description":5416,"extension":5417,"links":5418,"meta":5419,"navigation":5418,"ogImage":5418,"path":244,"seo":5420,"stem":245,"__hash__":5421},"nuxt_auto/0.docs/3.nuxt-auto/2.auto-api/20.rate-limiting.md",{"type":448,"value":449,"toc":5373},"minimark",[450,454,463,467,474,672,678,703,707,914,918,923,1150,1154,1215,1219,1279,1283,1409,1413,1509,1513,1565,1569,1572,1599,1602,1676,1680,1684,1687,1711,1716,1729,1734,1745,1749,1752,2110,2234,2238,2249,2253,2261,2265,2269,2584,2588,2851,2855,3068,3072,3332,3336,3340,3641,3645,3861,3864,3868,3932,3936,4226,4230,4234,4557,4561,4877,4881,4944,4948,4952,4955,4969,4973,4976,5017,5020,5058,5062,5065,5073,5077,5103,5107,5110,5349,5352,5369],[451,452,243],"h1",{"id":453},"rate-limiting",[455,456,457,458,462],"p",{},"Rate limiting is provided as a built-in plugin to protect your API from abuse and ensure fair usage. It registers as ",[459,460,461],"code",{},"pre-auth"," middleware that runs automatically on every API request.",[464,465,194],"h2",{"id":466},"installation",[455,468,469,470,473],{},"The rate limiting plugin is included with @websideproject/nuxt-auto-api. Enable it via the ",[459,471,472],{},"plugins"," array:",[475,476,481],"pre",{"className":477,"code":478,"language":479,"meta":480,"style":480},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","// nuxt.config.ts\nimport { createRateLimitPlugin } from '@websideproject/nuxt-auto-api/plugins'\n\nexport default defineNuxtConfig({\n  autoApi: {\n    plugins: [\n      createRateLimitPlugin({\n        windowMs: 60000,  // 1 minute\n        max: 100,         // 100 requests per minute\n        byIp: true,\n        byUser: false\n      })\n    ]\n  }\n})\n","typescript","",[459,482,483,492,523,530,549,562,573,583,601,617,632,643,652,658,664],{"__ignoreMap":480},[484,485,488],"span",{"class":486,"line":487},"line",1,[484,489,491],{"class":490},"sHwdD","// nuxt.config.ts\n",[484,493,495,499,503,507,510,513,516,520],{"class":486,"line":494},2,[484,496,498],{"class":497},"s7zQu","import",[484,500,502],{"class":501},"sMK4o"," {",[484,504,506],{"class":505},"sTEyZ"," createRateLimitPlugin",[484,508,509],{"class":501}," }",[484,511,512],{"class":497}," from",[484,514,515],{"class":501}," '",[484,517,519],{"class":518},"sfazB","@websideproject/nuxt-auto-api/plugins",[484,521,522],{"class":501},"'\n",[484,524,526],{"class":486,"line":525},3,[484,527,529],{"emptyLinePlaceholder":528},true,"\n",[484,531,533,536,539,543,546],{"class":486,"line":532},4,[484,534,535],{"class":497},"export",[484,537,538],{"class":497}," default",[484,540,542],{"class":541},"s2Zo4"," defineNuxtConfig",[484,544,545],{"class":505},"(",[484,547,548],{"class":501},"{\n",[484,550,552,556,559],{"class":486,"line":551},5,[484,553,555],{"class":554},"swJcz","  autoApi",[484,557,558],{"class":501},":",[484,560,561],{"class":501}," {\n",[484,563,565,568,570],{"class":486,"line":564},6,[484,566,567],{"class":554},"    plugins",[484,569,558],{"class":501},[484,571,572],{"class":505}," [\n",[484,574,576,579,581],{"class":486,"line":575},7,[484,577,578],{"class":541},"      createRateLimitPlugin",[484,580,545],{"class":505},[484,582,548],{"class":501},[484,584,586,589,591,595,598],{"class":486,"line":585},8,[484,587,588],{"class":554},"        windowMs",[484,590,558],{"class":501},[484,592,594],{"class":593},"sbssI"," 60000",[484,596,597],{"class":501},",",[484,599,600],{"class":490},"  // 1 minute\n",[484,602,604,607,609,612,614],{"class":486,"line":603},9,[484,605,606],{"class":554},"        max",[484,608,558],{"class":501},[484,610,611],{"class":593}," 100",[484,613,597],{"class":501},[484,615,616],{"class":490},"         // 100 requests per minute\n",[484,618,620,623,625,629],{"class":486,"line":619},10,[484,621,622],{"class":554},"        byIp",[484,624,558],{"class":501},[484,626,628],{"class":627},"sfNiH"," true",[484,630,631],{"class":501},",\n",[484,633,635,638,640],{"class":486,"line":634},11,[484,636,637],{"class":554},"        byUser",[484,639,558],{"class":501},[484,641,642],{"class":627}," false\n",[484,644,646,649],{"class":486,"line":645},12,[484,647,648],{"class":501},"      }",[484,650,651],{"class":505},")\n",[484,653,655],{"class":486,"line":654},13,[484,656,657],{"class":505},"    ]\n",[484,659,661],{"class":486,"line":660},14,[484,662,663],{"class":501},"  }\n",[484,665,667,670],{"class":486,"line":666},15,[484,668,669],{"class":501},"}",[484,671,651],{"class":505},[455,673,674,675,677],{},"No additional Nitro plugin or manual middleware setup is needed. The plugin automatically registers ",[459,676,461],{}," middleware that checks rate limits before authorization runs.",[679,680,681],"blockquote",{},[455,682,683,687,688,691,692,695,696,691,699,702],{},[684,685,686],"strong",{},"Migration note:"," If you were using the legacy ",[459,689,690],{},"createRateLimitExtension"," with ",[459,693,694],{},"extensions: [...]",", switch to ",[459,697,698],{},"createRateLimitPlugin",[459,700,701],{},"plugins: [...]",". The configuration options are the same.",[464,704,706],{"id":705},"configuration-options","Configuration Options",[475,708,710],{"className":477,"code":709,"language":479,"meta":480,"style":480},"interface RateLimitConfig {\n  // Window size in milliseconds (default: 60000 = 1 minute)\n  windowMs?: number\n\n  // Maximum requests per window (default: 100)\n  max?: number\n\n  // Rate limit by IP address (default: true)\n  byIp?: boolean\n\n  // Rate limit by user ID (default: false)\n  byUser?: boolean\n\n  // Custom key generator\n  keyGenerator?: (event: any) => string\n\n  // Skip rate limiting for certain requests\n  skip?: (event: any) => boolean\n\n  // Custom error message\n  message?: string\n\n  // Custom storage backend\n  store?: RateLimitStore\n}\n",[459,711,712,724,729,740,744,749,758,762,767,777,781,786,795,799,804,832,837,843,865,870,876,886,891,897,908],{"__ignoreMap":480},[484,713,714,718,722],{"class":486,"line":487},[484,715,717],{"class":716},"spNyl","interface",[484,719,721],{"class":720},"sBMFI"," RateLimitConfig",[484,723,561],{"class":501},[484,725,726],{"class":486,"line":494},[484,727,728],{"class":490},"  // Window size in milliseconds (default: 60000 = 1 minute)\n",[484,730,731,734,737],{"class":486,"line":525},[484,732,733],{"class":554},"  windowMs",[484,735,736],{"class":501},"?:",[484,738,739],{"class":720}," number\n",[484,741,742],{"class":486,"line":532},[484,743,529],{"emptyLinePlaceholder":528},[484,745,746],{"class":486,"line":551},[484,747,748],{"class":490},"  // Maximum requests per window (default: 100)\n",[484,750,751,754,756],{"class":486,"line":564},[484,752,753],{"class":554},"  max",[484,755,736],{"class":501},[484,757,739],{"class":720},[484,759,760],{"class":486,"line":575},[484,761,529],{"emptyLinePlaceholder":528},[484,763,764],{"class":486,"line":585},[484,765,766],{"class":490},"  // Rate limit by IP address (default: true)\n",[484,768,769,772,774],{"class":486,"line":603},[484,770,771],{"class":554},"  byIp",[484,773,736],{"class":501},[484,775,776],{"class":720}," boolean\n",[484,778,779],{"class":486,"line":619},[484,780,529],{"emptyLinePlaceholder":528},[484,782,783],{"class":486,"line":634},[484,784,785],{"class":490},"  // Rate limit by user ID (default: false)\n",[484,787,788,791,793],{"class":486,"line":645},[484,789,790],{"class":554},"  byUser",[484,792,736],{"class":501},[484,794,776],{"class":720},[484,796,797],{"class":486,"line":654},[484,798,529],{"emptyLinePlaceholder":528},[484,800,801],{"class":486,"line":660},[484,802,803],{"class":490},"  // Custom key generator\n",[484,805,806,809,811,814,818,820,823,826,829],{"class":486,"line":666},[484,807,808],{"class":554},"  keyGenerator",[484,810,736],{"class":501},[484,812,813],{"class":501}," (",[484,815,817],{"class":816},"sHdIc","event",[484,819,558],{"class":501},[484,821,822],{"class":720}," any",[484,824,825],{"class":501},")",[484,827,828],{"class":716}," =>",[484,830,831],{"class":720}," string\n",[484,833,835],{"class":486,"line":834},16,[484,836,529],{"emptyLinePlaceholder":528},[484,838,840],{"class":486,"line":839},17,[484,841,842],{"class":490},"  // Skip rate limiting for certain requests\n",[484,844,846,849,851,853,855,857,859,861,863],{"class":486,"line":845},18,[484,847,848],{"class":554},"  skip",[484,850,736],{"class":501},[484,852,813],{"class":501},[484,854,817],{"class":816},[484,856,558],{"class":501},[484,858,822],{"class":720},[484,860,825],{"class":501},[484,862,828],{"class":716},[484,864,776],{"class":720},[484,866,868],{"class":486,"line":867},19,[484,869,529],{"emptyLinePlaceholder":528},[484,871,873],{"class":486,"line":872},20,[484,874,875],{"class":490},"  // Custom error message\n",[484,877,879,882,884],{"class":486,"line":878},21,[484,880,881],{"class":554},"  message",[484,883,736],{"class":501},[484,885,831],{"class":720},[484,887,889],{"class":486,"line":888},22,[484,890,529],{"emptyLinePlaceholder":528},[484,892,894],{"class":486,"line":893},23,[484,895,896],{"class":490},"  // Custom storage backend\n",[484,898,900,903,905],{"class":486,"line":899},24,[484,901,902],{"class":554},"  store",[484,904,736],{"class":501},[484,906,907],{"class":720}," RateLimitStore\n",[484,909,911],{"class":486,"line":910},25,[484,912,913],{"class":501},"}\n",[464,915,917],{"id":916},"examples","Examples",[919,920,922],"h3",{"id":921},"different-limits-by-endpoint","Different Limits by Endpoint",[475,924,926],{"className":477,"code":925,"language":479,"meta":480,"style":480},"// server/plugins/rate-limit.ts\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('request', async (event) => {\n    if (event.path.startsWith('/api/auth/')) {\n      // Stricter limit for auth endpoints\n      await checkRateLimitWithConfig(event, {\n        windowMs: 60000,\n        max: 5  // Only 5 attempts per minute\n      })\n    } else if (event.path.startsWith('/api/')) {\n      // Normal limit for other endpoints\n      await checkRateLimit(event)\n    }\n  })\n})\n",[459,927,928,933,955,996,1029,1034,1050,1060,1072,1078,1114,1119,1132,1137,1144],{"__ignoreMap":480},[484,929,930],{"class":486,"line":487},[484,931,932],{"class":490},"// server/plugins/rate-limit.ts\n",[484,934,935,937,939,942,944,946,949,951,953],{"class":486,"line":494},[484,936,535],{"class":497},[484,938,538],{"class":497},[484,940,941],{"class":541}," defineNitroPlugin",[484,943,545],{"class":505},[484,945,545],{"class":501},[484,947,948],{"class":816},"nitroApp",[484,950,825],{"class":501},[484,952,828],{"class":716},[484,954,561],{"class":501},[484,956,957,960,963,966,968,971,973,976,979,981,983,986,988,990,992,994],{"class":486,"line":525},[484,958,959],{"class":505},"  nitroApp",[484,961,962],{"class":501},".",[484,964,965],{"class":505},"hooks",[484,967,962],{"class":501},[484,969,970],{"class":541},"hook",[484,972,545],{"class":554},[484,974,975],{"class":501},"'",[484,977,978],{"class":518},"request",[484,980,975],{"class":501},[484,982,597],{"class":501},[484,984,985],{"class":716}," async",[484,987,813],{"class":501},[484,989,817],{"class":816},[484,991,825],{"class":501},[484,993,828],{"class":716},[484,995,561],{"class":501},[484,997,998,1001,1003,1005,1007,1010,1012,1015,1017,1019,1022,1024,1027],{"class":486,"line":532},[484,999,1000],{"class":497},"    if",[484,1002,813],{"class":554},[484,1004,817],{"class":505},[484,1006,962],{"class":501},[484,1008,1009],{"class":505},"path",[484,1011,962],{"class":501},[484,1013,1014],{"class":541},"startsWith",[484,1016,545],{"class":554},[484,1018,975],{"class":501},[484,1020,1021],{"class":518},"/api/auth/",[484,1023,975],{"class":501},[484,1025,1026],{"class":554},")) ",[484,1028,548],{"class":501},[484,1030,1031],{"class":486,"line":551},[484,1032,1033],{"class":490},"      // Stricter limit for auth endpoints\n",[484,1035,1036,1039,1042,1044,1046,1048],{"class":486,"line":564},[484,1037,1038],{"class":497},"      await",[484,1040,1041],{"class":541}," checkRateLimitWithConfig",[484,1043,545],{"class":554},[484,1045,817],{"class":505},[484,1047,597],{"class":501},[484,1049,561],{"class":501},[484,1051,1052,1054,1056,1058],{"class":486,"line":575},[484,1053,588],{"class":554},[484,1055,558],{"class":501},[484,1057,594],{"class":593},[484,1059,631],{"class":501},[484,1061,1062,1064,1066,1069],{"class":486,"line":585},[484,1063,606],{"class":554},[484,1065,558],{"class":501},[484,1067,1068],{"class":593}," 5",[484,1070,1071],{"class":490},"  // Only 5 attempts per minute\n",[484,1073,1074,1076],{"class":486,"line":603},[484,1075,648],{"class":501},[484,1077,651],{"class":554},[484,1079,1080,1083,1086,1089,1091,1093,1095,1097,1099,1101,1103,1105,1108,1110,1112],{"class":486,"line":619},[484,1081,1082],{"class":501},"    }",[484,1084,1085],{"class":497}," else",[484,1087,1088],{"class":497}," if",[484,1090,813],{"class":554},[484,1092,817],{"class":505},[484,1094,962],{"class":501},[484,1096,1009],{"class":505},[484,1098,962],{"class":501},[484,1100,1014],{"class":541},[484,1102,545],{"class":554},[484,1104,975],{"class":501},[484,1106,1107],{"class":518},"/api/",[484,1109,975],{"class":501},[484,1111,1026],{"class":554},[484,1113,548],{"class":501},[484,1115,1116],{"class":486,"line":634},[484,1117,1118],{"class":490},"      // Normal limit for other endpoints\n",[484,1120,1121,1123,1126,1128,1130],{"class":486,"line":645},[484,1122,1038],{"class":497},[484,1124,1125],{"class":541}," checkRateLimit",[484,1127,545],{"class":554},[484,1129,817],{"class":505},[484,1131,651],{"class":554},[484,1133,1134],{"class":486,"line":654},[484,1135,1136],{"class":501},"    }\n",[484,1138,1139,1142],{"class":486,"line":660},[484,1140,1141],{"class":501},"  }",[484,1143,651],{"class":554},[484,1145,1146,1148],{"class":486,"line":666},[484,1147,669],{"class":501},[484,1149,651],{"class":505},[919,1151,1153],{"id":1152},"rate-limit-by-user","Rate Limit by User",[475,1155,1157],{"className":477,"code":1156,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 100,\n  byIp: false,\n  byUser: true  // Rate limit per authenticated user\n})\n",[459,1158,1159,1167,1177,1187,1198,1209],{"__ignoreMap":480},[484,1160,1161,1163,1165],{"class":486,"line":487},[484,1162,690],{"class":541},[484,1164,545],{"class":505},[484,1166,548],{"class":501},[484,1168,1169,1171,1173,1175],{"class":486,"line":494},[484,1170,733],{"class":554},[484,1172,558],{"class":501},[484,1174,594],{"class":593},[484,1176,631],{"class":501},[484,1178,1179,1181,1183,1185],{"class":486,"line":525},[484,1180,753],{"class":554},[484,1182,558],{"class":501},[484,1184,611],{"class":593},[484,1186,631],{"class":501},[484,1188,1189,1191,1193,1196],{"class":486,"line":532},[484,1190,771],{"class":554},[484,1192,558],{"class":501},[484,1194,1195],{"class":627}," false",[484,1197,631],{"class":501},[484,1199,1200,1202,1204,1206],{"class":486,"line":551},[484,1201,790],{"class":554},[484,1203,558],{"class":501},[484,1205,628],{"class":627},[484,1207,1208],{"class":490},"  // Rate limit per authenticated user\n",[484,1210,1211,1213],{"class":486,"line":564},[484,1212,669],{"class":501},[484,1214,651],{"class":505},[919,1216,1218],{"id":1217},"combined-ip-and-user","Combined IP and User",[475,1220,1222],{"className":477,"code":1221,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 100,\n  byIp: true,\n  byUser: true  // Both IP and user must be within limits\n})\n",[459,1223,1224,1232,1242,1252,1262,1273],{"__ignoreMap":480},[484,1225,1226,1228,1230],{"class":486,"line":487},[484,1227,690],{"class":541},[484,1229,545],{"class":505},[484,1231,548],{"class":501},[484,1233,1234,1236,1238,1240],{"class":486,"line":494},[484,1235,733],{"class":554},[484,1237,558],{"class":501},[484,1239,594],{"class":593},[484,1241,631],{"class":501},[484,1243,1244,1246,1248,1250],{"class":486,"line":525},[484,1245,753],{"class":554},[484,1247,558],{"class":501},[484,1249,611],{"class":593},[484,1251,631],{"class":501},[484,1253,1254,1256,1258,1260],{"class":486,"line":532},[484,1255,771],{"class":554},[484,1257,558],{"class":501},[484,1259,628],{"class":627},[484,1261,631],{"class":501},[484,1263,1264,1266,1268,1270],{"class":486,"line":551},[484,1265,790],{"class":554},[484,1267,558],{"class":501},[484,1269,628],{"class":627},[484,1271,1272],{"class":490},"  // Both IP and user must be within limits\n",[484,1274,1275,1277],{"class":486,"line":564},[484,1276,669],{"class":501},[484,1278,651],{"class":505},[919,1280,1282],{"id":1281},"custom-key-generator","Custom Key Generator",[475,1284,1286],{"className":477,"code":1285,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 100,\n  keyGenerator: (event) => {\n    // Rate limit by API key\n    const apiKey = event.node.req.headers['x-api-key']\n    return `apiKey:${apiKey}`\n  }\n})\n",[459,1287,1288,1296,1306,1316,1332,1337,1379,1399,1403],{"__ignoreMap":480},[484,1289,1290,1292,1294],{"class":486,"line":487},[484,1291,690],{"class":541},[484,1293,545],{"class":505},[484,1295,548],{"class":501},[484,1297,1298,1300,1302,1304],{"class":486,"line":494},[484,1299,733],{"class":554},[484,1301,558],{"class":501},[484,1303,594],{"class":593},[484,1305,631],{"class":501},[484,1307,1308,1310,1312,1314],{"class":486,"line":525},[484,1309,753],{"class":554},[484,1311,558],{"class":501},[484,1313,611],{"class":593},[484,1315,631],{"class":501},[484,1317,1318,1320,1322,1324,1326,1328,1330],{"class":486,"line":532},[484,1319,808],{"class":541},[484,1321,558],{"class":501},[484,1323,813],{"class":501},[484,1325,817],{"class":816},[484,1327,825],{"class":501},[484,1329,828],{"class":716},[484,1331,561],{"class":501},[484,1333,1334],{"class":486,"line":551},[484,1335,1336],{"class":490},"    // Rate limit by API key\n",[484,1338,1339,1342,1345,1348,1351,1353,1356,1358,1361,1363,1366,1369,1371,1374,1376],{"class":486,"line":564},[484,1340,1341],{"class":716},"    const",[484,1343,1344],{"class":505}," apiKey",[484,1346,1347],{"class":501}," =",[484,1349,1350],{"class":505}," event",[484,1352,962],{"class":501},[484,1354,1355],{"class":505},"node",[484,1357,962],{"class":501},[484,1359,1360],{"class":505},"req",[484,1362,962],{"class":501},[484,1364,1365],{"class":505},"headers",[484,1367,1368],{"class":554},"[",[484,1370,975],{"class":501},[484,1372,1373],{"class":518},"x-api-key",[484,1375,975],{"class":501},[484,1377,1378],{"class":554},"]\n",[484,1380,1381,1384,1387,1390,1393,1396],{"class":486,"line":575},[484,1382,1383],{"class":497},"    return",[484,1385,1386],{"class":501}," `",[484,1388,1389],{"class":518},"apiKey:",[484,1391,1392],{"class":501},"${",[484,1394,1395],{"class":505},"apiKey",[484,1397,1398],{"class":501},"}`\n",[484,1400,1401],{"class":486,"line":585},[484,1402,663],{"class":501},[484,1404,1405,1407],{"class":486,"line":603},[484,1406,669],{"class":501},[484,1408,651],{"class":505},[919,1410,1412],{"id":1411},"skip-certain-requests","Skip Certain Requests",[475,1414,1416],{"className":477,"code":1415,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 100,\n  skip: (event) => {\n    // Don't rate limit admins\n    return event.context.user?.role === 'admin'\n  }\n})\n",[459,1417,1418,1426,1436,1446,1462,1467,1499,1503],{"__ignoreMap":480},[484,1419,1420,1422,1424],{"class":486,"line":487},[484,1421,690],{"class":541},[484,1423,545],{"class":505},[484,1425,548],{"class":501},[484,1427,1428,1430,1432,1434],{"class":486,"line":494},[484,1429,733],{"class":554},[484,1431,558],{"class":501},[484,1433,594],{"class":593},[484,1435,631],{"class":501},[484,1437,1438,1440,1442,1444],{"class":486,"line":525},[484,1439,753],{"class":554},[484,1441,558],{"class":501},[484,1443,611],{"class":593},[484,1445,631],{"class":501},[484,1447,1448,1450,1452,1454,1456,1458,1460],{"class":486,"line":532},[484,1449,848],{"class":541},[484,1451,558],{"class":501},[484,1453,813],{"class":501},[484,1455,817],{"class":816},[484,1457,825],{"class":501},[484,1459,828],{"class":716},[484,1461,561],{"class":501},[484,1463,1464],{"class":486,"line":551},[484,1465,1466],{"class":490},"    // Don't rate limit admins\n",[484,1468,1469,1471,1473,1475,1478,1480,1483,1486,1489,1492,1494,1497],{"class":486,"line":564},[484,1470,1383],{"class":497},[484,1472,1350],{"class":505},[484,1474,962],{"class":501},[484,1476,1477],{"class":505},"context",[484,1479,962],{"class":501},[484,1481,1482],{"class":505},"user",[484,1484,1485],{"class":501},"?.",[484,1487,1488],{"class":505},"role",[484,1490,1491],{"class":501}," ===",[484,1493,515],{"class":501},[484,1495,1496],{"class":518},"admin",[484,1498,522],{"class":501},[484,1500,1501],{"class":486,"line":575},[484,1502,663],{"class":501},[484,1504,1505,1507],{"class":486,"line":585},[484,1506,669],{"class":501},[484,1508,651],{"class":505},[919,1510,1512],{"id":1511},"custom-error-message","Custom Error Message",[475,1514,1516],{"className":477,"code":1515,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 100,\n  message: 'You have exceeded the request limit. Please try again in a minute.'\n})\n",[459,1517,1518,1526,1536,1546,1559],{"__ignoreMap":480},[484,1519,1520,1522,1524],{"class":486,"line":487},[484,1521,690],{"class":541},[484,1523,545],{"class":505},[484,1525,548],{"class":501},[484,1527,1528,1530,1532,1534],{"class":486,"line":494},[484,1529,733],{"class":554},[484,1531,558],{"class":501},[484,1533,594],{"class":593},[484,1535,631],{"class":501},[484,1537,1538,1540,1542,1544],{"class":486,"line":525},[484,1539,753],{"class":554},[484,1541,558],{"class":501},[484,1543,611],{"class":593},[484,1545,631],{"class":501},[484,1547,1548,1550,1552,1554,1557],{"class":486,"line":532},[484,1549,881],{"class":554},[484,1551,558],{"class":501},[484,1553,515],{"class":501},[484,1555,1556],{"class":518},"You have exceeded the request limit. Please try again in a minute.",[484,1558,522],{"class":501},[484,1560,1561,1563],{"class":486,"line":551},[484,1562,669],{"class":501},[484,1564,651],{"class":505},[464,1566,1568],{"id":1567},"response-headers","Response Headers",[455,1570,1571],{},"Rate limit information is included in response headers:",[475,1573,1577],{"className":1574,"code":1575,"language":1576,"meta":480,"style":480},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","HTTP/1.1 200 OK\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 95\nX-RateLimit-Reset: 1704067200000\n","http",[459,1578,1579,1584,1589,1594],{"__ignoreMap":480},[484,1580,1581],{"class":486,"line":487},[484,1582,1583],{},"HTTP/1.1 200 OK\n",[484,1585,1586],{"class":486,"line":494},[484,1587,1588],{},"X-RateLimit-Limit: 100\n",[484,1590,1591],{"class":486,"line":525},[484,1592,1593],{},"X-RateLimit-Remaining: 95\n",[484,1595,1596],{"class":486,"line":532},[484,1597,1598],{},"X-RateLimit-Reset: 1704067200000\n",[455,1600,1601],{},"When limit is exceeded:",[475,1603,1605],{"className":1574,"code":1604,"language":1576,"meta":480,"style":480},"HTTP/1.1 429 Too Many Requests\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1704067200000\n\n{\n  \"statusCode\": 429,\n  \"statusMessage\": \"Too Many Requests\",\n  \"message\": \"Too many requests, please try again later\",\n  \"data\": {\n    \"limit\": 100,\n    \"windowMs\": 60000,\n    \"retryAfter\": 60\n  }\n}\n",[459,1606,1607,1612,1616,1621,1625,1629,1633,1638,1643,1648,1653,1658,1663,1668,1672],{"__ignoreMap":480},[484,1608,1609],{"class":486,"line":487},[484,1610,1611],{},"HTTP/1.1 429 Too Many Requests\n",[484,1613,1614],{"class":486,"line":494},[484,1615,1588],{},[484,1617,1618],{"class":486,"line":525},[484,1619,1620],{},"X-RateLimit-Remaining: 0\n",[484,1622,1623],{"class":486,"line":532},[484,1624,1598],{},[484,1626,1627],{"class":486,"line":551},[484,1628,529],{"emptyLinePlaceholder":528},[484,1630,1631],{"class":486,"line":564},[484,1632,548],{},[484,1634,1635],{"class":486,"line":575},[484,1636,1637],{},"  \"statusCode\": 429,\n",[484,1639,1640],{"class":486,"line":585},[484,1641,1642],{},"  \"statusMessage\": \"Too Many Requests\",\n",[484,1644,1645],{"class":486,"line":603},[484,1646,1647],{},"  \"message\": \"Too many requests, please try again later\",\n",[484,1649,1650],{"class":486,"line":619},[484,1651,1652],{},"  \"data\": {\n",[484,1654,1655],{"class":486,"line":634},[484,1656,1657],{},"    \"limit\": 100,\n",[484,1659,1660],{"class":486,"line":645},[484,1661,1662],{},"    \"windowMs\": 60000,\n",[484,1664,1665],{"class":486,"line":654},[484,1666,1667],{},"    \"retryAfter\": 60\n",[484,1669,1670],{"class":486,"line":660},[484,1671,663],{},[484,1673,1674],{"class":486,"line":666},[484,1675,913],{},[464,1677,1679],{"id":1678},"storage-backends","Storage Backends",[919,1681,1683],{"id":1682},"in-memory-default","In-Memory (Default)",[455,1685,1686],{},"Suitable for single-server deployments:",[475,1688,1690],{"className":477,"code":1689,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  // Uses in-memory store by default\n})\n",[459,1691,1692,1700,1705],{"__ignoreMap":480},[484,1693,1694,1696,1698],{"class":486,"line":487},[484,1695,690],{"class":541},[484,1697,545],{"class":505},[484,1699,548],{"class":501},[484,1701,1702],{"class":486,"line":494},[484,1703,1704],{"class":490},"  // Uses in-memory store by default\n",[484,1706,1707,1709],{"class":486,"line":525},[484,1708,669],{"class":501},[484,1710,651],{"class":505},[455,1712,1713],{},[684,1714,1715],{},"Pros:",[1717,1718,1719,1723,1726],"ul",{},[1720,1721,1722],"li",{},"Fast",[1720,1724,1725],{},"No external dependencies",[1720,1727,1728],{},"Automatic cleanup",[455,1730,1731],{},[684,1732,1733],{},"Cons:",[1717,1735,1736,1739,1742],{},[1720,1737,1738],{},"Not shared across multiple servers",[1720,1740,1741],{},"Lost on server restart",[1720,1743,1744],{},"Limited to server memory",[919,1746,1748],{"id":1747},"redis-recommended-for-production","Redis (Recommended for Production)",[455,1750,1751],{},"For multi-server deployments:",[475,1753,1755],{"className":477,"code":1754,"language":479,"meta":480,"style":480},"// server/utils/redis-rate-limit-store.ts\nimport { Redis } from 'ioredis'\n\nconst redis = new Redis(process.env.REDIS_URL)\n\nexport class RedisRateLimitStore {\n  async increment(key: string): Promise\u003Cnumber> {\n    const count = await redis.incr(key)\n\n    // Set TTL on first increment\n    if (count === 1) {\n      await redis.expire(key, 60)  // 60 seconds\n    }\n\n    return count\n  }\n\n  async reset(key: string): Promise\u003Cvoid> {\n    await redis.del(key)\n  }\n\n  async get(key: string): Promise\u003Cnumber> {\n    const count = await redis.get(key)\n    return count ? parseInt(count, 10) : 0\n  }\n}\n",[459,1756,1757,1762,1782,1786,1815,1819,1831,1866,1892,1896,1901,1920,1946,1950,1954,1961,1965,1969,1997,2015,2019,2023,2050,2073,2101,2105],{"__ignoreMap":480},[484,1758,1759],{"class":486,"line":487},[484,1760,1761],{"class":490},"// server/utils/redis-rate-limit-store.ts\n",[484,1763,1764,1766,1768,1771,1773,1775,1777,1780],{"class":486,"line":494},[484,1765,498],{"class":497},[484,1767,502],{"class":501},[484,1769,1770],{"class":505}," Redis",[484,1772,509],{"class":501},[484,1774,512],{"class":497},[484,1776,515],{"class":501},[484,1778,1779],{"class":518},"ioredis",[484,1781,522],{"class":501},[484,1783,1784],{"class":486,"line":525},[484,1785,529],{"emptyLinePlaceholder":528},[484,1787,1788,1791,1794,1797,1800,1802,1805,1807,1810,1812],{"class":486,"line":532},[484,1789,1790],{"class":716},"const",[484,1792,1793],{"class":505}," redis ",[484,1795,1796],{"class":501},"=",[484,1798,1799],{"class":501}," new",[484,1801,1770],{"class":541},[484,1803,1804],{"class":505},"(process",[484,1806,962],{"class":501},[484,1808,1809],{"class":505},"env",[484,1811,962],{"class":501},[484,1813,1814],{"class":505},"REDIS_URL)\n",[484,1816,1817],{"class":486,"line":551},[484,1818,529],{"emptyLinePlaceholder":528},[484,1820,1821,1823,1826,1829],{"class":486,"line":564},[484,1822,535],{"class":497},[484,1824,1825],{"class":716}," class",[484,1827,1828],{"class":720}," RedisRateLimitStore",[484,1830,561],{"class":501},[484,1832,1833,1836,1839,1841,1844,1846,1849,1852,1855,1858,1861,1864],{"class":486,"line":575},[484,1834,1835],{"class":716},"  async",[484,1837,1838],{"class":554}," increment",[484,1840,545],{"class":501},[484,1842,1843],{"class":816},"key",[484,1845,558],{"class":501},[484,1847,1848],{"class":720}," string",[484,1850,1851],{"class":501},"):",[484,1853,1854],{"class":720}," Promise",[484,1856,1857],{"class":501},"\u003C",[484,1859,1860],{"class":720},"number",[484,1862,1863],{"class":501},">",[484,1865,561],{"class":501},[484,1867,1868,1870,1873,1875,1878,1881,1883,1886,1888,1890],{"class":486,"line":585},[484,1869,1341],{"class":716},[484,1871,1872],{"class":505}," count",[484,1874,1347],{"class":501},[484,1876,1877],{"class":497}," await",[484,1879,1880],{"class":505}," redis",[484,1882,962],{"class":501},[484,1884,1885],{"class":541},"incr",[484,1887,545],{"class":554},[484,1889,1843],{"class":505},[484,1891,651],{"class":554},[484,1893,1894],{"class":486,"line":603},[484,1895,529],{"emptyLinePlaceholder":528},[484,1897,1898],{"class":486,"line":619},[484,1899,1900],{"class":490},"    // Set TTL on first increment\n",[484,1902,1903,1905,1907,1910,1912,1915,1918],{"class":486,"line":634},[484,1904,1000],{"class":497},[484,1906,813],{"class":554},[484,1908,1909],{"class":505},"count",[484,1911,1491],{"class":501},[484,1913,1914],{"class":593}," 1",[484,1916,1917],{"class":554},") ",[484,1919,548],{"class":501},[484,1921,1922,1924,1926,1928,1931,1933,1935,1937,1940,1943],{"class":486,"line":645},[484,1923,1038],{"class":497},[484,1925,1880],{"class":505},[484,1927,962],{"class":501},[484,1929,1930],{"class":541},"expire",[484,1932,545],{"class":554},[484,1934,1843],{"class":505},[484,1936,597],{"class":501},[484,1938,1939],{"class":593}," 60",[484,1941,1942],{"class":554},")  ",[484,1944,1945],{"class":490},"// 60 seconds\n",[484,1947,1948],{"class":486,"line":654},[484,1949,1136],{"class":501},[484,1951,1952],{"class":486,"line":660},[484,1953,529],{"emptyLinePlaceholder":528},[484,1955,1956,1958],{"class":486,"line":666},[484,1957,1383],{"class":497},[484,1959,1960],{"class":505}," count\n",[484,1962,1963],{"class":486,"line":834},[484,1964,663],{"class":501},[484,1966,1967],{"class":486,"line":839},[484,1968,529],{"emptyLinePlaceholder":528},[484,1970,1971,1973,1976,1978,1980,1982,1984,1986,1988,1990,1993,1995],{"class":486,"line":845},[484,1972,1835],{"class":716},[484,1974,1975],{"class":554}," reset",[484,1977,545],{"class":501},[484,1979,1843],{"class":816},[484,1981,558],{"class":501},[484,1983,1848],{"class":720},[484,1985,1851],{"class":501},[484,1987,1854],{"class":720},[484,1989,1857],{"class":501},[484,1991,1992],{"class":720},"void",[484,1994,1863],{"class":501},[484,1996,561],{"class":501},[484,1998,1999,2002,2004,2006,2009,2011,2013],{"class":486,"line":867},[484,2000,2001],{"class":497},"    await",[484,2003,1880],{"class":505},[484,2005,962],{"class":501},[484,2007,2008],{"class":541},"del",[484,2010,545],{"class":554},[484,2012,1843],{"class":505},[484,2014,651],{"class":554},[484,2016,2017],{"class":486,"line":872},[484,2018,663],{"class":501},[484,2020,2021],{"class":486,"line":878},[484,2022,529],{"emptyLinePlaceholder":528},[484,2024,2025,2027,2030,2032,2034,2036,2038,2040,2042,2044,2046,2048],{"class":486,"line":888},[484,2026,1835],{"class":716},[484,2028,2029],{"class":554}," get",[484,2031,545],{"class":501},[484,2033,1843],{"class":816},[484,2035,558],{"class":501},[484,2037,1848],{"class":720},[484,2039,1851],{"class":501},[484,2041,1854],{"class":720},[484,2043,1857],{"class":501},[484,2045,1860],{"class":720},[484,2047,1863],{"class":501},[484,2049,561],{"class":501},[484,2051,2052,2054,2056,2058,2060,2062,2064,2067,2069,2071],{"class":486,"line":893},[484,2053,1341],{"class":716},[484,2055,1872],{"class":505},[484,2057,1347],{"class":501},[484,2059,1877],{"class":497},[484,2061,1880],{"class":505},[484,2063,962],{"class":501},[484,2065,2066],{"class":541},"get",[484,2068,545],{"class":554},[484,2070,1843],{"class":505},[484,2072,651],{"class":554},[484,2074,2075,2077,2079,2082,2085,2087,2089,2091,2094,2096,2098],{"class":486,"line":899},[484,2076,1383],{"class":497},[484,2078,1872],{"class":505},[484,2080,2081],{"class":501}," ?",[484,2083,2084],{"class":541}," parseInt",[484,2086,545],{"class":554},[484,2088,1909],{"class":505},[484,2090,597],{"class":501},[484,2092,2093],{"class":593}," 10",[484,2095,1917],{"class":554},[484,2097,558],{"class":501},[484,2099,2100],{"class":593}," 0\n",[484,2102,2103],{"class":486,"line":910},[484,2104,663],{"class":501},[484,2106,2108],{"class":486,"line":2107},26,[484,2109,913],{"class":501},[475,2111,2113],{"className":477,"code":2112,"language":479,"meta":480,"style":480},"// nuxt.config.ts\nimport { createRateLimitExtension } from '@websideproject/nuxt-auto-api/extensions/rate-limiting'\nimport { RedisRateLimitStore } from './server/utils/redis-rate-limit-store'\n\nexport default defineNuxtConfig({\n  autoApi: {\n    extensions: [\n      createRateLimitExtension({\n        store: new RedisRateLimitStore()\n      })\n    ]\n  }\n})\n",[459,2114,2115,2119,2139,2158,2162,2174,2182,2191,2200,2214,2220,2224,2228],{"__ignoreMap":480},[484,2116,2117],{"class":486,"line":487},[484,2118,491],{"class":490},[484,2120,2121,2123,2125,2128,2130,2132,2134,2137],{"class":486,"line":494},[484,2122,498],{"class":497},[484,2124,502],{"class":501},[484,2126,2127],{"class":505}," createRateLimitExtension",[484,2129,509],{"class":501},[484,2131,512],{"class":497},[484,2133,515],{"class":501},[484,2135,2136],{"class":518},"@websideproject/nuxt-auto-api/extensions/rate-limiting",[484,2138,522],{"class":501},[484,2140,2141,2143,2145,2147,2149,2151,2153,2156],{"class":486,"line":525},[484,2142,498],{"class":497},[484,2144,502],{"class":501},[484,2146,1828],{"class":505},[484,2148,509],{"class":501},[484,2150,512],{"class":497},[484,2152,515],{"class":501},[484,2154,2155],{"class":518},"./server/utils/redis-rate-limit-store",[484,2157,522],{"class":501},[484,2159,2160],{"class":486,"line":532},[484,2161,529],{"emptyLinePlaceholder":528},[484,2163,2164,2166,2168,2170,2172],{"class":486,"line":551},[484,2165,535],{"class":497},[484,2167,538],{"class":497},[484,2169,542],{"class":541},[484,2171,545],{"class":505},[484,2173,548],{"class":501},[484,2175,2176,2178,2180],{"class":486,"line":564},[484,2177,555],{"class":554},[484,2179,558],{"class":501},[484,2181,561],{"class":501},[484,2183,2184,2187,2189],{"class":486,"line":575},[484,2185,2186],{"class":554},"    extensions",[484,2188,558],{"class":501},[484,2190,572],{"class":505},[484,2192,2193,2196,2198],{"class":486,"line":585},[484,2194,2195],{"class":541},"      createRateLimitExtension",[484,2197,545],{"class":505},[484,2199,548],{"class":501},[484,2201,2202,2205,2207,2209,2211],{"class":486,"line":603},[484,2203,2204],{"class":554},"        store",[484,2206,558],{"class":501},[484,2208,1799],{"class":501},[484,2210,1828],{"class":541},[484,2212,2213],{"class":505},"()\n",[484,2215,2216,2218],{"class":486,"line":619},[484,2217,648],{"class":501},[484,2219,651],{"class":505},[484,2221,2222],{"class":486,"line":634},[484,2223,657],{"class":505},[484,2225,2226],{"class":486,"line":645},[484,2227,663],{"class":501},[484,2229,2230,2232],{"class":486,"line":654},[484,2231,669],{"class":501},[484,2233,651],{"class":505},[455,2235,2236],{},[684,2237,1715],{},[1717,2239,2240,2243,2246],{},[1720,2241,2242],{},"Shared across multiple servers",[1720,2244,2245],{},"Persists across restarts",[1720,2247,2248],{},"Scalable",[455,2250,2251],{},[684,2252,1733],{},[1717,2254,2255,2258],{},[1720,2256,2257],{},"Requires Redis server",[1720,2259,2260],{},"Slightly slower than in-memory",[464,2262,2264],{"id":2263},"advanced-patterns","Advanced Patterns",[919,2266,2268],{"id":2267},"different-limits-for-different-users","Different Limits for Different Users",[475,2270,2272],{"className":477,"code":2271,"language":479,"meta":480,"style":480},"// server/plugins/rate-limit.ts\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('request', async (event) => {\n    if (!event.path.startsWith('/api/')) return\n\n    const user = event.context.user\n\n    let limit = 100  // Default\n\n    if (user?.plan === 'premium') {\n      limit = 1000\n    } else if (user?.plan === 'enterprise') {\n      limit = 10000\n    }\n\n    await checkRateLimitWithConfig(event, {\n      windowMs: 60000,\n      max: limit,\n      keyGenerator: () => `user:${user?.id || 'anonymous'}`\n    })\n  })\n})\n",[459,2273,2274,2278,2298,2332,2364,2368,2388,2392,2407,2411,2437,2447,2476,2485,2489,2493,2507,2518,2529,2566,2572,2578],{"__ignoreMap":480},[484,2275,2276],{"class":486,"line":487},[484,2277,932],{"class":490},[484,2279,2280,2282,2284,2286,2288,2290,2292,2294,2296],{"class":486,"line":494},[484,2281,535],{"class":497},[484,2283,538],{"class":497},[484,2285,941],{"class":541},[484,2287,545],{"class":505},[484,2289,545],{"class":501},[484,2291,948],{"class":816},[484,2293,825],{"class":501},[484,2295,828],{"class":716},[484,2297,561],{"class":501},[484,2299,2300,2302,2304,2306,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330],{"class":486,"line":525},[484,2301,959],{"class":505},[484,2303,962],{"class":501},[484,2305,965],{"class":505},[484,2307,962],{"class":501},[484,2309,970],{"class":541},[484,2311,545],{"class":554},[484,2313,975],{"class":501},[484,2315,978],{"class":518},[484,2317,975],{"class":501},[484,2319,597],{"class":501},[484,2321,985],{"class":716},[484,2323,813],{"class":501},[484,2325,817],{"class":816},[484,2327,825],{"class":501},[484,2329,828],{"class":716},[484,2331,561],{"class":501},[484,2333,2334,2336,2338,2341,2343,2345,2347,2349,2351,2353,2355,2357,2359,2361],{"class":486,"line":532},[484,2335,1000],{"class":497},[484,2337,813],{"class":554},[484,2339,2340],{"class":501},"!",[484,2342,817],{"class":505},[484,2344,962],{"class":501},[484,2346,1009],{"class":505},[484,2348,962],{"class":501},[484,2350,1014],{"class":541},[484,2352,545],{"class":554},[484,2354,975],{"class":501},[484,2356,1107],{"class":518},[484,2358,975],{"class":501},[484,2360,1026],{"class":554},[484,2362,2363],{"class":497},"return\n",[484,2365,2366],{"class":486,"line":551},[484,2367,529],{"emptyLinePlaceholder":528},[484,2369,2370,2372,2375,2377,2379,2381,2383,2385],{"class":486,"line":564},[484,2371,1341],{"class":716},[484,2373,2374],{"class":505}," user",[484,2376,1347],{"class":501},[484,2378,1350],{"class":505},[484,2380,962],{"class":501},[484,2382,1477],{"class":505},[484,2384,962],{"class":501},[484,2386,2387],{"class":505},"user\n",[484,2389,2390],{"class":486,"line":575},[484,2391,529],{"emptyLinePlaceholder":528},[484,2393,2394,2397,2400,2402,2404],{"class":486,"line":585},[484,2395,2396],{"class":716},"    let",[484,2398,2399],{"class":505}," limit",[484,2401,1347],{"class":501},[484,2403,611],{"class":593},[484,2405,2406],{"class":490},"  // Default\n",[484,2408,2409],{"class":486,"line":603},[484,2410,529],{"emptyLinePlaceholder":528},[484,2412,2413,2415,2417,2419,2421,2424,2426,2428,2431,2433,2435],{"class":486,"line":619},[484,2414,1000],{"class":497},[484,2416,813],{"class":554},[484,2418,1482],{"class":505},[484,2420,1485],{"class":501},[484,2422,2423],{"class":505},"plan",[484,2425,1491],{"class":501},[484,2427,515],{"class":501},[484,2429,2430],{"class":518},"premium",[484,2432,975],{"class":501},[484,2434,1917],{"class":554},[484,2436,548],{"class":501},[484,2438,2439,2442,2444],{"class":486,"line":634},[484,2440,2441],{"class":505},"      limit",[484,2443,1347],{"class":501},[484,2445,2446],{"class":593}," 1000\n",[484,2448,2449,2451,2453,2455,2457,2459,2461,2463,2465,2467,2470,2472,2474],{"class":486,"line":645},[484,2450,1082],{"class":501},[484,2452,1085],{"class":497},[484,2454,1088],{"class":497},[484,2456,813],{"class":554},[484,2458,1482],{"class":505},[484,2460,1485],{"class":501},[484,2462,2423],{"class":505},[484,2464,1491],{"class":501},[484,2466,515],{"class":501},[484,2468,2469],{"class":518},"enterprise",[484,2471,975],{"class":501},[484,2473,1917],{"class":554},[484,2475,548],{"class":501},[484,2477,2478,2480,2482],{"class":486,"line":654},[484,2479,2441],{"class":505},[484,2481,1347],{"class":501},[484,2483,2484],{"class":593}," 10000\n",[484,2486,2487],{"class":486,"line":660},[484,2488,1136],{"class":501},[484,2490,2491],{"class":486,"line":666},[484,2492,529],{"emptyLinePlaceholder":528},[484,2494,2495,2497,2499,2501,2503,2505],{"class":486,"line":834},[484,2496,2001],{"class":497},[484,2498,1041],{"class":541},[484,2500,545],{"class":554},[484,2502,817],{"class":505},[484,2504,597],{"class":501},[484,2506,561],{"class":501},[484,2508,2509,2512,2514,2516],{"class":486,"line":839},[484,2510,2511],{"class":554},"      windowMs",[484,2513,558],{"class":501},[484,2515,594],{"class":593},[484,2517,631],{"class":501},[484,2519,2520,2523,2525,2527],{"class":486,"line":845},[484,2521,2522],{"class":554},"      max",[484,2524,558],{"class":501},[484,2526,2399],{"class":505},[484,2528,631],{"class":501},[484,2530,2531,2534,2536,2539,2541,2543,2546,2548,2550,2552,2555,2558,2560,2563],{"class":486,"line":867},[484,2532,2533],{"class":541},"      keyGenerator",[484,2535,558],{"class":501},[484,2537,2538],{"class":501}," ()",[484,2540,828],{"class":716},[484,2542,1386],{"class":501},[484,2544,2545],{"class":518},"user:",[484,2547,1392],{"class":501},[484,2549,1482],{"class":505},[484,2551,1485],{"class":501},[484,2553,2554],{"class":505},"id ",[484,2556,2557],{"class":501},"||",[484,2559,515],{"class":501},[484,2561,2562],{"class":518},"anonymous",[484,2564,2565],{"class":501},"'}`\n",[484,2567,2568,2570],{"class":486,"line":872},[484,2569,1082],{"class":501},[484,2571,651],{"class":554},[484,2573,2574,2576],{"class":486,"line":878},[484,2575,1141],{"class":501},[484,2577,651],{"class":554},[484,2579,2580,2582],{"class":486,"line":888},[484,2581,669],{"class":501},[484,2583,651],{"class":505},[919,2585,2587],{"id":2586},"burst-vs-sustained-rate","Burst vs Sustained Rate",[475,2589,2591],{"className":477,"code":2590,"language":479,"meta":480,"style":480},"// Allow bursts but limit sustained rate\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('request', async (event) => {\n    if (!event.path.startsWith('/api/')) return\n\n    // Short-term burst limit: 20 requests per 10 seconds\n    await checkRateLimitWithConfig(event, {\n      windowMs: 10000,\n      max: 20,\n      keyGenerator: (e) => `burst:${getClientIP(e)}`\n    })\n\n    // Long-term sustained limit: 100 requests per minute\n    await checkRateLimitWithConfig(event, {\n      windowMs: 60000,\n      max: 100,\n      keyGenerator: (e) => `sustained:${getClientIP(e)}`\n    })\n  })\n})\n",[459,2592,2593,2598,2618,2652,2682,2686,2691,2705,2716,2727,2757,2763,2767,2772,2786,2796,2806,2833,2839,2845],{"__ignoreMap":480},[484,2594,2595],{"class":486,"line":487},[484,2596,2597],{"class":490},"// Allow bursts but limit sustained rate\n",[484,2599,2600,2602,2604,2606,2608,2610,2612,2614,2616],{"class":486,"line":494},[484,2601,535],{"class":497},[484,2603,538],{"class":497},[484,2605,941],{"class":541},[484,2607,545],{"class":505},[484,2609,545],{"class":501},[484,2611,948],{"class":816},[484,2613,825],{"class":501},[484,2615,828],{"class":716},[484,2617,561],{"class":501},[484,2619,2620,2622,2624,2626,2628,2630,2632,2634,2636,2638,2640,2642,2644,2646,2648,2650],{"class":486,"line":525},[484,2621,959],{"class":505},[484,2623,962],{"class":501},[484,2625,965],{"class":505},[484,2627,962],{"class":501},[484,2629,970],{"class":541},[484,2631,545],{"class":554},[484,2633,975],{"class":501},[484,2635,978],{"class":518},[484,2637,975],{"class":501},[484,2639,597],{"class":501},[484,2641,985],{"class":716},[484,2643,813],{"class":501},[484,2645,817],{"class":816},[484,2647,825],{"class":501},[484,2649,828],{"class":716},[484,2651,561],{"class":501},[484,2653,2654,2656,2658,2660,2662,2664,2666,2668,2670,2672,2674,2676,2678,2680],{"class":486,"line":532},[484,2655,1000],{"class":497},[484,2657,813],{"class":554},[484,2659,2340],{"class":501},[484,2661,817],{"class":505},[484,2663,962],{"class":501},[484,2665,1009],{"class":505},[484,2667,962],{"class":501},[484,2669,1014],{"class":541},[484,2671,545],{"class":554},[484,2673,975],{"class":501},[484,2675,1107],{"class":518},[484,2677,975],{"class":501},[484,2679,1026],{"class":554},[484,2681,2363],{"class":497},[484,2683,2684],{"class":486,"line":551},[484,2685,529],{"emptyLinePlaceholder":528},[484,2687,2688],{"class":486,"line":564},[484,2689,2690],{"class":490},"    // Short-term burst limit: 20 requests per 10 seconds\n",[484,2692,2693,2695,2697,2699,2701,2703],{"class":486,"line":575},[484,2694,2001],{"class":497},[484,2696,1041],{"class":541},[484,2698,545],{"class":554},[484,2700,817],{"class":505},[484,2702,597],{"class":501},[484,2704,561],{"class":501},[484,2706,2707,2709,2711,2714],{"class":486,"line":585},[484,2708,2511],{"class":554},[484,2710,558],{"class":501},[484,2712,2713],{"class":593}," 10000",[484,2715,631],{"class":501},[484,2717,2718,2720,2722,2725],{"class":486,"line":603},[484,2719,2522],{"class":554},[484,2721,558],{"class":501},[484,2723,2724],{"class":593}," 20",[484,2726,631],{"class":501},[484,2728,2729,2731,2733,2735,2738,2740,2742,2744,2747,2749,2752,2755],{"class":486,"line":619},[484,2730,2533],{"class":541},[484,2732,558],{"class":501},[484,2734,813],{"class":501},[484,2736,2737],{"class":816},"e",[484,2739,825],{"class":501},[484,2741,828],{"class":716},[484,2743,1386],{"class":501},[484,2745,2746],{"class":518},"burst:",[484,2748,1392],{"class":501},[484,2750,2751],{"class":541},"getClientIP",[484,2753,2754],{"class":505},"(e)",[484,2756,1398],{"class":501},[484,2758,2759,2761],{"class":486,"line":634},[484,2760,1082],{"class":501},[484,2762,651],{"class":554},[484,2764,2765],{"class":486,"line":645},[484,2766,529],{"emptyLinePlaceholder":528},[484,2768,2769],{"class":486,"line":654},[484,2770,2771],{"class":490},"    // Long-term sustained limit: 100 requests per minute\n",[484,2773,2774,2776,2778,2780,2782,2784],{"class":486,"line":660},[484,2775,2001],{"class":497},[484,2777,1041],{"class":541},[484,2779,545],{"class":554},[484,2781,817],{"class":505},[484,2783,597],{"class":501},[484,2785,561],{"class":501},[484,2787,2788,2790,2792,2794],{"class":486,"line":666},[484,2789,2511],{"class":554},[484,2791,558],{"class":501},[484,2793,594],{"class":593},[484,2795,631],{"class":501},[484,2797,2798,2800,2802,2804],{"class":486,"line":834},[484,2799,2522],{"class":554},[484,2801,558],{"class":501},[484,2803,611],{"class":593},[484,2805,631],{"class":501},[484,2807,2808,2810,2812,2814,2816,2818,2820,2822,2825,2827,2829,2831],{"class":486,"line":839},[484,2809,2533],{"class":541},[484,2811,558],{"class":501},[484,2813,813],{"class":501},[484,2815,2737],{"class":816},[484,2817,825],{"class":501},[484,2819,828],{"class":716},[484,2821,1386],{"class":501},[484,2823,2824],{"class":518},"sustained:",[484,2826,1392],{"class":501},[484,2828,2751],{"class":541},[484,2830,2754],{"class":505},[484,2832,1398],{"class":501},[484,2834,2835,2837],{"class":486,"line":845},[484,2836,1082],{"class":501},[484,2838,651],{"class":554},[484,2840,2841,2843],{"class":486,"line":867},[484,2842,1141],{"class":501},[484,2844,651],{"class":554},[484,2846,2847,2849],{"class":486,"line":872},[484,2848,669],{"class":501},[484,2850,651],{"class":505},[919,2852,2854],{"id":2853},"progressive-rate-limiting","Progressive Rate Limiting",[475,2856,2858],{"className":477,"code":2857,"language":479,"meta":480,"style":480},"// Increase limits based on usage\nconst getUserRateLimit = async (userId: string) => {\n  const usage = await db.query.usage.findFirst({\n    where: eq(usage.userId, userId)\n  })\n\n  if (!usage) return 100\n\n  // Increase limit for active users\n  if (usage.requestCount > 10000) {\n    return 500\n  } else if (usage.requestCount > 1000) {\n    return 200\n  }\n\n  return 100\n}\n",[459,2859,2860,2865,2891,2925,2950,2956,2960,2979,2983,2988,3010,3017,3042,3049,3053,3057,3064],{"__ignoreMap":480},[484,2861,2862],{"class":486,"line":487},[484,2863,2864],{"class":490},"// Increase limits based on usage\n",[484,2866,2867,2869,2872,2874,2876,2878,2881,2883,2885,2887,2889],{"class":486,"line":494},[484,2868,1790],{"class":716},[484,2870,2871],{"class":505}," getUserRateLimit ",[484,2873,1796],{"class":501},[484,2875,985],{"class":716},[484,2877,813],{"class":501},[484,2879,2880],{"class":816},"userId",[484,2882,558],{"class":501},[484,2884,1848],{"class":720},[484,2886,825],{"class":501},[484,2888,828],{"class":716},[484,2890,561],{"class":501},[484,2892,2893,2896,2899,2901,2903,2906,2908,2911,2913,2916,2918,2921,2923],{"class":486,"line":525},[484,2894,2895],{"class":716},"  const",[484,2897,2898],{"class":505}," usage",[484,2900,1347],{"class":501},[484,2902,1877],{"class":497},[484,2904,2905],{"class":505}," db",[484,2907,962],{"class":501},[484,2909,2910],{"class":505},"query",[484,2912,962],{"class":501},[484,2914,2915],{"class":505},"usage",[484,2917,962],{"class":501},[484,2919,2920],{"class":541},"findFirst",[484,2922,545],{"class":554},[484,2924,548],{"class":501},[484,2926,2927,2930,2932,2935,2937,2939,2941,2943,2945,2948],{"class":486,"line":532},[484,2928,2929],{"class":554},"    where",[484,2931,558],{"class":501},[484,2933,2934],{"class":541}," eq",[484,2936,545],{"class":554},[484,2938,2915],{"class":505},[484,2940,962],{"class":501},[484,2942,2880],{"class":505},[484,2944,597],{"class":501},[484,2946,2947],{"class":505}," userId",[484,2949,651],{"class":554},[484,2951,2952,2954],{"class":486,"line":551},[484,2953,1141],{"class":501},[484,2955,651],{"class":554},[484,2957,2958],{"class":486,"line":564},[484,2959,529],{"emptyLinePlaceholder":528},[484,2961,2962,2965,2967,2969,2971,2973,2976],{"class":486,"line":575},[484,2963,2964],{"class":497},"  if",[484,2966,813],{"class":554},[484,2968,2340],{"class":501},[484,2970,2915],{"class":505},[484,2972,1917],{"class":554},[484,2974,2975],{"class":497},"return",[484,2977,2978],{"class":593}," 100\n",[484,2980,2981],{"class":486,"line":585},[484,2982,529],{"emptyLinePlaceholder":528},[484,2984,2985],{"class":486,"line":603},[484,2986,2987],{"class":490},"  // Increase limit for active users\n",[484,2989,2990,2992,2994,2996,2998,3001,3004,3006,3008],{"class":486,"line":619},[484,2991,2964],{"class":497},[484,2993,813],{"class":554},[484,2995,2915],{"class":505},[484,2997,962],{"class":501},[484,2999,3000],{"class":505},"requestCount",[484,3002,3003],{"class":501}," >",[484,3005,2713],{"class":593},[484,3007,1917],{"class":554},[484,3009,548],{"class":501},[484,3011,3012,3014],{"class":486,"line":634},[484,3013,1383],{"class":497},[484,3015,3016],{"class":593}," 500\n",[484,3018,3019,3021,3023,3025,3027,3029,3031,3033,3035,3038,3040],{"class":486,"line":645},[484,3020,1141],{"class":501},[484,3022,1085],{"class":497},[484,3024,1088],{"class":497},[484,3026,813],{"class":554},[484,3028,2915],{"class":505},[484,3030,962],{"class":501},[484,3032,3000],{"class":505},[484,3034,3003],{"class":501},[484,3036,3037],{"class":593}," 1000",[484,3039,1917],{"class":554},[484,3041,548],{"class":501},[484,3043,3044,3046],{"class":486,"line":654},[484,3045,1383],{"class":497},[484,3047,3048],{"class":593}," 200\n",[484,3050,3051],{"class":486,"line":660},[484,3052,663],{"class":501},[484,3054,3055],{"class":486,"line":666},[484,3056,529],{"emptyLinePlaceholder":528},[484,3058,3059,3062],{"class":486,"line":834},[484,3060,3061],{"class":497},"  return",[484,3063,2978],{"class":593},[484,3065,3066],{"class":486,"line":839},[484,3067,913],{"class":501},[919,3069,3071],{"id":3070},"per-endpoint-limits","Per-Endpoint Limits",[475,3073,3075],{"className":477,"code":3074,"language":479,"meta":480,"style":480},"const ENDPOINT_LIMITS = {\n  '/api/search': { windowMs: 60000, max: 10 },\n  '/api/export': { windowMs: 3600000, max: 5 },\n  '/api/upload': { windowMs: 60000, max: 20 },\n  default: { windowMs: 60000, max: 100 }\n}\n\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('request', async (event) => {\n    const config = ENDPOINT_LIMITS[event.path] || ENDPOINT_LIMITS.default\n\n    await checkRateLimitWithConfig(event, config)\n  })\n})\n",[459,3076,3077,3088,3121,3151,3180,3206,3210,3214,3234,3268,3300,3304,3320,3326],{"__ignoreMap":480},[484,3078,3079,3081,3084,3086],{"class":486,"line":487},[484,3080,1790],{"class":716},[484,3082,3083],{"class":505}," ENDPOINT_LIMITS ",[484,3085,1796],{"class":501},[484,3087,561],{"class":501},[484,3089,3090,3093,3096,3098,3100,3102,3105,3107,3109,3111,3114,3116,3118],{"class":486,"line":494},[484,3091,3092],{"class":501},"  '",[484,3094,3095],{"class":554},"/api/search",[484,3097,975],{"class":501},[484,3099,558],{"class":501},[484,3101,502],{"class":501},[484,3103,3104],{"class":554}," windowMs",[484,3106,558],{"class":501},[484,3108,594],{"class":593},[484,3110,597],{"class":501},[484,3112,3113],{"class":554}," max",[484,3115,558],{"class":501},[484,3117,2093],{"class":593},[484,3119,3120],{"class":501}," },\n",[484,3122,3123,3125,3128,3130,3132,3134,3136,3138,3141,3143,3145,3147,3149],{"class":486,"line":525},[484,3124,3092],{"class":501},[484,3126,3127],{"class":554},"/api/export",[484,3129,975],{"class":501},[484,3131,558],{"class":501},[484,3133,502],{"class":501},[484,3135,3104],{"class":554},[484,3137,558],{"class":501},[484,3139,3140],{"class":593}," 3600000",[484,3142,597],{"class":501},[484,3144,3113],{"class":554},[484,3146,558],{"class":501},[484,3148,1068],{"class":593},[484,3150,3120],{"class":501},[484,3152,3153,3155,3158,3160,3162,3164,3166,3168,3170,3172,3174,3176,3178],{"class":486,"line":532},[484,3154,3092],{"class":501},[484,3156,3157],{"class":554},"/api/upload",[484,3159,975],{"class":501},[484,3161,558],{"class":501},[484,3163,502],{"class":501},[484,3165,3104],{"class":554},[484,3167,558],{"class":501},[484,3169,594],{"class":593},[484,3171,597],{"class":501},[484,3173,3113],{"class":554},[484,3175,558],{"class":501},[484,3177,2724],{"class":593},[484,3179,3120],{"class":501},[484,3181,3182,3185,3187,3189,3191,3193,3195,3197,3199,3201,3203],{"class":486,"line":551},[484,3183,3184],{"class":554},"  default",[484,3186,558],{"class":501},[484,3188,502],{"class":501},[484,3190,3104],{"class":554},[484,3192,558],{"class":501},[484,3194,594],{"class":593},[484,3196,597],{"class":501},[484,3198,3113],{"class":554},[484,3200,558],{"class":501},[484,3202,611],{"class":593},[484,3204,3205],{"class":501}," }\n",[484,3207,3208],{"class":486,"line":564},[484,3209,913],{"class":501},[484,3211,3212],{"class":486,"line":575},[484,3213,529],{"emptyLinePlaceholder":528},[484,3215,3216,3218,3220,3222,3224,3226,3228,3230,3232],{"class":486,"line":585},[484,3217,535],{"class":497},[484,3219,538],{"class":497},[484,3221,941],{"class":541},[484,3223,545],{"class":505},[484,3225,545],{"class":501},[484,3227,948],{"class":816},[484,3229,825],{"class":501},[484,3231,828],{"class":716},[484,3233,561],{"class":501},[484,3235,3236,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,3258,3260,3262,3264,3266],{"class":486,"line":603},[484,3237,959],{"class":505},[484,3239,962],{"class":501},[484,3241,965],{"class":505},[484,3243,962],{"class":501},[484,3245,970],{"class":541},[484,3247,545],{"class":554},[484,3249,975],{"class":501},[484,3251,978],{"class":518},[484,3253,975],{"class":501},[484,3255,597],{"class":501},[484,3257,985],{"class":716},[484,3259,813],{"class":501},[484,3261,817],{"class":816},[484,3263,825],{"class":501},[484,3265,828],{"class":716},[484,3267,561],{"class":501},[484,3269,3270,3272,3275,3277,3280,3282,3284,3286,3288,3291,3293,3295,3297],{"class":486,"line":619},[484,3271,1341],{"class":716},[484,3273,3274],{"class":505}," config",[484,3276,1347],{"class":501},[484,3278,3279],{"class":505}," ENDPOINT_LIMITS",[484,3281,1368],{"class":554},[484,3283,817],{"class":505},[484,3285,962],{"class":501},[484,3287,1009],{"class":505},[484,3289,3290],{"class":554},"] ",[484,3292,2557],{"class":501},[484,3294,3279],{"class":505},[484,3296,962],{"class":501},[484,3298,3299],{"class":505},"default\n",[484,3301,3302],{"class":486,"line":634},[484,3303,529],{"emptyLinePlaceholder":528},[484,3305,3306,3308,3310,3312,3314,3316,3318],{"class":486,"line":645},[484,3307,2001],{"class":497},[484,3309,1041],{"class":541},[484,3311,545],{"class":554},[484,3313,817],{"class":505},[484,3315,597],{"class":501},[484,3317,3274],{"class":505},[484,3319,651],{"class":554},[484,3321,3322,3324],{"class":486,"line":654},[484,3323,1141],{"class":501},[484,3325,651],{"class":554},[484,3327,3328,3330],{"class":486,"line":660},[484,3329,669],{"class":501},[484,3331,651],{"class":505},[464,3333,3335],{"id":3334},"monitoring","Monitoring",[919,3337,3339],{"id":3338},"track-rate-limit-hits","Track Rate Limit Hits",[475,3341,3343],{"className":477,"code":3342,"language":479,"meta":480,"style":480},"export default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook('request', async (event) => {\n    try {\n      await checkRateLimit(event)\n    } catch (error) {\n      if (error.statusCode === 429) {\n        // Log rate limit hit\n        console.warn('Rate limit exceeded:', {\n          ip: getClientIP(event),\n          path: event.path,\n          userId: event.context.user?.id\n        })\n\n        // Track in analytics\n        await trackEvent('rate_limit_exceeded', {\n          ip: getClientIP(event),\n          path: event.path\n        })\n      }\n      throw error\n    }\n  })\n})\n",[459,3344,3345,3365,3399,3406,3418,3434,3457,3462,3485,3503,3518,3540,3547,3551,3556,3577,3593,3606,3612,3617,3625,3629,3635],{"__ignoreMap":480},[484,3346,3347,3349,3351,3353,3355,3357,3359,3361,3363],{"class":486,"line":487},[484,3348,535],{"class":497},[484,3350,538],{"class":497},[484,3352,941],{"class":541},[484,3354,545],{"class":505},[484,3356,545],{"class":501},[484,3358,948],{"class":816},[484,3360,825],{"class":501},[484,3362,828],{"class":716},[484,3364,561],{"class":501},[484,3366,3367,3369,3371,3373,3375,3377,3379,3381,3383,3385,3387,3389,3391,3393,3395,3397],{"class":486,"line":494},[484,3368,959],{"class":505},[484,3370,962],{"class":501},[484,3372,965],{"class":505},[484,3374,962],{"class":501},[484,3376,970],{"class":541},[484,3378,545],{"class":554},[484,3380,975],{"class":501},[484,3382,978],{"class":518},[484,3384,975],{"class":501},[484,3386,597],{"class":501},[484,3388,985],{"class":716},[484,3390,813],{"class":501},[484,3392,817],{"class":816},[484,3394,825],{"class":501},[484,3396,828],{"class":716},[484,3398,561],{"class":501},[484,3400,3401,3404],{"class":486,"line":525},[484,3402,3403],{"class":497},"    try",[484,3405,561],{"class":501},[484,3407,3408,3410,3412,3414,3416],{"class":486,"line":532},[484,3409,1038],{"class":497},[484,3411,1125],{"class":541},[484,3413,545],{"class":554},[484,3415,817],{"class":505},[484,3417,651],{"class":554},[484,3419,3420,3422,3425,3427,3430,3432],{"class":486,"line":551},[484,3421,1082],{"class":501},[484,3423,3424],{"class":497}," catch",[484,3426,813],{"class":554},[484,3428,3429],{"class":505},"error",[484,3431,1917],{"class":554},[484,3433,548],{"class":501},[484,3435,3436,3439,3441,3443,3445,3448,3450,3453,3455],{"class":486,"line":564},[484,3437,3438],{"class":497},"      if",[484,3440,813],{"class":554},[484,3442,3429],{"class":505},[484,3444,962],{"class":501},[484,3446,3447],{"class":505},"statusCode",[484,3449,1491],{"class":501},[484,3451,3452],{"class":593}," 429",[484,3454,1917],{"class":554},[484,3456,548],{"class":501},[484,3458,3459],{"class":486,"line":575},[484,3460,3461],{"class":490},"        // Log rate limit hit\n",[484,3463,3464,3467,3469,3472,3474,3476,3479,3481,3483],{"class":486,"line":585},[484,3465,3466],{"class":505},"        console",[484,3468,962],{"class":501},[484,3470,3471],{"class":541},"warn",[484,3473,545],{"class":554},[484,3475,975],{"class":501},[484,3477,3478],{"class":518},"Rate limit exceeded:",[484,3480,975],{"class":501},[484,3482,597],{"class":501},[484,3484,561],{"class":501},[484,3486,3487,3490,3492,3495,3497,3499,3501],{"class":486,"line":603},[484,3488,3489],{"class":554},"          ip",[484,3491,558],{"class":501},[484,3493,3494],{"class":541}," getClientIP",[484,3496,545],{"class":554},[484,3498,817],{"class":505},[484,3500,825],{"class":554},[484,3502,631],{"class":501},[484,3504,3505,3508,3510,3512,3514,3516],{"class":486,"line":619},[484,3506,3507],{"class":554},"          path",[484,3509,558],{"class":501},[484,3511,1350],{"class":505},[484,3513,962],{"class":501},[484,3515,1009],{"class":505},[484,3517,631],{"class":501},[484,3519,3520,3523,3525,3527,3529,3531,3533,3535,3537],{"class":486,"line":634},[484,3521,3522],{"class":554},"          userId",[484,3524,558],{"class":501},[484,3526,1350],{"class":505},[484,3528,962],{"class":501},[484,3530,1477],{"class":505},[484,3532,962],{"class":501},[484,3534,1482],{"class":505},[484,3536,1485],{"class":501},[484,3538,3539],{"class":505},"id\n",[484,3541,3542,3545],{"class":486,"line":645},[484,3543,3544],{"class":501},"        }",[484,3546,651],{"class":554},[484,3548,3549],{"class":486,"line":654},[484,3550,529],{"emptyLinePlaceholder":528},[484,3552,3553],{"class":486,"line":660},[484,3554,3555],{"class":490},"        // Track in analytics\n",[484,3557,3558,3561,3564,3566,3568,3571,3573,3575],{"class":486,"line":666},[484,3559,3560],{"class":497},"        await",[484,3562,3563],{"class":541}," trackEvent",[484,3565,545],{"class":554},[484,3567,975],{"class":501},[484,3569,3570],{"class":518},"rate_limit_exceeded",[484,3572,975],{"class":501},[484,3574,597],{"class":501},[484,3576,561],{"class":501},[484,3578,3579,3581,3583,3585,3587,3589,3591],{"class":486,"line":834},[484,3580,3489],{"class":554},[484,3582,558],{"class":501},[484,3584,3494],{"class":541},[484,3586,545],{"class":554},[484,3588,817],{"class":505},[484,3590,825],{"class":554},[484,3592,631],{"class":501},[484,3594,3595,3597,3599,3601,3603],{"class":486,"line":839},[484,3596,3507],{"class":554},[484,3598,558],{"class":501},[484,3600,1350],{"class":505},[484,3602,962],{"class":501},[484,3604,3605],{"class":505},"path\n",[484,3607,3608,3610],{"class":486,"line":845},[484,3609,3544],{"class":501},[484,3611,651],{"class":554},[484,3613,3614],{"class":486,"line":867},[484,3615,3616],{"class":501},"      }\n",[484,3618,3619,3622],{"class":486,"line":872},[484,3620,3621],{"class":497},"      throw",[484,3623,3624],{"class":505}," error\n",[484,3626,3627],{"class":486,"line":878},[484,3628,1136],{"class":501},[484,3630,3631,3633],{"class":486,"line":888},[484,3632,1141],{"class":501},[484,3634,651],{"class":554},[484,3636,3637,3639],{"class":486,"line":893},[484,3638,669],{"class":501},[484,3640,651],{"class":505},[919,3642,3644],{"id":3643},"prometheus-metrics","Prometheus Metrics",[475,3646,3648],{"className":477,"code":3647,"language":479,"meta":480,"style":480},"import { Counter } from 'prom-client'\n\nconst rateLimitCounter = new Counter({\n  name: 'rate_limit_exceeded_total',\n  help: 'Total number of rate limit exceeded errors',\n  labelNames: ['path', 'user_type']\n})\n\n// In your rate limit check\nif (error.statusCode === 429) {\n  rateLimitCounter.inc({\n    path: event.path,\n    user_type: event.context.user ? 'authenticated' : 'anonymous'\n  })\n}\n",[459,3649,3650,3670,3674,3691,3707,3723,3750,3756,3760,3765,3787,3801,3816,3851,3857],{"__ignoreMap":480},[484,3651,3652,3654,3656,3659,3661,3663,3665,3668],{"class":486,"line":487},[484,3653,498],{"class":497},[484,3655,502],{"class":501},[484,3657,3658],{"class":505}," Counter",[484,3660,509],{"class":501},[484,3662,512],{"class":497},[484,3664,515],{"class":501},[484,3666,3667],{"class":518},"prom-client",[484,3669,522],{"class":501},[484,3671,3672],{"class":486,"line":494},[484,3673,529],{"emptyLinePlaceholder":528},[484,3675,3676,3678,3681,3683,3685,3687,3689],{"class":486,"line":525},[484,3677,1790],{"class":716},[484,3679,3680],{"class":505}," rateLimitCounter ",[484,3682,1796],{"class":501},[484,3684,1799],{"class":501},[484,3686,3658],{"class":541},[484,3688,545],{"class":505},[484,3690,548],{"class":501},[484,3692,3693,3696,3698,3700,3703,3705],{"class":486,"line":532},[484,3694,3695],{"class":554},"  name",[484,3697,558],{"class":501},[484,3699,515],{"class":501},[484,3701,3702],{"class":518},"rate_limit_exceeded_total",[484,3704,975],{"class":501},[484,3706,631],{"class":501},[484,3708,3709,3712,3714,3716,3719,3721],{"class":486,"line":551},[484,3710,3711],{"class":554},"  help",[484,3713,558],{"class":501},[484,3715,515],{"class":501},[484,3717,3718],{"class":518},"Total number of rate limit exceeded errors",[484,3720,975],{"class":501},[484,3722,631],{"class":501},[484,3724,3725,3728,3730,3733,3735,3737,3739,3741,3743,3746,3748],{"class":486,"line":564},[484,3726,3727],{"class":554},"  labelNames",[484,3729,558],{"class":501},[484,3731,3732],{"class":505}," [",[484,3734,975],{"class":501},[484,3736,1009],{"class":518},[484,3738,975],{"class":501},[484,3740,597],{"class":501},[484,3742,515],{"class":501},[484,3744,3745],{"class":518},"user_type",[484,3747,975],{"class":501},[484,3749,1378],{"class":505},[484,3751,3752,3754],{"class":486,"line":575},[484,3753,669],{"class":501},[484,3755,651],{"class":505},[484,3757,3758],{"class":486,"line":585},[484,3759,529],{"emptyLinePlaceholder":528},[484,3761,3762],{"class":486,"line":603},[484,3763,3764],{"class":490},"// In your rate limit check\n",[484,3766,3767,3770,3773,3775,3778,3781,3783,3785],{"class":486,"line":619},[484,3768,3769],{"class":497},"if",[484,3771,3772],{"class":505}," (error",[484,3774,962],{"class":501},[484,3776,3777],{"class":505},"statusCode ",[484,3779,3780],{"class":501},"===",[484,3782,3452],{"class":593},[484,3784,1917],{"class":505},[484,3786,548],{"class":501},[484,3788,3789,3792,3794,3797,3799],{"class":486,"line":634},[484,3790,3791],{"class":505},"  rateLimitCounter",[484,3793,962],{"class":501},[484,3795,3796],{"class":541},"inc",[484,3798,545],{"class":554},[484,3800,548],{"class":501},[484,3802,3803,3806,3808,3810,3812,3814],{"class":486,"line":645},[484,3804,3805],{"class":554},"    path",[484,3807,558],{"class":501},[484,3809,1350],{"class":505},[484,3811,962],{"class":501},[484,3813,1009],{"class":505},[484,3815,631],{"class":501},[484,3817,3818,3821,3823,3825,3827,3829,3831,3833,3835,3837,3840,3842,3845,3847,3849],{"class":486,"line":654},[484,3819,3820],{"class":554},"    user_type",[484,3822,558],{"class":501},[484,3824,1350],{"class":505},[484,3826,962],{"class":501},[484,3828,1477],{"class":505},[484,3830,962],{"class":501},[484,3832,1482],{"class":505},[484,3834,2081],{"class":501},[484,3836,515],{"class":501},[484,3838,3839],{"class":518},"authenticated",[484,3841,975],{"class":501},[484,3843,3844],{"class":501}," :",[484,3846,515],{"class":501},[484,3848,2562],{"class":518},[484,3850,522],{"class":501},[484,3852,3853,3855],{"class":486,"line":660},[484,3854,1141],{"class":501},[484,3856,651],{"class":554},[484,3858,3859],{"class":486,"line":666},[484,3860,913],{"class":501},[464,3862,271],{"id":3863},"testing",[919,3865,3867],{"id":3866},"disable-in-development","Disable in Development",[475,3869,3871],{"className":477,"code":3870,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  skip: (event) => {\n    return process.env.NODE_ENV === 'development'\n  }\n})\n",[459,3872,3873,3881,3897,3922,3926],{"__ignoreMap":480},[484,3874,3875,3877,3879],{"class":486,"line":487},[484,3876,690],{"class":541},[484,3878,545],{"class":505},[484,3880,548],{"class":501},[484,3882,3883,3885,3887,3889,3891,3893,3895],{"class":486,"line":494},[484,3884,848],{"class":541},[484,3886,558],{"class":501},[484,3888,813],{"class":501},[484,3890,817],{"class":816},[484,3892,825],{"class":501},[484,3894,828],{"class":716},[484,3896,561],{"class":501},[484,3898,3899,3901,3904,3906,3908,3910,3913,3915,3917,3920],{"class":486,"line":525},[484,3900,1383],{"class":497},[484,3902,3903],{"class":505}," process",[484,3905,962],{"class":501},[484,3907,1809],{"class":505},[484,3909,962],{"class":501},[484,3911,3912],{"class":505},"NODE_ENV",[484,3914,1491],{"class":501},[484,3916,515],{"class":501},[484,3918,3919],{"class":518},"development",[484,3921,522],{"class":501},[484,3923,3924],{"class":486,"line":532},[484,3925,663],{"class":501},[484,3927,3928,3930],{"class":486,"line":551},[484,3929,669],{"class":501},[484,3931,651],{"class":505},[919,3933,3935],{"id":3934},"test-rate-limiting","Test Rate Limiting",[475,3937,3939],{"className":477,"code":3938,"language":479,"meta":480,"style":480},"// test/rate-limiting.test.ts\nimport { describe, it, expect } from 'vitest'\n\ndescribe('Rate Limiting', () => {\n  it('should block requests after limit', async () => {\n    const requests = []\n\n    // Make 101 requests (limit is 100)\n    for (let i = 0; i \u003C 101; i++) {\n      requests.push(\n        $fetch('/api/users').catch(e => e)\n      )\n    }\n\n    const results = await Promise.all(requests)\n\n    // Last request should be rate limited\n    expect(results[100].statusCode).toBe(429)\n  })\n})\n",[459,3940,3941,3946,3976,3980,4001,4025,4037,4041,4046,4086,4099,4131,4136,4140,4144,4169,4173,4178,4214,4220],{"__ignoreMap":480},[484,3942,3943],{"class":486,"line":487},[484,3944,3945],{"class":490},"// test/rate-limiting.test.ts\n",[484,3947,3948,3950,3952,3955,3957,3960,3962,3965,3967,3969,3971,3974],{"class":486,"line":494},[484,3949,498],{"class":497},[484,3951,502],{"class":501},[484,3953,3954],{"class":505}," describe",[484,3956,597],{"class":501},[484,3958,3959],{"class":505}," it",[484,3961,597],{"class":501},[484,3963,3964],{"class":505}," expect",[484,3966,509],{"class":501},[484,3968,512],{"class":497},[484,3970,515],{"class":501},[484,3972,3973],{"class":518},"vitest",[484,3975,522],{"class":501},[484,3977,3978],{"class":486,"line":525},[484,3979,529],{"emptyLinePlaceholder":528},[484,3981,3982,3985,3987,3989,3991,3993,3995,3997,3999],{"class":486,"line":532},[484,3983,3984],{"class":541},"describe",[484,3986,545],{"class":505},[484,3988,975],{"class":501},[484,3990,243],{"class":518},[484,3992,975],{"class":501},[484,3994,597],{"class":501},[484,3996,2538],{"class":501},[484,3998,828],{"class":716},[484,4000,561],{"class":501},[484,4002,4003,4006,4008,4010,4013,4015,4017,4019,4021,4023],{"class":486,"line":551},[484,4004,4005],{"class":541},"  it",[484,4007,545],{"class":554},[484,4009,975],{"class":501},[484,4011,4012],{"class":518},"should block requests after limit",[484,4014,975],{"class":501},[484,4016,597],{"class":501},[484,4018,985],{"class":716},[484,4020,2538],{"class":501},[484,4022,828],{"class":716},[484,4024,561],{"class":501},[484,4026,4027,4029,4032,4034],{"class":486,"line":564},[484,4028,1341],{"class":716},[484,4030,4031],{"class":505}," requests",[484,4033,1347],{"class":501},[484,4035,4036],{"class":554}," []\n",[484,4038,4039],{"class":486,"line":575},[484,4040,529],{"emptyLinePlaceholder":528},[484,4042,4043],{"class":486,"line":585},[484,4044,4045],{"class":490},"    // Make 101 requests (limit is 100)\n",[484,4047,4048,4051,4053,4056,4059,4061,4064,4067,4069,4072,4075,4077,4079,4082,4084],{"class":486,"line":603},[484,4049,4050],{"class":497},"    for",[484,4052,813],{"class":554},[484,4054,4055],{"class":716},"let",[484,4057,4058],{"class":505}," i",[484,4060,1347],{"class":501},[484,4062,4063],{"class":593}," 0",[484,4065,4066],{"class":501},";",[484,4068,4058],{"class":505},[484,4070,4071],{"class":501}," \u003C",[484,4073,4074],{"class":593}," 101",[484,4076,4066],{"class":501},[484,4078,4058],{"class":505},[484,4080,4081],{"class":501},"++",[484,4083,1917],{"class":554},[484,4085,548],{"class":501},[484,4087,4088,4091,4093,4096],{"class":486,"line":619},[484,4089,4090],{"class":505},"      requests",[484,4092,962],{"class":501},[484,4094,4095],{"class":541},"push",[484,4097,4098],{"class":554},"(\n",[484,4100,4101,4104,4106,4108,4111,4113,4115,4117,4120,4122,4124,4126,4129],{"class":486,"line":634},[484,4102,4103],{"class":541},"        $fetch",[484,4105,545],{"class":554},[484,4107,975],{"class":501},[484,4109,4110],{"class":518},"/api/users",[484,4112,975],{"class":501},[484,4114,825],{"class":554},[484,4116,962],{"class":501},[484,4118,4119],{"class":541},"catch",[484,4121,545],{"class":554},[484,4123,2737],{"class":816},[484,4125,828],{"class":716},[484,4127,4128],{"class":505}," e",[484,4130,651],{"class":554},[484,4132,4133],{"class":486,"line":645},[484,4134,4135],{"class":554},"      )\n",[484,4137,4138],{"class":486,"line":654},[484,4139,1136],{"class":501},[484,4141,4142],{"class":486,"line":660},[484,4143,529],{"emptyLinePlaceholder":528},[484,4145,4146,4148,4151,4153,4155,4157,4159,4162,4164,4167],{"class":486,"line":666},[484,4147,1341],{"class":716},[484,4149,4150],{"class":505}," results",[484,4152,1347],{"class":501},[484,4154,1877],{"class":497},[484,4156,1854],{"class":720},[484,4158,962],{"class":501},[484,4160,4161],{"class":541},"all",[484,4163,545],{"class":554},[484,4165,4166],{"class":505},"requests",[484,4168,651],{"class":554},[484,4170,4171],{"class":486,"line":834},[484,4172,529],{"emptyLinePlaceholder":528},[484,4174,4175],{"class":486,"line":839},[484,4176,4177],{"class":490},"    // Last request should be rate limited\n",[484,4179,4180,4183,4185,4188,4190,4193,4196,4198,4200,4202,4204,4207,4209,4212],{"class":486,"line":845},[484,4181,4182],{"class":541},"    expect",[484,4184,545],{"class":554},[484,4186,4187],{"class":505},"results",[484,4189,1368],{"class":554},[484,4191,4192],{"class":593},"100",[484,4194,4195],{"class":554},"]",[484,4197,962],{"class":501},[484,4199,3447],{"class":505},[484,4201,825],{"class":554},[484,4203,962],{"class":501},[484,4205,4206],{"class":541},"toBe",[484,4208,545],{"class":554},[484,4210,4211],{"class":593},"429",[484,4213,651],{"class":554},[484,4215,4216,4218],{"class":486,"line":867},[484,4217,1141],{"class":501},[484,4219,651],{"class":554},[484,4221,4222,4224],{"class":486,"line":872},[484,4223,669],{"class":501},[484,4225,651],{"class":505},[464,4227,4229],{"id":4228},"client-side-handling","Client-Side Handling",[919,4231,4233],{"id":4232},"respect-rate-limits","Respect Rate Limits",[475,4235,4237],{"className":477,"code":4236,"language":479,"meta":480,"style":480},"// composables/useAutoApiWithRateLimit.ts\nexport const useAutoApiWithRateLimit = (resource: string) => {\n  const { fetch, ...rest } = useAutoApiFetch(resource)\n\n  const fetchWithRetry = async (options: any) => {\n    try {\n      return await fetch(options)\n    } catch (error) {\n      if (error.statusCode === 429) {\n        const retryAfter = error.data?.retryAfter || 60\n\n        // Show user-friendly message\n        console.warn(`Rate limited. Retrying in ${retryAfter} seconds...`)\n\n        // Wait and retry\n        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))\n        return await fetch(options)\n      }\n      throw error\n    }\n  }\n\n  return { ...rest, fetch: fetchWithRetry }\n}\n",[459,4238,4239,4244,4271,4301,4305,4331,4337,4352,4366,4386,4415,4419,4424,4453,4457,4462,4496,4511,4515,4521,4525,4529,4533,4553],{"__ignoreMap":480},[484,4240,4241],{"class":486,"line":487},[484,4242,4243],{"class":490},"// composables/useAutoApiWithRateLimit.ts\n",[484,4245,4246,4248,4251,4254,4256,4258,4261,4263,4265,4267,4269],{"class":486,"line":494},[484,4247,535],{"class":497},[484,4249,4250],{"class":716}," const",[484,4252,4253],{"class":505}," useAutoApiWithRateLimit ",[484,4255,1796],{"class":501},[484,4257,813],{"class":501},[484,4259,4260],{"class":816},"resource",[484,4262,558],{"class":501},[484,4264,1848],{"class":720},[484,4266,825],{"class":501},[484,4268,828],{"class":716},[484,4270,561],{"class":501},[484,4272,4273,4275,4277,4280,4282,4285,4288,4290,4292,4295,4297,4299],{"class":486,"line":525},[484,4274,2895],{"class":716},[484,4276,502],{"class":501},[484,4278,4279],{"class":505}," fetch",[484,4281,597],{"class":501},[484,4283,4284],{"class":501}," ...",[484,4286,4287],{"class":505},"rest",[484,4289,509],{"class":501},[484,4291,1347],{"class":501},[484,4293,4294],{"class":541}," useAutoApiFetch",[484,4296,545],{"class":554},[484,4298,4260],{"class":505},[484,4300,651],{"class":554},[484,4302,4303],{"class":486,"line":532},[484,4304,529],{"emptyLinePlaceholder":528},[484,4306,4307,4309,4312,4314,4316,4318,4321,4323,4325,4327,4329],{"class":486,"line":551},[484,4308,2895],{"class":716},[484,4310,4311],{"class":505}," fetchWithRetry",[484,4313,1347],{"class":501},[484,4315,985],{"class":716},[484,4317,813],{"class":501},[484,4319,4320],{"class":816},"options",[484,4322,558],{"class":501},[484,4324,822],{"class":720},[484,4326,825],{"class":501},[484,4328,828],{"class":716},[484,4330,561],{"class":501},[484,4332,4333,4335],{"class":486,"line":564},[484,4334,3403],{"class":497},[484,4336,561],{"class":501},[484,4338,4339,4342,4344,4346,4348,4350],{"class":486,"line":575},[484,4340,4341],{"class":497},"      return",[484,4343,1877],{"class":497},[484,4345,4279],{"class":541},[484,4347,545],{"class":554},[484,4349,4320],{"class":505},[484,4351,651],{"class":554},[484,4353,4354,4356,4358,4360,4362,4364],{"class":486,"line":585},[484,4355,1082],{"class":501},[484,4357,3424],{"class":497},[484,4359,813],{"class":554},[484,4361,3429],{"class":505},[484,4363,1917],{"class":554},[484,4365,548],{"class":501},[484,4367,4368,4370,4372,4374,4376,4378,4380,4382,4384],{"class":486,"line":603},[484,4369,3438],{"class":497},[484,4371,813],{"class":554},[484,4373,3429],{"class":505},[484,4375,962],{"class":501},[484,4377,3447],{"class":505},[484,4379,1491],{"class":501},[484,4381,3452],{"class":593},[484,4383,1917],{"class":554},[484,4385,548],{"class":501},[484,4387,4388,4391,4394,4396,4399,4401,4404,4406,4409,4412],{"class":486,"line":619},[484,4389,4390],{"class":716},"        const",[484,4392,4393],{"class":505}," retryAfter",[484,4395,1347],{"class":501},[484,4397,4398],{"class":505}," error",[484,4400,962],{"class":501},[484,4402,4403],{"class":505},"data",[484,4405,1485],{"class":501},[484,4407,4408],{"class":505},"retryAfter",[484,4410,4411],{"class":501}," ||",[484,4413,4414],{"class":593}," 60\n",[484,4416,4417],{"class":486,"line":634},[484,4418,529],{"emptyLinePlaceholder":528},[484,4420,4421],{"class":486,"line":645},[484,4422,4423],{"class":490},"        // Show user-friendly message\n",[484,4425,4426,4428,4430,4432,4434,4437,4440,4442,4444,4446,4449,4451],{"class":486,"line":654},[484,4427,3466],{"class":505},[484,4429,962],{"class":501},[484,4431,3471],{"class":541},[484,4433,545],{"class":554},[484,4435,4436],{"class":501},"`",[484,4438,4439],{"class":518},"Rate limited. Retrying in ",[484,4441,1392],{"class":501},[484,4443,4408],{"class":505},[484,4445,669],{"class":501},[484,4447,4448],{"class":518}," seconds...",[484,4450,4436],{"class":501},[484,4452,651],{"class":554},[484,4454,4455],{"class":486,"line":660},[484,4456,529],{"emptyLinePlaceholder":528},[484,4458,4459],{"class":486,"line":666},[484,4460,4461],{"class":490},"        // Wait and retry\n",[484,4463,4464,4466,4468,4470,4472,4475,4477,4480,4482,4484,4486,4488,4491,4493],{"class":486,"line":834},[484,4465,3560],{"class":497},[484,4467,1799],{"class":501},[484,4469,1854],{"class":720},[484,4471,545],{"class":554},[484,4473,4474],{"class":816},"resolve",[484,4476,828],{"class":716},[484,4478,4479],{"class":541}," setTimeout",[484,4481,545],{"class":554},[484,4483,4474],{"class":505},[484,4485,597],{"class":501},[484,4487,4393],{"class":505},[484,4489,4490],{"class":501}," *",[484,4492,3037],{"class":593},[484,4494,4495],{"class":554},"))\n",[484,4497,4498,4501,4503,4505,4507,4509],{"class":486,"line":839},[484,4499,4500],{"class":497},"        return",[484,4502,1877],{"class":497},[484,4504,4279],{"class":541},[484,4506,545],{"class":554},[484,4508,4320],{"class":505},[484,4510,651],{"class":554},[484,4512,4513],{"class":486,"line":845},[484,4514,3616],{"class":501},[484,4516,4517,4519],{"class":486,"line":867},[484,4518,3621],{"class":497},[484,4520,3624],{"class":505},[484,4522,4523],{"class":486,"line":872},[484,4524,1136],{"class":501},[484,4526,4527],{"class":486,"line":878},[484,4528,663],{"class":501},[484,4530,4531],{"class":486,"line":888},[484,4532,529],{"emptyLinePlaceholder":528},[484,4534,4535,4537,4539,4541,4543,4545,4547,4549,4551],{"class":486,"line":893},[484,4536,3061],{"class":497},[484,4538,502],{"class":501},[484,4540,4284],{"class":501},[484,4542,4287],{"class":505},[484,4544,597],{"class":501},[484,4546,4279],{"class":554},[484,4548,558],{"class":501},[484,4550,4311],{"class":505},[484,4552,3205],{"class":501},[484,4554,4555],{"class":486,"line":899},[484,4556,913],{"class":501},[919,4558,4560],{"id":4559},"display-rate-limit-info","Display Rate Limit Info",[475,4562,4566],{"className":4563,"code":4564,"language":4565,"meta":480,"style":480},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup>\nconst rateLimitInfo = ref(null)\n\nonMounted(() => {\n  // Check headers after request\n  const { data, response } = await useAutoApiFetch('users')\n\n  if (response.headers) {\n    rateLimitInfo.value = {\n      limit: response.headers.get('X-RateLimit-Limit'),\n      remaining: response.headers.get('X-RateLimit-Remaining'),\n      reset: new Date(parseInt(response.headers.get('X-RateLimit-Reset')))\n    }\n  }\n})\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"rateLimitInfo\">\n    {{ rateLimitInfo.remaining }} / {{ rateLimitInfo.limit }} requests remaining\n    (resets at {{ rateLimitInfo.reset.toLocaleTimeString() }})\n  \u003C/div>\n\u003C/template>\n","vue",[459,4567,4568,4581,4600,4604,4618,4623,4656,4660,4677,4691,4720,4750,4791,4795,4799,4805,4814,4818,4827,4850,4855,4860,4869],{"__ignoreMap":480},[484,4569,4570,4572,4575,4578],{"class":486,"line":487},[484,4571,1857],{"class":501},[484,4573,4574],{"class":554},"script",[484,4576,4577],{"class":716}," setup",[484,4579,4580],{"class":501},">\n",[484,4582,4583,4585,4588,4590,4593,4595,4598],{"class":486,"line":494},[484,4584,1790],{"class":716},[484,4586,4587],{"class":505}," rateLimitInfo ",[484,4589,1796],{"class":501},[484,4591,4592],{"class":541}," ref",[484,4594,545],{"class":505},[484,4596,4597],{"class":501},"null",[484,4599,651],{"class":505},[484,4601,4602],{"class":486,"line":525},[484,4603,529],{"emptyLinePlaceholder":528},[484,4605,4606,4609,4611,4614,4616],{"class":486,"line":532},[484,4607,4608],{"class":541},"onMounted",[484,4610,545],{"class":505},[484,4612,4613],{"class":501},"()",[484,4615,828],{"class":716},[484,4617,561],{"class":501},[484,4619,4620],{"class":486,"line":551},[484,4621,4622],{"class":490},"  // Check headers after request\n",[484,4624,4625,4627,4629,4632,4634,4637,4639,4641,4643,4645,4647,4649,4652,4654],{"class":486,"line":564},[484,4626,2895],{"class":716},[484,4628,502],{"class":501},[484,4630,4631],{"class":505}," data",[484,4633,597],{"class":501},[484,4635,4636],{"class":505}," response",[484,4638,509],{"class":501},[484,4640,1347],{"class":501},[484,4642,1877],{"class":497},[484,4644,4294],{"class":541},[484,4646,545],{"class":554},[484,4648,975],{"class":501},[484,4650,4651],{"class":518},"users",[484,4653,975],{"class":501},[484,4655,651],{"class":554},[484,4657,4658],{"class":486,"line":575},[484,4659,529],{"emptyLinePlaceholder":528},[484,4661,4662,4664,4666,4669,4671,4673,4675],{"class":486,"line":585},[484,4663,2964],{"class":497},[484,4665,813],{"class":554},[484,4667,4668],{"class":505},"response",[484,4670,962],{"class":501},[484,4672,1365],{"class":505},[484,4674,1917],{"class":554},[484,4676,548],{"class":501},[484,4678,4679,4682,4684,4687,4689],{"class":486,"line":603},[484,4680,4681],{"class":505},"    rateLimitInfo",[484,4683,962],{"class":501},[484,4685,4686],{"class":505},"value",[484,4688,1347],{"class":501},[484,4690,561],{"class":501},[484,4692,4693,4695,4697,4699,4701,4703,4705,4707,4709,4711,4714,4716,4718],{"class":486,"line":619},[484,4694,2441],{"class":554},[484,4696,558],{"class":501},[484,4698,4636],{"class":505},[484,4700,962],{"class":501},[484,4702,1365],{"class":505},[484,4704,962],{"class":501},[484,4706,2066],{"class":541},[484,4708,545],{"class":554},[484,4710,975],{"class":501},[484,4712,4713],{"class":518},"X-RateLimit-Limit",[484,4715,975],{"class":501},[484,4717,825],{"class":554},[484,4719,631],{"class":501},[484,4721,4722,4725,4727,4729,4731,4733,4735,4737,4739,4741,4744,4746,4748],{"class":486,"line":634},[484,4723,4724],{"class":554},"      remaining",[484,4726,558],{"class":501},[484,4728,4636],{"class":505},[484,4730,962],{"class":501},[484,4732,1365],{"class":505},[484,4734,962],{"class":501},[484,4736,2066],{"class":541},[484,4738,545],{"class":554},[484,4740,975],{"class":501},[484,4742,4743],{"class":518},"X-RateLimit-Remaining",[484,4745,975],{"class":501},[484,4747,825],{"class":554},[484,4749,631],{"class":501},[484,4751,4752,4755,4757,4759,4762,4764,4767,4769,4771,4773,4775,4777,4779,4781,4783,4786,4788],{"class":486,"line":645},[484,4753,4754],{"class":554},"      reset",[484,4756,558],{"class":501},[484,4758,1799],{"class":501},[484,4760,4761],{"class":541}," Date",[484,4763,545],{"class":554},[484,4765,4766],{"class":541},"parseInt",[484,4768,545],{"class":554},[484,4770,4668],{"class":505},[484,4772,962],{"class":501},[484,4774,1365],{"class":505},[484,4776,962],{"class":501},[484,4778,2066],{"class":541},[484,4780,545],{"class":554},[484,4782,975],{"class":501},[484,4784,4785],{"class":518},"X-RateLimit-Reset",[484,4787,975],{"class":501},[484,4789,4790],{"class":554},")))\n",[484,4792,4793],{"class":486,"line":654},[484,4794,1136],{"class":501},[484,4796,4797],{"class":486,"line":660},[484,4798,663],{"class":501},[484,4800,4801,4803],{"class":486,"line":666},[484,4802,669],{"class":501},[484,4804,651],{"class":505},[484,4806,4807,4810,4812],{"class":486,"line":834},[484,4808,4809],{"class":501},"\u003C/",[484,4811,4574],{"class":554},[484,4813,4580],{"class":501},[484,4815,4816],{"class":486,"line":839},[484,4817,529],{"emptyLinePlaceholder":528},[484,4819,4820,4822,4825],{"class":486,"line":845},[484,4821,1857],{"class":501},[484,4823,4824],{"class":554},"template",[484,4826,4580],{"class":501},[484,4828,4829,4832,4835,4838,4840,4843,4846,4848],{"class":486,"line":867},[484,4830,4831],{"class":501},"  \u003C",[484,4833,4834],{"class":554},"div",[484,4836,4837],{"class":716}," v-if",[484,4839,1796],{"class":501},[484,4841,4842],{"class":501},"\"",[484,4844,4845],{"class":518},"rateLimitInfo",[484,4847,4842],{"class":501},[484,4849,4580],{"class":501},[484,4851,4852],{"class":486,"line":872},[484,4853,4854],{"class":505},"    {{ rateLimitInfo.remaining }} / {{ rateLimitInfo.limit }} requests remaining\n",[484,4856,4857],{"class":486,"line":878},[484,4858,4859],{"class":505},"    (resets at {{ rateLimitInfo.reset.toLocaleTimeString() }})\n",[484,4861,4862,4865,4867],{"class":486,"line":888},[484,4863,4864],{"class":501},"  \u003C/",[484,4866,4834],{"class":554},[484,4868,4580],{"class":501},[484,4870,4871,4873,4875],{"class":486,"line":893},[484,4872,4809],{"class":501},[484,4874,4824],{"class":554},[484,4876,4580],{"class":501},[464,4878,4880],{"id":4879},"best-practices","Best Practices",[4882,4883,4884,4890,4896,4902,4908,4914,4920,4926,4932,4938],"ol",{},[1720,4885,4886,4889],{},[684,4887,4888],{},"Start conservative",": Begin with lower limits and increase based on usage",[1720,4891,4892,4895],{},[684,4893,4894],{},"Monitor closely",": Track 429 errors to find the right limits",[1720,4897,4898,4901],{},[684,4899,4900],{},"Use Redis in production",": In-memory store doesn't scale",[1720,4903,4904,4907],{},[684,4905,4906],{},"Differentiate users",": Premium users get higher limits",[1720,4909,4910,4913],{},[684,4911,4912],{},"Protect expensive endpoints",": Lower limits for resource-intensive operations",[1720,4915,4916,4919],{},[684,4917,4918],{},"Clear error messages",": Help users understand what happened",[1720,4921,4922,4925],{},[684,4923,4924],{},"Provide headers",": Include X-RateLimit-* headers",[1720,4927,4928,4931],{},[684,4929,4930],{},"Test thoroughly",": Ensure rate limiting doesn't break normal usage",[1720,4933,4934,4937],{},[684,4935,4936],{},"Document limits",": Tell users what the limits are",[1720,4939,4940,4943],{},[684,4941,4942],{},"Consider quotas",": Combine with daily/monthly quotas for paid tiers",[464,4945,4947],{"id":4946},"troubleshooting","Troubleshooting",[919,4949,4951],{"id":4950},"rate-limit-not-working","Rate Limit Not Working",[455,4953,4954],{},"Check that:",[4882,4956,4957,4960,4963,4966],{},[1720,4958,4959],{},"Extension is registered in nuxt.config.ts",[1720,4961,4962],{},"Nitro plugin is applying checkRateLimit",[1720,4964,4965],{},"Redis is connected (if using Redis store)",[1720,4967,4968],{},"Skip function isn't excluding all requests",[919,4970,4972],{"id":4971},"too-many-false-positives","Too Many False Positives",[455,4974,4975],{},"Adjust limits:",[475,4977,4979],{"className":477,"code":4978,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  windowMs: 60000,\n  max: 200  // Increase limit\n})\n",[459,4980,4981,4989,4999,5011],{"__ignoreMap":480},[484,4982,4983,4985,4987],{"class":486,"line":487},[484,4984,690],{"class":541},[484,4986,545],{"class":505},[484,4988,548],{"class":501},[484,4990,4991,4993,4995,4997],{"class":486,"line":494},[484,4992,733],{"class":554},[484,4994,558],{"class":501},[484,4996,594],{"class":593},[484,4998,631],{"class":501},[484,5000,5001,5003,5005,5008],{"class":486,"line":525},[484,5002,753],{"class":554},[484,5004,558],{"class":501},[484,5006,5007],{"class":593}," 200",[484,5009,5010],{"class":490},"  // Increase limit\n",[484,5012,5013,5015],{"class":486,"line":532},[484,5014,669],{"class":501},[484,5016,651],{"class":505},[455,5018,5019],{},"Or use per-user limits instead of per-IP:",[475,5021,5023],{"className":477,"code":5022,"language":479,"meta":480,"style":480},"createRateLimitExtension({\n  byIp: false,\n  byUser: true\n})\n",[459,5024,5025,5033,5043,5052],{"__ignoreMap":480},[484,5026,5027,5029,5031],{"class":486,"line":487},[484,5028,690],{"class":541},[484,5030,545],{"class":505},[484,5032,548],{"class":501},[484,5034,5035,5037,5039,5041],{"class":486,"line":494},[484,5036,771],{"class":554},[484,5038,558],{"class":501},[484,5040,1195],{"class":627},[484,5042,631],{"class":501},[484,5044,5045,5047,5049],{"class":486,"line":525},[484,5046,790],{"class":554},[484,5048,558],{"class":501},[484,5050,5051],{"class":627}," true\n",[484,5053,5054,5056],{"class":486,"line":532},[484,5055,669],{"class":501},[484,5057,651],{"class":505},[919,5059,5061],{"id":5060},"memory-issues","Memory Issues",[455,5063,5064],{},"Switch to Redis:",[1717,5066,5067,5070],{},[1720,5068,5069],{},"In-memory store grows with traffic",[1720,5071,5072],{},"Redis provides bounded memory usage",[464,5074,5076],{"id":5075},"alternatives","Alternatives",[1717,5078,5079,5085,5091,5097],{},[1720,5080,5081,5084],{},[684,5082,5083],{},"Cloudflare",": Rate limiting at the edge",[1720,5086,5087,5090],{},[684,5088,5089],{},"nginx",": Rate limiting at reverse proxy",[1720,5092,5093,5096],{},[684,5094,5095],{},"API Gateway",": AWS API Gateway, Kong, etc.",[1720,5098,5099,5102],{},[684,5100,5101],{},"Custom middleware",": Full control but more work",[464,5104,5106],{"id":5105},"migration-path","Migration Path",[455,5108,5109],{},"From custom rate limiting:",[475,5111,5113],{"className":477,"code":5112,"language":479,"meta":480,"style":480},"// Before: Custom implementation\nconst requests = new Map()\n\nexport default defineEventHandler(async (event) => {\n  const ip = getClientIP(event)\n  const count = requests.get(ip) || 0\n\n  if (count > 100) {\n    throw createError({ statusCode: 429, message: 'Too many requests' })\n  }\n\n  requests.set(ip, count + 1)\n  // ... rest of handler\n})\n\n// After: Use extension\ncreateRateLimitExtension({\n  max: 100,\n  byIp: true\n})\n",[459,5114,5115,5120,5136,5140,5164,5181,5206,5210,5226,5264,5268,5272,5297,5302,5308,5312,5317,5325,5335,5343],{"__ignoreMap":480},[484,5116,5117],{"class":486,"line":487},[484,5118,5119],{"class":490},"// Before: Custom implementation\n",[484,5121,5122,5124,5127,5129,5131,5134],{"class":486,"line":494},[484,5123,1790],{"class":716},[484,5125,5126],{"class":505}," requests ",[484,5128,1796],{"class":501},[484,5130,1799],{"class":501},[484,5132,5133],{"class":541}," Map",[484,5135,2213],{"class":505},[484,5137,5138],{"class":486,"line":525},[484,5139,529],{"emptyLinePlaceholder":528},[484,5141,5142,5144,5146,5149,5151,5154,5156,5158,5160,5162],{"class":486,"line":532},[484,5143,535],{"class":497},[484,5145,538],{"class":497},[484,5147,5148],{"class":541}," defineEventHandler",[484,5150,545],{"class":505},[484,5152,5153],{"class":716},"async",[484,5155,813],{"class":501},[484,5157,817],{"class":816},[484,5159,825],{"class":501},[484,5161,828],{"class":716},[484,5163,561],{"class":501},[484,5165,5166,5168,5171,5173,5175,5177,5179],{"class":486,"line":551},[484,5167,2895],{"class":716},[484,5169,5170],{"class":505}," ip",[484,5172,1347],{"class":501},[484,5174,3494],{"class":541},[484,5176,545],{"class":554},[484,5178,817],{"class":505},[484,5180,651],{"class":554},[484,5182,5183,5185,5187,5189,5191,5193,5195,5197,5200,5202,5204],{"class":486,"line":564},[484,5184,2895],{"class":716},[484,5186,1872],{"class":505},[484,5188,1347],{"class":501},[484,5190,4031],{"class":505},[484,5192,962],{"class":501},[484,5194,2066],{"class":541},[484,5196,545],{"class":554},[484,5198,5199],{"class":505},"ip",[484,5201,1917],{"class":554},[484,5203,2557],{"class":501},[484,5205,2100],{"class":593},[484,5207,5208],{"class":486,"line":575},[484,5209,529],{"emptyLinePlaceholder":528},[484,5211,5212,5214,5216,5218,5220,5222,5224],{"class":486,"line":585},[484,5213,2964],{"class":497},[484,5215,813],{"class":554},[484,5217,1909],{"class":505},[484,5219,3003],{"class":501},[484,5221,611],{"class":593},[484,5223,1917],{"class":554},[484,5225,548],{"class":501},[484,5227,5228,5231,5234,5236,5239,5242,5244,5246,5248,5251,5253,5255,5258,5260,5262],{"class":486,"line":603},[484,5229,5230],{"class":497},"    throw",[484,5232,5233],{"class":541}," createError",[484,5235,545],{"class":554},[484,5237,5238],{"class":501},"{",[484,5240,5241],{"class":554}," statusCode",[484,5243,558],{"class":501},[484,5245,3452],{"class":593},[484,5247,597],{"class":501},[484,5249,5250],{"class":554}," message",[484,5252,558],{"class":501},[484,5254,515],{"class":501},[484,5256,5257],{"class":518},"Too many requests",[484,5259,975],{"class":501},[484,5261,509],{"class":501},[484,5263,651],{"class":554},[484,5265,5266],{"class":486,"line":619},[484,5267,663],{"class":501},[484,5269,5270],{"class":486,"line":634},[484,5271,529],{"emptyLinePlaceholder":528},[484,5273,5274,5277,5279,5282,5284,5286,5288,5290,5293,5295],{"class":486,"line":645},[484,5275,5276],{"class":505},"  requests",[484,5278,962],{"class":501},[484,5280,5281],{"class":541},"set",[484,5283,545],{"class":554},[484,5285,5199],{"class":505},[484,5287,597],{"class":501},[484,5289,1872],{"class":505},[484,5291,5292],{"class":501}," +",[484,5294,1914],{"class":593},[484,5296,651],{"class":554},[484,5298,5299],{"class":486,"line":654},[484,5300,5301],{"class":490},"  // ... rest of handler\n",[484,5303,5304,5306],{"class":486,"line":660},[484,5305,669],{"class":501},[484,5307,651],{"class":505},[484,5309,5310],{"class":486,"line":666},[484,5311,529],{"emptyLinePlaceholder":528},[484,5313,5314],{"class":486,"line":834},[484,5315,5316],{"class":490},"// After: Use extension\n",[484,5318,5319,5321,5323],{"class":486,"line":839},[484,5320,690],{"class":541},[484,5322,545],{"class":505},[484,5324,548],{"class":501},[484,5326,5327,5329,5331,5333],{"class":486,"line":845},[484,5328,753],{"class":554},[484,5330,558],{"class":501},[484,5332,611],{"class":593},[484,5334,631],{"class":501},[484,5336,5337,5339,5341],{"class":486,"line":867},[484,5338,771],{"class":554},[484,5340,558],{"class":501},[484,5342,5051],{"class":627},[484,5344,5345,5347],{"class":486,"line":872},[484,5346,669],{"class":501},[484,5348,651],{"class":505},[455,5350,5351],{},"Benefits:",[1717,5353,5354,5357,5360,5363,5366],{},[1720,5355,5356],{},"Automatic window management",[1720,5358,5359],{},"Response headers",[1720,5361,5362],{},"Multiple storage backends",[1720,5364,5365],{},"Better error messages",[1720,5367,5368],{},"Configuration options",[5370,5371,5372],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":480,"searchDepth":494,"depth":494,"links":5374},[5375,5376,5377,5385,5386,5390,5396,5400,5404,5408,5409,5414,5415],{"id":466,"depth":494,"text":194},{"id":705,"depth":494,"text":706},{"id":916,"depth":494,"text":917,"children":5378},[5379,5380,5381,5382,5383,5384],{"id":921,"depth":525,"text":922},{"id":1152,"depth":525,"text":1153},{"id":1217,"depth":525,"text":1218},{"id":1281,"depth":525,"text":1282},{"id":1411,"depth":525,"text":1412},{"id":1511,"depth":525,"text":1512},{"id":1567,"depth":494,"text":1568},{"id":1678,"depth":494,"text":1679,"children":5387},[5388,5389],{"id":1682,"depth":525,"text":1683},{"id":1747,"depth":525,"text":1748},{"id":2263,"depth":494,"text":2264,"children":5391},[5392,5393,5394,5395],{"id":2267,"depth":525,"text":2268},{"id":2586,"depth":525,"text":2587},{"id":2853,"depth":525,"text":2854},{"id":3070,"depth":525,"text":3071},{"id":3334,"depth":494,"text":3335,"children":5397},[5398,5399],{"id":3338,"depth":525,"text":3339},{"id":3643,"depth":525,"text":3644},{"id":3863,"depth":494,"text":271,"children":5401},[5402,5403],{"id":3866,"depth":525,"text":3867},{"id":3934,"depth":525,"text":3935},{"id":4228,"depth":494,"text":4229,"children":5405},[5406,5407],{"id":4232,"depth":525,"text":4233},{"id":4559,"depth":525,"text":4560},{"id":4879,"depth":494,"text":4880},{"id":4946,"depth":494,"text":4947,"children":5410},[5411,5412,5413],{"id":4950,"depth":525,"text":4951},{"id":4971,"depth":525,"text":4972},{"id":5060,"depth":525,"text":5061},{"id":5075,"depth":494,"text":5076},{"id":5105,"depth":494,"text":5106},"Rate limiting is provided as a built-in plugin to protect your API from abuse and ensure fair usage. It registers as pre-auth middleware that runs automatically on every API request.","md",null,{},{"title":243,"description":5416},"3pivjOlde8pVCiGpUGjm-FbwWV9MOVjYCN4vE9NQCnI",[5423,5425],{"title":239,"path":240,"stem":241,"description":5424,"children":-1},"@websideproject/nuxt-auto-api uses Zod for validation with automatic schema generation from Drizzle tables via drizzle-zod.",{"title":247,"path":248,"stem":249,"description":5426,"children":-1},"The Request Metadata Plugin automatically captures request metadata (IP address, geolocation, user-agent, etc.) and makes it available throughout your API handlers. It supports both context-only access (for hooks and authorization) and optional database persistence.",1772977478053]