[{"data":1,"prerenderedAt":812},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fcore-concepts\u002Fdecorators":184,"\u002Fdocs\u002Fcore-concepts\u002Fdecorators-surround":807},[4,21,90,166],{"title":5,"path":6,"stem":7,"children":8,"status":11,"icon":20},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,16],{"title":10,"path":6,"stem":7,"status":11},"Introduction",null,{"title":13,"path":14,"stem":15,"status":11},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F2.installation",{"title":17,"path":18,"stem":19,"status":11},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","1.docs\u002F1.getting-started\u002F3.quick-start",false,{"title":22,"path":23,"stem":24,"children":25,"status":11,"icon":20},"Core Concepts","\u002Fdocs\u002Fcore-concepts","1.docs\u002F2.core-concepts\u002F1.index",[26,28,32,36,40,50,54,58,62,66,70,74,78,82,86],{"title":27,"path":23,"stem":24,"status":11},"Overview",{"title":29,"path":30,"stem":31,"status":11},"Response","\u002Fdocs\u002Fcore-concepts\u002Fresponse","1.docs\u002F2.core-concepts\u002F10.response",{"title":33,"path":34,"stem":35,"status":11},"Cookies","\u002Fdocs\u002Fcore-concepts\u002Fcookies","1.docs\u002F2.core-concepts\u002F11.cookies",{"title":37,"path":38,"stem":39,"status":11},"Testing","\u002Fdocs\u002Fcore-concepts\u002Ftesting","1.docs\u002F2.core-concepts\u002F12.testing",{"title":41,"path":42,"stem":43,"children":44,"status":11,"icon":20,"defaultOpen":20},"Decorators","\u002Fdocs\u002Fcore-concepts\u002Fdecorators","1.docs\u002F2.core-concepts\u002F13.decorators\u002F1.index",[45,46],{"title":27,"path":42,"stem":43,"status":11},{"title":47,"path":48,"stem":49,"status":11},"Custom","\u002Fdocs\u002Fcore-concepts\u002Fdecorators\u002Fcustom","1.docs\u002F2.core-concepts\u002F13.decorators\u002F2.custom",{"title":51,"path":52,"stem":53,"status":11},"Discovery Service","\u002Fdocs\u002Fcore-concepts\u002Fdiscovery","1.docs\u002F2.core-concepts\u002F14.discovery",{"title":55,"path":56,"stem":57,"status":11},"Application Lifecycle","\u002Fdocs\u002Fcore-concepts\u002Fapp-lifecycle","1.docs\u002F2.core-concepts\u002F15.app-lifecycle",{"title":59,"path":60,"stem":61,"status":11},"Controllers","\u002Fdocs\u002Fcore-concepts\u002Fcontrollers","1.docs\u002F2.core-concepts\u002F2.controllers",{"title":63,"path":64,"stem":65,"status":11},"Routing","\u002Fdocs\u002Fcore-concepts\u002Frouting","1.docs\u002F2.core-concepts\u002F3.routing",{"title":67,"path":68,"stem":69,"status":11},"Providers","\u002Fdocs\u002Fcore-concepts\u002Fproviders","1.docs\u002F2.core-concepts\u002F4.providers",{"title":71,"path":72,"stem":73,"status":11},"Modules","\u002Fdocs\u002Fcore-concepts\u002Fmodules","1.docs\u002F2.core-concepts\u002F5.modules",{"title":75,"path":76,"stem":77,"status":11},"Configuration","\u002Fdocs\u002Fcore-concepts\u002Fconfiguration","1.docs\u002F2.core-concepts\u002F6.configuration",{"title":79,"path":80,"stem":81,"status":11},"Middleware","\u002Fdocs\u002Fcore-concepts\u002Fmiddleware","1.docs\u002F2.core-concepts\u002F7.middleware",{"title":83,"path":84,"stem":85,"status":11},"Guards","\u002Fdocs\u002Fcore-concepts\u002Fguards","1.docs\u002F2.core-concepts\u002F8.guards",{"title":87,"path":88,"stem":89,"status":11},"Exceptions","\u002Fdocs\u002Fcore-concepts\u002Fexceptions","1.docs\u002F2.core-concepts\u002F9.exceptions",{"title":91,"path":92,"stem":93,"children":94,"status":11,"icon":20},"Packages","\u002Fdocs\u002Fpackages","1.docs\u002F3.packages\u002F1.index",[95,96,101,112,116,120,138,142,146,150,154,158,162],{"title":27,"path":92,"stem":93,"status":11},{"title":97,"path":98,"stem":99,"status":100},"CLI","\u002Fdocs\u002Fpackages\u002Fcli","1.docs\u002F3.packages\u002F10.cli","experimental",{"title":102,"path":103,"stem":104,"children":105,"status":11,"icon":20,"defaultOpen":20},"Events","\u002Fdocs\u002Fpackages\u002Fmessaging","1.docs\u002F3.packages\u002F11.messaging\u002F1.index",[106,108],{"title":27,"path":103,"stem":104,"status":107},"beta",{"title":109,"path":110,"stem":111,"status":100},"Redis","\u002Fdocs\u002Fpackages\u002Fmessaging\u002Fredis","1.docs\u002F3.packages\u002F11.messaging\u002F2.redis",{"title":113,"path":114,"stem":115,"status":107},"Serve Static","\u002Fdocs\u002Fpackages\u002Fserve-static","1.docs\u002F3.packages\u002F12.serve-static",{"title":117,"path":118,"stem":119,"status":107},"Rate Limit","\u002Fdocs\u002Fpackages\u002Frate-limit","1.docs\u002F3.packages\u002F13.rate-limit",{"title":121,"path":122,"stem":123,"children":124,"status":11,"icon":20,"defaultOpen":20},"Auth","\u002Fdocs\u002Fpackages\u002Fauth","1.docs\u002F3.packages\u002F2.auth\u002F1.index",[125,126,130,134],{"title":27,"path":122,"stem":123,"status":107},{"title":127,"path":128,"stem":129,"status":107},"JWT Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Fjwt","1.docs\u002F3.packages\u002F2.auth\u002F2.jwt",{"title":131,"path":132,"stem":133,"status":107},"Local Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Flocal","1.docs\u002F3.packages\u002F2.auth\u002F3.local",{"title":135,"path":136,"stem":137,"status":100},"OAuth2 Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Foauth2","1.docs\u002F3.packages\u002F2.auth\u002F4.oauth2",{"title":139,"path":140,"stem":141,"status":107},"JWT","\u002Fdocs\u002Fpackages\u002Fjwt","1.docs\u002F3.packages\u002F3.jwt",{"title":143,"path":144,"stem":145,"status":107},"Drizzle","\u002Fdocs\u002Fpackages\u002Fdrizzle","1.docs\u002F3.packages\u002F4.drizzle",{"title":147,"path":148,"stem":149,"status":107},"Papr","\u002Fdocs\u002Fpackages\u002Fpapr","1.docs\u002F3.packages\u002F5.papr",{"title":151,"path":152,"stem":153,"status":107},"Mongoose","\u002Fdocs\u002Fpackages\u002Fmongoose","1.docs\u002F3.packages\u002F6.mongoose",{"title":155,"path":156,"stem":157,"status":107},"Swagger","\u002Fdocs\u002Fpackages\u002Fswagger","1.docs\u002F3.packages\u002F7.swagger",{"title":159,"path":160,"stem":161,"status":107},"Node Server","\u002Fdocs\u002Fpackages\u002Fnode-server","1.docs\u002F3.packages\u002F8.node-server",{"title":163,"path":164,"stem":165,"status":107},"uWS Server","\u002Fdocs\u002Fpackages\u002Fuws-server","1.docs\u002F3.packages\u002F9.uws-server",{"title":167,"path":168,"stem":169,"children":170,"status":11,"icon":20},"Roadmap","\u002Fdocs\u002Froadmap","1.docs\u002F4.roadmap\u002F1.index",[171,172,176,180],{"title":27,"path":168,"stem":169,"status":11},{"title":173,"path":174,"stem":175,"status":11},"Short term (0-3 months)","\u002Fdocs\u002Froadmap\u002Fshort-term","1.docs\u002F4.roadmap\u002F2.short-term",{"title":177,"path":178,"stem":179,"status":11},"Mid term (3-9 months)","\u002Fdocs\u002Froadmap\u002Fmid-term","1.docs\u002F4.roadmap\u002F3.mid-term",{"title":181,"path":182,"stem":183,"status":11},"Long term (9-12+ months)","\u002Fdocs\u002Froadmap\u002Flong-term","1.docs\u002F4.roadmap\u002F4.long-term",{"id":185,"title":27,"body":186,"description":802,"extension":803,"meta":804,"navigation":557,"path":42,"seo":805,"status":11,"stem":43,"__hash__":806},"docs\u002F1.docs\u002F2.core-concepts\u002F13.decorators\u002F1.index.md",{"type":187,"value":188,"toc":795},"minimark",[189,212,232,237,240,263,266,270,277,478,488,492,505,654,657,681,685,696,732,735,746,750,770,781,791],[190,191,192,193,197,198,197,201,197,204,207,208,211],"p",{},"MiiaJS is a decorator-driven framework. ",[194,195,196],"code",{},"@Controller",", ",[194,199,200],{},"@Get",[194,202,203],{},"@UseGuard",[194,205,206],{},"@ValidateBody"," - every declarative knob you reach for is a decorator that attaches metadata to a class or method. The framework reads that metadata later (during ",[194,209,210],{},"app.init()",", in middleware, or in tools like Swagger spec generation) and turns it into runtime behaviour.",[190,213,214,215,219,220,223,224,227,228,231],{},"The decorator system is built on ",[216,217,218],"strong",{},"TC39 native decorators"," - no ",[194,221,222],{},"reflect-metadata",", no experimental compiler flags, no WeakMap-based shadow stores. The ",[194,225,226],{},"Symbol.metadata"," polyfill ships as the first import of ",[194,229,230],{},"@miiajs\u002Fcore",", so it's ready everywhere your code runs.",[233,234,236],"h2",{"id":235},"mental-model","Mental model",[190,238,239],{},"The whole story is two steps:",[241,242,243,254],"ol",{},[244,245,246,249,250,253],"li",{},[216,247,248],{},"Decorators attach metadata at class definition time."," Each decorator runs once, when the class is declared, and writes structured data onto ",[194,251,252],{},"Class[Symbol.metadata]"," keyed by symbols.",[244,255,256,259,260,262],{},[216,257,258],{},"The framework reads that metadata later."," ",[194,261,210],{}," walks every controller, pulls routes, middleware, guards, status codes, and validation schemas off the metadata, and wires them into the router. Per-request middleware can also read metadata to make decisions.",[190,264,265],{},"If you internalise this, every other piece of the framework - and your own custom decorators - falls out for free.",[233,267,269],{"id":268},"built-in-decorators","Built-in decorators",[190,271,272,273,276],{},"Every decorator below is built with the same public factory API that's available to you in ",[274,275,47],"a",{"href":48},".",[278,279,280,299],"table",{},[281,282,283],"thead",{},[284,285,286,290,293,296],"tr",{},[287,288,289],"th",{},"Decorator",[287,291,292],{},"Kind",[287,294,295],{},"What it does",[287,297,298],{},"See",[300,301,302,320,335,351,385,401,422,443,460],"tbody",{},[284,303,304,310,313,316],{},[305,306,307],"td",{},[194,308,309],{},"@Injectable",[305,311,312],{},"class",[305,314,315],{},"Marks a class as DI-resolvable",[305,317,318],{},[274,319,67],{"href":68},[284,321,322,326,328,331],{},[305,323,324],{},[194,325,196],{},[305,327,312],{},[305,329,330],{},"Route prefix + controller marker",[305,332,333],{},[274,334,59],{"href":60},[284,336,337,342,344,347],{},[305,338,339],{},[194,340,341],{},"@Module",[305,343,312],{},[305,345,346],{},"Groups controllers, providers, and imports",[305,348,349],{},[274,350,71],{"href":72},[284,352,353,375,378,381],{},[305,354,355,259,357,259,360,259,363,259,366,259,369,259,372],{},[194,356,200],{},[194,358,359],{},"@Post",[194,361,362],{},"@Put",[194,364,365],{},"@Patch",[194,367,368],{},"@Delete",[194,370,371],{},"@Head",[194,373,374],{},"@Options",[305,376,377],{},"method",[305,379,380],{},"Bind an HTTP route to a handler",[305,382,383],{},[274,384,63],{"href":64},[284,386,387,392,394,397],{},[305,388,389],{},[194,390,391],{},"@Status",[305,393,377],{},[305,395,396],{},"Default status code for the handler's response",[305,398,399],{},[274,400,59],{"href":60},[284,402,403,413,415,418],{},[305,404,405,259,407,259,410],{},[194,406,206],{},[194,408,409],{},"@ValidateQuery",[194,411,412],{},"@ValidateParams",[305,414,377],{},[305,416,417],{},"Zod-like schema validation, transparently replaces the parsed value",[305,419,420],{},[274,421,59],{"href":60},[284,423,424,429,436,439],{},[305,425,426],{},[194,427,428],{},"@Use",[305,430,431,432,435],{},"class ",[216,433,434],{},"or"," method",[305,437,438],{},"Apply Koa-style middleware",[305,440,441],{},[274,442,79],{"href":80},[284,444,445,449,453,456],{},[305,446,447],{},[194,448,203],{},[305,450,431,451,435],{},[216,452,434],{},[305,454,455],{},"Apply one or more guards",[305,457,458],{},[274,459,83],{"href":84},[284,461,462,467,471,474],{},[305,463,464],{},[194,465,466],{},"@SkipGuard",[305,468,431,469,435],{},[216,470,434],{},[305,472,473],{},"Exclude a previously-applied guard from a route",[305,475,476],{},[274,477,83],{"href":84},[190,479,480,481,197,483,197,485,487],{},"The class\u002Fmethod dual decorators (",[194,482,428],{},[194,484,203],{},[194,486,466],{},") inspect their TC39 context and branch - applied to a class they affect every handler in the controller, applied to a method they affect just that handler.",[233,489,491],{"id":490},"storage-model","Storage model",[190,493,494,495,497,498,501,502,504],{},"Metadata lives on the class constructor under ",[194,496,226],{},", keyed by ",[194,499,500],{},"Symbol","s exported from ",[194,503,230],{},":",[506,507,512],"pre",{"className":508,"code":509,"language":510,"meta":511,"style":511},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { ROUTES, getMeta } from '@miiajs\u002Fcore'\n\nclass UserController {\n  @Get('\u002F')\n  list() { \u002F* ... *\u002F }\n}\n\nconst routes = getMeta(UserController, ROUTES)\n\u002F\u002F → [{ method: 'GET', path: '\u002F', handlerName: 'list' }]\n","typescript","",[194,513,514,552,559,572,596,615,621,626,648],{"__ignoreMap":511},[515,516,519,523,527,531,534,537,540,543,546,549],"span",{"class":517,"line":518},"line",1,[515,520,522],{"class":521},"s7zQu","import",[515,524,526],{"class":525},"sMK4o"," {",[515,528,530],{"class":529},"sTEyZ"," ROUTES",[515,532,533],{"class":525},",",[515,535,536],{"class":529}," getMeta",[515,538,539],{"class":525}," }",[515,541,542],{"class":521}," from",[515,544,545],{"class":525}," '",[515,547,230],{"class":548},"sfazB",[515,550,551],{"class":525},"'\n",[515,553,555],{"class":517,"line":554},2,[515,556,558],{"emptyLinePlaceholder":557},true,"\n",[515,560,562,565,569],{"class":517,"line":561},3,[515,563,312],{"class":564},"spNyl",[515,566,568],{"class":567},"sBMFI"," UserController",[515,570,571],{"class":525}," {\n",[515,573,575,578,582,585,588,591,593],{"class":517,"line":574},4,[515,576,577],{"class":525},"  @",[515,579,581],{"class":580},"s2Zo4","Get",[515,583,584],{"class":529},"(",[515,586,587],{"class":525},"'",[515,589,590],{"class":548},"\u002F",[515,592,587],{"class":525},[515,594,595],{"class":529},")\n",[515,597,599,603,606,608,612],{"class":517,"line":598},5,[515,600,602],{"class":601},"swJcz","  list",[515,604,605],{"class":525},"()",[515,607,526],{"class":525},[515,609,611],{"class":610},"sHwdD"," \u002F* ... *\u002F",[515,613,614],{"class":525}," }\n",[515,616,618],{"class":517,"line":617},6,[515,619,620],{"class":525},"}\n",[515,622,624],{"class":517,"line":623},7,[515,625,558],{"emptyLinePlaceholder":557},[515,627,629,632,635,638,640,643,645],{"class":517,"line":628},8,[515,630,631],{"class":564},"const",[515,633,634],{"class":529}," routes ",[515,636,637],{"class":525},"=",[515,639,536],{"class":580},[515,641,642],{"class":529},"(UserController",[515,644,533],{"class":525},[515,646,647],{"class":529}," ROUTES)\n",[515,649,651],{"class":517,"line":650},9,[515,652,653],{"class":610},"\u002F\u002F → [{ method: 'GET', path: '\u002F', handlerName: 'list' }]\n",[190,655,656],{},"A few properties to remember:",[658,659,660,666,675],"ul",{},[244,661,662,665],{},[216,663,664],{},"Class-level scope."," Metadata is attached to the constructor, not to instances. Every instance shares the same metadata.",[244,667,668,671,672,674],{},[216,669,670],{},"Symbol keys."," Always use ",[194,673,500],{},"s, never strings - string keys would collide with user-land properties on the metadata object.",[244,676,677,680],{},[216,678,679],{},"Lifetime = module lifetime."," The metadata exists for as long as the class is reachable. There's no per-request reset.",[233,682,684],{"id":683},"execution-model","Execution model",[190,686,687,688,691,692,695],{},"Decorators run ",[216,689,690],{},"once",", at class definition, in ",[216,693,694],{},"bottom-up order"," when stacked:",[506,697,699],{"className":508,"code":698,"language":510,"meta":511,"style":511},"@A   \u002F\u002F runs second\n@B   \u002F\u002F runs first\nclass Foo {}\n",[194,700,701,712,722],{"__ignoreMap":511},[515,702,703,706,709],{"class":517,"line":518},[515,704,705],{"class":525},"@",[515,707,708],{"class":529},"A   ",[515,710,711],{"class":610},"\u002F\u002F runs second\n",[515,713,714,716,719],{"class":517,"line":554},[515,715,705],{"class":525},[515,717,718],{"class":529},"B   ",[515,720,721],{"class":610},"\u002F\u002F runs first\n",[515,723,724,726,729],{"class":517,"line":561},[515,725,312],{"class":564},[515,727,728],{"class":567}," Foo",[515,730,731],{"class":525}," {}\n",[190,733,734],{},"This matters in exactly one situation: when one decorator reads metadata that another just wrote. In practice most decorators only write, so order is irrelevant - but if you're building decorators that read each other's output, remember the rule.",[190,736,737,738,741,742,745],{},"Decorators are ",[216,739,740],{},"never"," invoked per request. If you need request-time behaviour, write ",[274,743,744],{"href":80},"middleware"," - the decorator's job is to declare intent, the middleware's job is to act on it.",[233,747,749],{"id":748},"extending-the-framework","Extending the framework",[190,751,752,753,755,756,759,760,197,763,197,766,769],{},"Everything in the catalogue above is built with the four factory functions and five metadata helpers that ",[194,754,230],{}," exposes publicly. The same API powers ",[194,757,758],{},"@miiajs\u002Fswagger","'s ",[194,761,762],{},"@ApiTag",[194,764,765],{},"@ApiOperation",[194,767,768],{},"@ApiSecurity",", etc., and it's available to you for any project-specific decorator you need.",[190,771,772,773,776,777,780],{},"For higher-level recipes that always apply together - e.g. ",[194,774,775],{},"@AdminOnly()"," that bundles auth, a role guard, and a status code - compose them with ",[194,778,779],{},"applyDecorators",". No metadata plumbing required; it just stacks existing decorators into one.",[190,782,783,784,787,788,790],{},"See ",[274,785,786],{"href":48},"Custom Decorators"," for the factories, helpers, walkthroughs, and the ",[194,789,779],{}," recipe.",[792,793,794],"style",{},"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 .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 .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 .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 .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);}",{"title":511,"searchDepth":554,"depth":554,"links":796},[797,798,799,800,801],{"id":235,"depth":554,"text":236},{"id":268,"depth":554,"text":269},{"id":490,"depth":554,"text":491},{"id":683,"depth":554,"text":684},{"id":748,"depth":554,"text":749},"How decorators work in MiiaJS - TC39 native, Symbol.metadata, and the catalogue of built-ins.","md",{},{"title":27,"description":802},"xKn6VG6TfE0Yai3G4AZVdDDBN2ykoPUfloGIIQhcKlQ",[808,810],{"title":37,"path":38,"stem":39,"description":809,"children":-1},"Test your application with the built-in TestApp utility.",{"title":47,"path":48,"stem":49,"description":811,"children":-1},"Build your own decorators with createClassDecorator, createMethodDecorator, createDecorator, createFieldDecorator, and the metadata helpers.",1784659937411]