[{"data":1,"prerenderedAt":986},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fv2\u002Fagent\u002Fserver\u002Fmiddleware":3},{"id":4,"title":5,"body":6,"description":17,"extension":979,"meta":980,"navigation":155,"path":982,"seo":983,"stem":984,"__hash__":985},"docs\u002Fdocs\u002Fv2\u002Fagent\u002Fserver\u002Fmiddleware.md","middleware\u002F",{"type":7,"value":8,"toc":969},"minimark",[9,14,18,25,30,265,268,282,286,295,298,318,322,327,448,452,807,811,944,947,951,965],[10,11,13],"h1",{"id":12},"servermiddleware","server\u002Fmiddleware\u002F",[15,16,17],"p",{},"Middleware files run on every HTTP request before your route handlers. Use them for cross-cutting concerns: logging, cors, rate limiting, request timing, auth checks that apply globally.",[15,19,20,21,24],{},"Files in ",[22,23,13],"code",{}," are loaded in filename order and run before every request.",[26,27,29],"h2",{"id":28},"defining-middleware","Defining middleware",[31,32,37],"pre",{"className":33,"code":34,"language":35,"meta":36,"style":36},"language-ts shiki shiki-themes dark-plus","\u002F\u002F server\u002Fmiddleware\u002Flogging.ts\nexport default defineEventHandler(event => {\n    const start = Date.now()\n    console.log(`→ ${event.method} ${event.path}`)\n\n    \u002F\u002F Attach timing header on response\n    event.node.res.on(\"finish\", () => {\n        console.log(`← ${event.method} ${event.path} ${Date.now() - start}ms`)\n    })\n})\n","ts","",[22,38,39,48,77,102,150,157,163,197,253,259],{"__ignoreMap":36},[40,41,44],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sOLPB","\u002F\u002F server\u002Fmiddleware\u002Flogging.ts\n",[40,49,51,55,58,62,66,70,74],{"class":42,"line":50},2,[40,52,54],{"class":53},"sYbnZ","export",[40,56,57],{"class":53}," default",[40,59,61],{"class":60},"sCudf"," defineEventHandler",[40,63,65],{"class":64},"sTNBD","(",[40,67,69],{"class":68},"s3F5K","event",[40,71,73],{"class":72},"scz_3"," =>",[40,75,76],{"class":64}," {\n",[40,78,80,83,87,90,93,96,99],{"class":42,"line":79},3,[40,81,82],{"class":72},"    const",[40,84,86],{"class":85},"s9McN"," start",[40,88,89],{"class":64}," = ",[40,91,92],{"class":68},"Date",[40,94,95],{"class":64},".",[40,97,98],{"class":60},"now",[40,100,101],{"class":64},"()\n",[40,103,105,108,110,113,115,119,122,124,126,129,132,135,137,139,142,144,147],{"class":42,"line":104},4,[40,106,107],{"class":68},"    console",[40,109,95],{"class":64},[40,111,112],{"class":60},"log",[40,114,65],{"class":64},[40,116,118],{"class":117},"sKc5r","`→ ",[40,120,121],{"class":72},"${",[40,123,69],{"class":68},[40,125,95],{"class":64},[40,127,128],{"class":68},"method",[40,130,131],{"class":72},"}",[40,133,134],{"class":72}," ${",[40,136,69],{"class":68},[40,138,95],{"class":64},[40,140,141],{"class":68},"path",[40,143,131],{"class":72},[40,145,146],{"class":117},"`",[40,148,149],{"class":64},")\n",[40,151,153],{"class":42,"line":152},5,[40,154,156],{"emptyLinePlaceholder":155},true,"\n",[40,158,160],{"class":42,"line":159},6,[40,161,162],{"class":46},"    \u002F\u002F Attach timing header on response\n",[40,164,166,169,171,174,176,179,181,184,186,189,192,195],{"class":42,"line":165},7,[40,167,168],{"class":68},"    event",[40,170,95],{"class":64},[40,172,173],{"class":68},"node",[40,175,95],{"class":64},[40,177,178],{"class":68},"res",[40,180,95],{"class":64},[40,182,183],{"class":60},"on",[40,185,65],{"class":64},[40,187,188],{"class":117},"\"finish\"",[40,190,191],{"class":64},", () ",[40,193,194],{"class":72},"=>",[40,196,76],{"class":64},[40,198,200,203,205,207,209,212,214,216,218,220,222,224,226,228,230,232,234,236,238,240,243,246,248,251],{"class":42,"line":199},8,[40,201,202],{"class":68},"        console",[40,204,95],{"class":64},[40,206,112],{"class":60},[40,208,65],{"class":64},[40,210,211],{"class":117},"`← ",[40,213,121],{"class":72},[40,215,69],{"class":68},[40,217,95],{"class":64},[40,219,128],{"class":68},[40,221,131],{"class":72},[40,223,134],{"class":72},[40,225,69],{"class":68},[40,227,95],{"class":64},[40,229,141],{"class":68},[40,231,131],{"class":72},[40,233,134],{"class":72},[40,235,92],{"class":68},[40,237,95],{"class":64},[40,239,98],{"class":60},[40,241,242],{"class":64},"() - ",[40,244,245],{"class":68},"start",[40,247,131],{"class":72},[40,249,250],{"class":117},"ms`",[40,252,149],{"class":64},[40,254,256],{"class":42,"line":255},9,[40,257,258],{"class":64},"    })\n",[40,260,262],{"class":42,"line":261},10,[40,263,264],{"class":64},"})\n",[15,266,267],{},"Middleware receives the event and can:",[269,270,271,275],"ul",{},[272,273,274],"li",{},"Return nothing to pass through to the next handler",[272,276,277,278,281],{},"Throw a ",[22,279,280],{},"createError"," to short-circuit and return an error response",[26,283,285],{"id":284},"order-of-execution","Order of execution",[31,287,289],{"className":33,"code":288,"language":35,"meta":36,"style":36},"\"request → middleware\u002Fa.ts → middleware\u002Fb.ts → route handler\"\n",[22,290,291],{"__ignoreMap":36},[40,292,293],{"class":42,"line":43},[40,294,288],{"class":117},[15,296,297],{},"If you need specific ordering, prefix filenames with numbers:",[31,299,301],{"className":33,"code":300,"language":35,"meta":36,"style":36},"\"server\u002Fmiddleware\u002F01-logging.ts\"\n\"server\u002Fmiddleware\u002F02-cors.ts\"\n\"server\u002Fmiddleware\u002F99-timing.ts\"\n",[22,302,303,308,313],{"__ignoreMap":36},[40,304,305],{"class":42,"line":43},[40,306,307],{"class":117},"\"server\u002Fmiddleware\u002F01-logging.ts\"\n",[40,309,310],{"class":42,"line":50},[40,311,312],{"class":117},"\"server\u002Fmiddleware\u002F02-cors.ts\"\n",[40,314,315],{"class":42,"line":79},[40,316,317],{"class":117},"\"server\u002Fmiddleware\u002F99-timing.ts\"\n",[26,319,321],{"id":320},"practical-examples","Practical examples",[323,324,326],"h3",{"id":325},"cors","CORS",[31,328,330],{"className":33,"code":329,"language":35,"meta":36,"style":36},"\u002F\u002F server\u002Fmiddleware\u002Fcors.ts\nimport { defineEventHandler, setHeader } from \"h3\"\n\nexport default defineEventHandler(event => {\n    setHeader(event, \"Access-Control-Allow-Origin\", \"*\")\n    setHeader(event, \"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\")\n    setHeader(event, \"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n})\n",[22,331,332,337,363,367,383,404,424,444],{"__ignoreMap":36},[40,333,334],{"class":42,"line":43},[40,335,336],{"class":46},"\u002F\u002F server\u002Fmiddleware\u002Fcors.ts\n",[40,338,339,342,345,348,351,354,357,360],{"class":42,"line":50},[40,340,341],{"class":53},"import",[40,343,344],{"class":64}," { ",[40,346,347],{"class":68},"defineEventHandler",[40,349,350],{"class":64},", ",[40,352,353],{"class":68},"setHeader",[40,355,356],{"class":64}," } ",[40,358,359],{"class":53},"from",[40,361,362],{"class":117}," \"h3\"\n",[40,364,365],{"class":42,"line":79},[40,366,156],{"emptyLinePlaceholder":155},[40,368,369,371,373,375,377,379,381],{"class":42,"line":104},[40,370,54],{"class":53},[40,372,57],{"class":53},[40,374,61],{"class":60},[40,376,65],{"class":64},[40,378,69],{"class":68},[40,380,73],{"class":72},[40,382,76],{"class":64},[40,384,385,388,390,392,394,397,399,402],{"class":42,"line":152},[40,386,387],{"class":60},"    setHeader",[40,389,65],{"class":64},[40,391,69],{"class":68},[40,393,350],{"class":64},[40,395,396],{"class":117},"\"Access-Control-Allow-Origin\"",[40,398,350],{"class":64},[40,400,401],{"class":117},"\"*\"",[40,403,149],{"class":64},[40,405,406,408,410,412,414,417,419,422],{"class":42,"line":159},[40,407,387],{"class":60},[40,409,65],{"class":64},[40,411,69],{"class":68},[40,413,350],{"class":64},[40,415,416],{"class":117},"\"Access-Control-Allow-Methods\"",[40,418,350],{"class":64},[40,420,421],{"class":117},"\"GET, POST, OPTIONS\"",[40,423,149],{"class":64},[40,425,426,428,430,432,434,437,439,442],{"class":42,"line":165},[40,427,387],{"class":60},[40,429,65],{"class":64},[40,431,69],{"class":68},[40,433,350],{"class":64},[40,435,436],{"class":117},"\"Access-Control-Allow-Headers\"",[40,438,350],{"class":64},[40,440,441],{"class":117},"\"Content-Type, Authorization\"",[40,443,149],{"class":64},[40,445,446],{"class":42,"line":199},[40,447,264],{"class":64},[323,449,451],{"id":450},"rate-limiting","Rate limiting",[31,453,455],{"className":33,"code":454,"language":35,"meta":36,"style":36},"\u002F\u002F server\u002Fmiddleware\u002Frate-limit.ts\nimport { defineEventHandler, createError } from \"h3\"\n\nconst requests = new Map\u003Cstring, number[]>() \u002F\u002F ip → [timestamps]\n\nexport default defineEventHandler(event => {\n    const ip = event.node.req.socket.remoteAddress ?? \"unknown\"\n    const now = Date.now()\n    const window = 60000 \u002F\u002F 1 minute\n    const limit = 60 \u002F\u002F requests per minute\n\n    const timestamps = requests.get(ip) ?? []\n    const recent = timestamps.filter(t => now - t \u003C window)\n\n    if (recent.length >= limit) {\n        throw createError({ statusCode: 429, message: \"rate limit exceeded\" })\n    }\n\n    recent.push(now)\n    requests.set(ip, recent)\n})\n",[22,456,457,462,480,484,518,522,538,574,591,607,622,627,653,693,698,724,753,759,764,781,802],{"__ignoreMap":36},[40,458,459],{"class":42,"line":43},[40,460,461],{"class":46},"\u002F\u002F server\u002Fmiddleware\u002Frate-limit.ts\n",[40,463,464,466,468,470,472,474,476,478],{"class":42,"line":50},[40,465,341],{"class":53},[40,467,344],{"class":64},[40,469,347],{"class":68},[40,471,350],{"class":64},[40,473,280],{"class":68},[40,475,356],{"class":64},[40,477,359],{"class":53},[40,479,362],{"class":117},[40,481,482],{"class":42,"line":79},[40,483,156],{"emptyLinePlaceholder":155},[40,485,486,489,492,494,497,500,503,507,509,512,515],{"class":42,"line":104},[40,487,488],{"class":72},"const",[40,490,491],{"class":85}," requests",[40,493,89],{"class":64},[40,495,496],{"class":72},"new",[40,498,499],{"class":60}," Map",[40,501,502],{"class":64},"\u003C",[40,504,506],{"class":505},"sNl3T","string",[40,508,350],{"class":64},[40,510,511],{"class":505},"number",[40,513,514],{"class":64},"[]>() ",[40,516,517],{"class":46},"\u002F\u002F ip → [timestamps]\n",[40,519,520],{"class":42,"line":152},[40,521,156],{"emptyLinePlaceholder":155},[40,523,524,526,528,530,532,534,536],{"class":42,"line":159},[40,525,54],{"class":53},[40,527,57],{"class":53},[40,529,61],{"class":60},[40,531,65],{"class":64},[40,533,69],{"class":68},[40,535,73],{"class":72},[40,537,76],{"class":64},[40,539,540,542,545,547,549,551,553,555,558,560,563,565,568,571],{"class":42,"line":165},[40,541,82],{"class":72},[40,543,544],{"class":85}," ip",[40,546,89],{"class":64},[40,548,69],{"class":68},[40,550,95],{"class":64},[40,552,173],{"class":68},[40,554,95],{"class":64},[40,556,557],{"class":68},"req",[40,559,95],{"class":64},[40,561,562],{"class":68},"socket",[40,564,95],{"class":64},[40,566,567],{"class":68},"remoteAddress",[40,569,570],{"class":64}," ?? ",[40,572,573],{"class":117},"\"unknown\"\n",[40,575,576,578,581,583,585,587,589],{"class":42,"line":199},[40,577,82],{"class":72},[40,579,580],{"class":85}," now",[40,582,89],{"class":64},[40,584,92],{"class":68},[40,586,95],{"class":64},[40,588,98],{"class":60},[40,590,101],{"class":64},[40,592,593,595,598,600,604],{"class":42,"line":255},[40,594,82],{"class":72},[40,596,597],{"class":85}," window",[40,599,89],{"class":64},[40,601,603],{"class":602},"sDGX8","60000",[40,605,606],{"class":46}," \u002F\u002F 1 minute\n",[40,608,609,611,614,616,619],{"class":42,"line":261},[40,610,82],{"class":72},[40,612,613],{"class":85}," limit",[40,615,89],{"class":64},[40,617,618],{"class":602},"60",[40,620,621],{"class":46}," \u002F\u002F requests per minute\n",[40,623,625],{"class":42,"line":624},11,[40,626,156],{"emptyLinePlaceholder":155},[40,628,630,632,635,637,640,642,645,647,650],{"class":42,"line":629},12,[40,631,82],{"class":72},[40,633,634],{"class":85}," timestamps",[40,636,89],{"class":64},[40,638,639],{"class":68},"requests",[40,641,95],{"class":64},[40,643,644],{"class":60},"get",[40,646,65],{"class":64},[40,648,649],{"class":68},"ip",[40,651,652],{"class":64},") ?? []\n",[40,654,656,658,661,663,666,668,671,673,676,678,680,683,685,688,691],{"class":42,"line":655},13,[40,657,82],{"class":72},[40,659,660],{"class":85}," recent",[40,662,89],{"class":64},[40,664,665],{"class":68},"timestamps",[40,667,95],{"class":64},[40,669,670],{"class":60},"filter",[40,672,65],{"class":64},[40,674,675],{"class":68},"t",[40,677,73],{"class":72},[40,679,580],{"class":68},[40,681,682],{"class":64}," - ",[40,684,675],{"class":68},[40,686,687],{"class":64}," \u003C ",[40,689,690],{"class":68},"window",[40,692,149],{"class":64},[40,694,696],{"class":42,"line":695},14,[40,697,156],{"emptyLinePlaceholder":155},[40,699,701,704,707,710,712,715,718,721],{"class":42,"line":700},15,[40,702,703],{"class":53},"    if",[40,705,706],{"class":64}," (",[40,708,709],{"class":68},"recent",[40,711,95],{"class":64},[40,713,714],{"class":68},"length",[40,716,717],{"class":64}," >= ",[40,719,720],{"class":68},"limit",[40,722,723],{"class":64},") {\n",[40,725,727,730,733,736,739,742,744,747,750],{"class":42,"line":726},16,[40,728,729],{"class":53},"        throw",[40,731,732],{"class":60}," createError",[40,734,735],{"class":64},"({ ",[40,737,738],{"class":68},"statusCode:",[40,740,741],{"class":602}," 429",[40,743,350],{"class":64},[40,745,746],{"class":68},"message:",[40,748,749],{"class":117}," \"rate limit exceeded\"",[40,751,752],{"class":64}," })\n",[40,754,756],{"class":42,"line":755},17,[40,757,758],{"class":64},"    }\n",[40,760,762],{"class":42,"line":761},18,[40,763,156],{"emptyLinePlaceholder":155},[40,765,767,770,772,775,777,779],{"class":42,"line":766},19,[40,768,769],{"class":68},"    recent",[40,771,95],{"class":64},[40,773,774],{"class":60},"push",[40,776,65],{"class":64},[40,778,98],{"class":68},[40,780,149],{"class":64},[40,782,784,787,789,792,794,796,798,800],{"class":42,"line":783},20,[40,785,786],{"class":68},"    requests",[40,788,95],{"class":64},[40,790,791],{"class":60},"set",[40,793,65],{"class":64},[40,795,649],{"class":68},[40,797,350],{"class":64},[40,799,709],{"class":68},[40,801,149],{"class":64},[40,803,805],{"class":42,"line":804},21,[40,806,264],{"class":64},[323,808,810],{"id":809},"request-validation","Request validation",[31,812,814],{"className":33,"code":813,"language":35,"meta":36,"style":36},"\u002F\u002F server\u002Fmiddleware\u002Fapi-key.ts\nimport { defineEventHandler, getRequestHeader, createError } from \"h3\"\n\nexport default defineEventHandler(event => {\n    const key = getRequestHeader(event, \"x-api-key\")\n    if (!key || !key.startsWith(\"axon_\")) {\n        throw createError({ statusCode: 401, message: \"invalid api key\" })\n    }\n})\n",[22,815,816,821,844,848,864,886,914,936,940],{"__ignoreMap":36},[40,817,818],{"class":42,"line":43},[40,819,820],{"class":46},"\u002F\u002F server\u002Fmiddleware\u002Fapi-key.ts\n",[40,822,823,825,827,829,831,834,836,838,840,842],{"class":42,"line":50},[40,824,341],{"class":53},[40,826,344],{"class":64},[40,828,347],{"class":68},[40,830,350],{"class":64},[40,832,833],{"class":68},"getRequestHeader",[40,835,350],{"class":64},[40,837,280],{"class":68},[40,839,356],{"class":64},[40,841,359],{"class":53},[40,843,362],{"class":117},[40,845,846],{"class":42,"line":79},[40,847,156],{"emptyLinePlaceholder":155},[40,849,850,852,854,856,858,860,862],{"class":42,"line":104},[40,851,54],{"class":53},[40,853,57],{"class":53},[40,855,61],{"class":60},[40,857,65],{"class":64},[40,859,69],{"class":68},[40,861,73],{"class":72},[40,863,76],{"class":64},[40,865,866,868,871,873,875,877,879,881,884],{"class":42,"line":152},[40,867,82],{"class":72},[40,869,870],{"class":85}," key",[40,872,89],{"class":64},[40,874,833],{"class":60},[40,876,65],{"class":64},[40,878,69],{"class":68},[40,880,350],{"class":64},[40,882,883],{"class":117},"\"x-api-key\"",[40,885,149],{"class":64},[40,887,888,890,893,896,899,901,903,906,908,911],{"class":42,"line":159},[40,889,703],{"class":53},[40,891,892],{"class":64}," (!",[40,894,895],{"class":68},"key",[40,897,898],{"class":64}," || !",[40,900,895],{"class":68},[40,902,95],{"class":64},[40,904,905],{"class":60},"startsWith",[40,907,65],{"class":64},[40,909,910],{"class":117},"\"axon_\"",[40,912,913],{"class":64},")) {\n",[40,915,916,918,920,922,924,927,929,931,934],{"class":42,"line":165},[40,917,729],{"class":53},[40,919,732],{"class":60},[40,921,735],{"class":64},[40,923,738],{"class":68},[40,925,926],{"class":602}," 401",[40,928,350],{"class":64},[40,930,746],{"class":68},[40,932,933],{"class":117}," \"invalid api key\"",[40,935,752],{"class":64},[40,937,938],{"class":42,"line":199},[40,939,758],{"class":64},[40,941,942],{"class":42,"line":255},[40,943,264],{"class":64},[15,945,946],{},"Note: Deployed routes already require auth by default. Use middleware for additional checks, not for the default auth.",[26,948,950],{"id":949},"what-middleware-cannot-do","What middleware cannot do",[269,952,953,956],{},[272,954,955],{},"Middleware cannot modify the response body after the handler runs.",[272,957,958,959,964],{},"For async initialization (connecting to services), use ",[960,961,963],"a",{"href":962},"\u002Fdocs\u002Fv2\u002Fagent\u002Fserver\u002Fplugins","plugins"," instead — plugins run once at boot, middleware runs on every request.",[966,967,968],"style",{},"html pre.shiki code .sOLPB, html code.shiki .sOLPB{--shiki-default:#6A9955}html pre.shiki code .sYbnZ, html code.shiki .sYbnZ{--shiki-default:#C586C0}html pre.shiki code .sCudf, html code.shiki .sCudf{--shiki-default:#DCDCAA}html pre.shiki code .sTNBD, html code.shiki .sTNBD{--shiki-default:#D4D4D4}html pre.shiki code .s3F5K, html code.shiki .s3F5K{--shiki-default:#9CDCFE}html pre.shiki code .scz_3, html code.shiki .scz_3{--shiki-default:#569CD6}html pre.shiki code .s9McN, html code.shiki .s9McN{--shiki-default:#4FC1FF}html pre.shiki code .sKc5r, html code.shiki .sKc5r{--shiki-default:#CE9178}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 pre.shiki code .sNl3T, html code.shiki .sNl3T{--shiki-default:#4EC9B0}html pre.shiki code .sDGX8, html code.shiki .sDGX8{--shiki-default:#B5CEA8}",{"title":36,"searchDepth":50,"depth":50,"links":970},[971,972,973,978],{"id":28,"depth":50,"text":29},{"id":284,"depth":50,"text":285},{"id":320,"depth":50,"text":321,"children":974},[975,976,977],{"id":325,"depth":79,"text":326},{"id":450,"depth":79,"text":451},{"id":809,"depth":79,"text":810},{"id":949,"depth":50,"text":950},"md",{"icon":981},"vscode-icons:file-type-typescript","\u002Fdocs\u002Fv2\u002Fagent\u002Fserver\u002Fmiddleware",{"title":5,"description":17},"docs\u002Fv2\u002Fagent\u002Fserver\u002Fmiddleware","mqtWRGjpf9YHLECCXvPShG0FrRRgWdZlnrXHrTCEsXA",1785671806439]