[{"data":1,"prerenderedAt":554},["ShallowReactive",2],{"docs:\u002Fdocs\u002Fopenapi":3,"i-lucide:moon":545,"i-lucide:github":550,"i-lucide:menu":552},{"id":4,"title":5,"body":6,"description":537,"extension":538,"meta":539,"navigation":540,"path":541,"seo":542,"stem":543,"__hash__":544},"docs\u002Fdocs\u002Fopenapi.md","OpenAPI",{"type":7,"value":8,"toc":530},"minimark",[9,13,18,46,62,66,76,148,163,312,335,338,342,355,383,387,409,420,428,433,437,440,486,493,502,526],[10,11,12],"p",{},"The generated document is based on the same request and response schemas used by the runtime.",[14,15,17],"h2",{"id":16},"schema-route","Schema route",[10,19,20,21,25,26,29,30,33,34,29,36,39,40,29,43,45],{},"By default, the module serves an OpenAPI document at ",[22,23,24],"code",{},"\u002F_endpoints\u002Fschema",", but only in development. Set ",[22,27,28],{},"endpoints.openApi"," to ",[22,31,32],{},"false"," to disable the generated route entirely, or explicitly set ",[22,35,28],{},[22,37,38],{},"true"," (or ",[22,41,42],{},"endpoints.openApi.enabled",[22,44,38],{},") to also serve it in production.",[47,48,53],"pre",{"className":49,"code":50,"language":51,"meta":52,"style":52},"language-http shiki shiki-themes github-light-default github-dark-default","GET \u002F_endpoints\u002Fschema\n","http","",[22,54,55],{"__ignoreMap":52},[56,57,60],"span",{"class":58,"line":59},"line",1,[56,61,50],{},[14,63,65],{"id":64},"document-metadata","Document metadata",[10,67,68,71,72,75],{},[22,69,70],{},"title",", ",[22,73,74],{},"version",", and the schema route itself are plain values, so they are module options:",[47,77,81],{"className":78,"code":79,"language":80,"meta":52,"style":52},"language-ts shiki shiki-themes github-light-default github-dark-default","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  endpoints: {\n    openApi: { title: 'Example API', version: '1.0.0' },\n  },\n})\n","ts",[22,82,83,89,107,114,133,142],{"__ignoreMap":52},[56,84,85],{"class":58,"line":59},[56,86,88],{"class":87},"sA0OV","\u002F\u002F nuxt.config.ts\n",[56,90,92,96,99,103],{"class":58,"line":91},2,[56,93,95],{"class":94},"swxxe","export",[56,97,98],{"class":94}," default",[56,100,102],{"class":101},"s9HWX"," defineNuxtConfig",[56,104,106],{"class":105},"sVS1I","({\n",[56,108,110],{"class":58,"line":109},3,[56,111,113],{"class":112},"saQ_J","  endpoints: {\n",[56,115,117,120,124,127,130],{"class":58,"line":116},4,[56,118,119],{"class":112},"    openApi: { title: ",[56,121,123],{"class":122},"sZItA","'Example API'",[56,125,126],{"class":112},", version: ",[56,128,129],{"class":122},"'1.0.0'",[56,131,132],{"class":112}," },\n",[56,134,136,139],{"class":58,"line":135},5,[56,137,138],{"class":112},"  }",[56,140,141],{"class":105},",\n",[56,143,145],{"class":58,"line":144},6,[56,146,147],{"class":105},"})\n",[10,149,150,151,154,155,162],{},"Everything else about the document belongs to the application rather than to any endpoint — where the API is deployed, how it is authenticated, how operations are grouped. None of that can come from ",[22,152,153],{},"nuxt.config.ts",", because module options reach the server as JSON and an extension callback is a function. It is declared in ",[156,157,159],"a",{"href":158},"\u002Fdocs\u002Fendpoints#hooks",[22,160,161],{},"server\u002Fendpoints\u002Fruntime.ts",", alongside the other application-wide endpoint settings:",[47,164,166],{"className":78,"code":165,"language":80,"meta":52,"style":52},"\u002F\u002F server\u002Fendpoints\u002Fruntime.ts\nexport default defineEndpointRuntime({\n  openApi: {\n    document: {\n      servers: [{ url: 'https:\u002F\u002Fapi.example.com' }],\n      components: {\n        securitySchemes: {\n          bearerAuth: { type: 'http', scheme: 'bearer' },\n        },\n      },\n      tags: [{ name: 'users', description: 'User management' }],\n    },\n    extend(document) {\n      document.security = [{ bearerAuth: [] }]\n    },\n  },\n})\n",[22,167,168,173,184,189,194,205,210,216,233,239,245,262,268,283,295,300,307],{"__ignoreMap":52},[56,169,170],{"class":58,"line":59},[56,171,172],{"class":87},"\u002F\u002F server\u002Fendpoints\u002Fruntime.ts\n",[56,174,175,177,179,182],{"class":58,"line":91},[56,176,95],{"class":94},[56,178,98],{"class":94},[56,180,181],{"class":101}," defineEndpointRuntime",[56,183,106],{"class":105},[56,185,186],{"class":58,"line":109},[56,187,188],{"class":112},"  openApi: {\n",[56,190,191],{"class":58,"line":116},[56,192,193],{"class":112},"    document: {\n",[56,195,196,199,202],{"class":58,"line":135},[56,197,198],{"class":112},"      servers: [{ url: ",[56,200,201],{"class":122},"'https:\u002F\u002Fapi.example.com'",[56,203,204],{"class":112}," }],\n",[56,206,207],{"class":58,"line":144},[56,208,209],{"class":112},"      components: {\n",[56,211,213],{"class":58,"line":212},7,[56,214,215],{"class":112},"        securitySchemes: {\n",[56,217,219,222,225,228,231],{"class":58,"line":218},8,[56,220,221],{"class":112},"          bearerAuth: { type: ",[56,223,224],{"class":122},"'http'",[56,226,227],{"class":112},", scheme: ",[56,229,230],{"class":122},"'bearer'",[56,232,132],{"class":112},[56,234,236],{"class":58,"line":235},9,[56,237,238],{"class":112},"        },\n",[56,240,242],{"class":58,"line":241},10,[56,243,244],{"class":112},"      },\n",[56,246,248,251,254,257,260],{"class":58,"line":247},11,[56,249,250],{"class":112},"      tags: [{ name: ",[56,252,253],{"class":122},"'users'",[56,255,256],{"class":112},", description: ",[56,258,259],{"class":122},"'User management'",[56,261,204],{"class":112},[56,263,265],{"class":58,"line":264},12,[56,266,267],{"class":112},"    },\n",[56,269,271,274,277,280],{"class":58,"line":270},13,[56,272,273],{"class":101},"    extend",[56,275,276],{"class":112},"(",[56,278,279],{"class":105},"document",[56,281,282],{"class":112},") {\n",[56,284,286,289,292],{"class":58,"line":285},14,[56,287,288],{"class":112},"      document.security ",[56,290,291],{"class":94},"=",[56,293,294],{"class":112}," [{ bearerAuth: [] }]\n",[56,296,298],{"class":58,"line":297},15,[56,299,267],{"class":112},[56,301,303,305],{"class":58,"line":302},16,[56,304,138],{"class":112},[56,306,141],{"class":105},[56,308,310],{"class":58,"line":309},17,[56,311,147],{"class":105},[313,314,315,321],"ul",{},[316,317,318,320],"li",{},[22,319,279],{}," is deep-merged into the generated document, so it adds to what the contracts produced instead of replacing it. Use it for declarative additions.",[316,322,323,326,327,330,331,334],{},[22,324,325],{},"extend"," runs last, on the merged document, and mutates it in place. Use it when a patch cannot express the edit — reading the generated ",[22,328,329],{},"operationId","s, or attaching ",[22,332,333],{},"security"," to specific paths.",[10,336,337],{},"Both are optional. With neither, the document is exactly what the route contracts describe.",[14,339,341],{"id":340},"generated-from-route-contracts","Generated from route contracts",[10,343,344,345,348,349,351,352,354],{},"Request schemas, response schemas, summaries, route paths, and optional operation IDs are collected from discovered endpoint definitions. When ",[22,346,347],{},"operation"," is omitted, a stable operationId is derived from the route method and path. OpenAPI-only details are layered on top through ",[22,350,279],{}," and ",[22,353,325],{}," above.",[10,356,357,358,362,363,366,367,370,371,374,375,378,379,382],{},"A ",[156,359,361],{"href":360},"\u002Fdocs\u002Fendpoints#non-json-responses","media response"," appears like any other status. Its content keys are the declared ",[22,364,365],{},"media"," — one entry per type when it declares several — and each schema is the opaque ",[22,368,369],{},"{ type: 'string', contentEncoding: 'binary' }"," unless the declaration supplies a ",[22,372,373],{},"schema"," to document the payload, or one chunk of it, in more detail. A validated status labelled with a ",[22,376,377],{},"+json"," profile is keyed by that profile instead of ",[22,380,381],{},"application\u002Fjson",".",[14,384,386],{"id":385},"nitros-own-openapi","Nitro's own OpenAPI",[10,388,389,390,393,394,397,398,351,401,404,405,408],{},"Nitro can serve an OpenAPI document of its own, behind ",[22,391,392],{},"nitro.experimental.openAPI",", at ",[22,395,396],{},"\u002F_openapi.json"," with Scalar and Swagger UI at ",[22,399,400],{},"\u002F_scalar",[22,402,403],{},"\u002F_swagger",". It is off by default, and in production only when ",[22,406,407],{},"nitro.openAPI.production"," is set.",[10,410,411,412,415,416,419],{},"It describes the same routes, but it cannot describe their contracts. Its per-route metadata comes from the ",[22,413,414],{},"defineRouteMeta()"," macro, whose argument is read at build time as JSON literals only — so a schema built from Zod, Valibot, or Effect Schema can never reach it, and a route without a hand-written literal is documented as a path, a method, and ",[22,417,418],{},"200: OK",". There is no way to feed endpoint contracts into it short of duplicating every schema by hand, which is the drift this module exists to remove.",[10,421,422,423,427],{},"So the two do not merge. Enabling both serves two documents at two routes, and the module warns about it at build time, naming both. If both are configured for the ",[424,425,426],"em",{},"same"," route the build fails instead: two handlers on one route leave which document is served up to registration order.",[10,429,430,431,382],{},"Keep Nitro's enabled only if you want its bundled UI. Nothing stops you from pointing your own Scalar or Swagger UI at ",[22,432,24],{},[14,434,436],{"id":435},"framework-generated-responses","Framework-generated responses",[10,438,439],{},"Some responses the runtime produces itself, without the handler being involved, and they are in the document because a consumer cannot detect them otherwise:",[313,441,442,448,459,469],{},[316,443,444,447],{},[22,445,446],{},"400"," once the endpoint validates any request part;",[316,449,450,453,454,458],{},[22,451,452],{},"415"," when the request body is a ",[156,455,457],{"href":456},"\u002Fdocs\u002Fendpoints#media-type-request-bodies","media-type map",";",[316,460,461,464,465,458],{},[22,462,463],{},"406"," when the endpoint ",[156,466,468],{"href":467},"\u002Fdocs\u002Fendpoints#several-representations-of-one-status","negotiates",[316,470,471,71,473,476,477,480,481,485],{},[22,472,446],{},[22,474,475],{},"409",", and ",[22,478,479],{},"422"," for an ",[156,482,484],{"href":483},"\u002Fdocs\u002Fidempotency","idempotent"," endpoint.",[10,487,488,489,492],{},"Each is derived from the contract alone, so the document lists exactly what that endpoint's configuration makes reachable. Declaring one of those statuses yourself does not hide the generated shape — the two are merged as a ",[22,490,491],{},"oneOf",", so the document keeps describing both.",[10,494,495,496,501],{},"The schemas describe the default bodies. An endpoint or application that replaces them through ",[156,497,498],{"href":158},[22,499,500],{},"onValidationError"," is describing its own shapes, and should declare those statuses in the contract.",[10,503,504,505,509,510,513,514,517,518,521,522,525],{},"Two body shapes appear here, and the rule behind them is: ",[506,507,508],"strong",{},"follow the specification where one exists, otherwise match the platform."," The idempotency statuses use ",[22,511,512],{},"application\u002Fproblem+json"," because the IETF ",[22,515,516],{},"Idempotency-Key"," draft specifies Problem Details for them. The validation, 415, and 406 bodies are ours to shape, so they use ",[22,519,520],{},"{ statusCode, statusMessage, data }"," — the same shape h3 serializes a ",[22,523,524],{},"createError"," into, which is what the rest of a Nuxt application's errors already look like. One error handler covers your endpoints and your plain routes alike.",[527,528,529],"style",{},"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 .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 .sA0OV, html code.shiki .sA0OV{--shiki-light:#6E7781;--shiki-dark:#8B949E}html pre.shiki code .swxxe, html code.shiki .swxxe{--shiki-light:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .s9HWX, html code.shiki .s9HWX{--shiki-light:#8250DF;--shiki-dark:#D2A8FF}html pre.shiki code .sVS1I, html code.shiki .sVS1I{--shiki-light:#953800;--shiki-dark:#FFA657}html pre.shiki code .saQ_J, html code.shiki .saQ_J{--shiki-light:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .sZItA, html code.shiki .sZItA{--shiki-light:#0A3069;--shiki-dark:#A5D6FF}",{"title":52,"searchDepth":91,"depth":91,"links":531},[532,533,534,535,536],{"id":16,"depth":91,"text":17},{"id":64,"depth":91,"text":65},{"id":340,"depth":91,"text":341},{"id":385,"depth":91,"text":386},{"id":435,"depth":91,"text":436},"Serve OpenAPI 3.1 from endpoint definitions.","md",{},true,"\u002Fdocs\u002Fopenapi",{"title":5,"description":537},"docs\u002Fopenapi","5n_DqQrBx1o1EeWihkogkL9BZQO7MekWk6xWRjiSpnc",{"left":546,"top":546,"width":547,"height":547,"rotate":546,"vFlip":548,"hFlip":548,"body":549},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\"\u002F>",{"left":546,"top":546,"width":547,"height":547,"rotate":546,"vFlip":548,"hFlip":548,"body":551,"hidden":540},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5c.08-1.25-.27-2.48-1-3.5c.28-1.15.28-2.35 0-3.5c0 0-1 0-3 1.5c-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5c-.39.49-.68 1.05-.85 1.65S8.93 17.38 9 18v4\"\u002F>\u003Cpath d=\"M9 18c-4.51 2-5-2-7-2\"\u002F>\u003C\u002Fg>",{"left":546,"top":546,"width":547,"height":547,"rotate":546,"vFlip":548,"hFlip":548,"body":553},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"\u002F>",1787723882318]