[{"data":1,"prerenderedAt":3179},["ShallowReactive",2],{"navigation_docs_en":3,"-en-http-chi":342,"-en-http-chi-surround":3174},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":113,"body":344,"description":3167,"extension":3168,"links":3169,"meta":3170,"navigation":3171,"path":114,"seo":3172,"stem":115,"__hash__":3173},"docs_en\u002Fen\u002F3.http\u002F3.chi.md",{"type":345,"value":346,"toc":3152},"minimark",[347,385,414,433,438,585,621,625,628,801,829,834,841,885,889,895,1502,1510,1514,1696,1768,1772,1790,2137,2140,2211,2215,2333,2352,2376,2380,2389,2853,2882,2915,2919,2931,2935,2943,3098,3101,3105,3136,3139,3148],[348,349,350,354,355,358,359,363,364,368,369,372,373,376,377,380,381,384],"p",{},[351,352,353],"code",{},"server\u002Fchix"," is the kit's chi engine: it builds a ",[351,356,357],{},"*chi.Mux"," with the kit defaults (RFC 9457 problem responses for panics, 404 and 405, OTel middleware, CORS, body limit) and runs it with the ",[360,361,362],"a",{"href":74},"shared lifecycle",". Its positioning is ",[365,366,367],"strong",{},"stdlib-first",": chi is not a framework, it is routing over ",[351,370,371],{},"net\u002Fhttp",". Every middleware is a plain ",[351,374,375],{},"func(http.Handler) http.Handler",", every handler is ",[351,378,379],{},"http.HandlerFunc","-compatible. Any net\u002Fhttp tool in the Go ecosystem (middleware libraries, ",[351,382,383],{},"httptest",", profilers) works unmodified.",[386,387,392],"pre",{"className":388,"code":389,"language":390,"meta":391,"style":391},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fgpsystem\u002Fserver\u002Fchix\"\n","go","",[351,393,394],{"__ignoreMap":391},[395,396,399,403,407,411],"span",{"class":397,"line":398},"line",1,[395,400,402],{"class":401},"s7zQu","import",[395,404,406],{"class":405},"sMK4o"," \"",[395,408,410],{"class":409},"sBMFI","github.com\u002Fgp-system\u002Fgpsystem\u002Fserver\u002Fchix",[395,412,413],{"class":405},"\"\n",[348,415,416,417,420,421,424,425,428,429,432],{},"It is the chassis of projects ",[360,418,419],{"href":305},"generated"," with ",[351,422,423],{},"--engine chi","; its ergonomics deliberately mirror ",[360,426,427],{"href":109},"fiberx",", so the ",[351,430,431],{},"main.go"," diff between the two engines stays minimal.",[434,435,437],"h2",{"id":436},"api","API",[386,439,441],{"className":388,"code":440,"language":390,"meta":391,"style":391},"func Run(ctx context.Context, cfg server.Config, register RegisterFunc, opts ...Option) error\nfunc New(cfg server.Config, opts ...Option) *chi.Mux \u002F\u002F for tests and special cases\n\ntype RegisterFunc func(r chi.Router) error\n",[351,442,443,508,552,559],{"__ignoreMap":391},[395,444,445,448,452,455,459,462,465,468,471,474,477,479,482,484,487,490,492,495,498,501,504],{"class":397,"line":398},[395,446,447],{"class":405},"func",[395,449,451],{"class":450},"s2Zo4"," Run",[395,453,454],{"class":405},"(",[395,456,458],{"class":457},"sHdIc","ctx",[395,460,461],{"class":409}," context",[395,463,464],{"class":405},".",[395,466,467],{"class":409},"Context",[395,469,470],{"class":405},",",[395,472,473],{"class":457}," cfg",[395,475,476],{"class":409}," server",[395,478,464],{"class":405},[395,480,481],{"class":409},"Config",[395,483,470],{"class":405},[395,485,486],{"class":457}," register",[395,488,489],{"class":409}," RegisterFunc",[395,491,470],{"class":405},[395,493,494],{"class":457}," opts",[395,496,497],{"class":405}," ...",[395,499,500],{"class":409},"Option",[395,502,503],{"class":405},")",[395,505,507],{"class":506},"spNyl"," error\n",[395,509,511,513,516,518,521,523,525,527,529,531,533,535,537,540,543,545,548],{"class":397,"line":510},2,[395,512,447],{"class":405},[395,514,515],{"class":450}," New",[395,517,454],{"class":405},[395,519,520],{"class":457},"cfg",[395,522,476],{"class":409},[395,524,464],{"class":405},[395,526,481],{"class":409},[395,528,470],{"class":405},[395,530,494],{"class":457},[395,532,497],{"class":405},[395,534,500],{"class":409},[395,536,503],{"class":405},[395,538,539],{"class":405}," *",[395,541,542],{"class":409},"chi",[395,544,464],{"class":405},[395,546,547],{"class":409},"Mux",[395,549,551],{"class":550},"sHwdD"," \u002F\u002F for tests and special cases\n",[395,553,555],{"class":397,"line":554},3,[395,556,558],{"emptyLinePlaceholder":557},true,"\n",[395,560,562,565,567,570,573,576,578,581,583],{"class":397,"line":561},4,[395,563,564],{"class":405},"type",[395,566,489],{"class":409},[395,568,569],{"class":405}," func(",[395,571,572],{"class":457},"r",[395,574,575],{"class":409}," chi",[395,577,464],{"class":405},[395,579,580],{"class":409},"Router",[395,582,503],{"class":405},[395,584,507],{"class":506},[348,586,587,590,591,594,595,598,599,602,603,606,607,610,611,614,615,618,619,464],{},[351,588,589],{},"Run"," blocks until exit: telemetry bootstrap → router construction → ",[351,592,593],{},"register(r)"," → ",[351,596,597],{},"http.Server"," + ",[351,600,601],{},"ListenAndServe"," → graceful shutdown on ",[351,604,605],{},"SIGINT","\u002F",[351,608,609],{},"SIGTERM",". It returns ",[351,612,613],{},"nil"," on clean shutdown. ",[351,616,617],{},"New"," returns the fully wired router without running it or bootstrapping telemetry: you pair it with ",[351,620,383],{},[434,622,624],{"id":623},"what-run-wires","What Run wires",[348,626,627],{},"The middleware chain and the router-level handlers, in this order:",[629,630,631,647],"table",{},[632,633,634],"thead",{},[635,636,637,641,644],"tr",{},[638,639,640],"th",{},"What",[638,642,643],{},"From",[638,645,646],{},"What it does",[648,649,650,674,692,707,720,737,765,785],"tbody",{},[635,651,652,656,659],{},[653,654,655],"td",{},"recoverer",[653,657,658],{},"kit",[653,660,661,662,665,666,669,670,673],{},"a panic becomes an ",[351,663,664],{},"errs"," error whose stack points at the ",[365,667,668],{},"panic site",", rendered as a 500 problem response (it re-raises ",[351,671,672],{},"http.ErrAbortHandler",", net\u002Fhttp's flow-control signal)",[635,675,676,679,681],{},[653,677,678],{},"validator injector",[653,680,658],{},[653,682,683,684,687,688,691],{},"puts the validator given via ",[351,685,686],{},"WithValidator"," into the request context, where ",[351,689,690],{},"StrictValidator(nil)"," finds it (see below)",[635,693,694,697,702],{},[653,695,696],{},"OTel middleware",[653,698,699],{},[351,700,701],{},"otelhttp",[653,703,704,705],{},"per-request server span + HTTP metrics: ",[360,706,232],{"href":233},[635,708,709,712,714],{},[653,710,711],{},"Sentry request hub",[653,713,658],{},[653,715,716,717,719],{},"an isolated Sentry hub per request, so breadcrumbs never mix across requests; no-op when ",[360,718,237],{"href":238}," is off",[635,721,722,725,730],{},[653,723,724],{},"dev logger",[653,726,727],{},[351,728,729],{},"chi\u002Fmiddleware.Logger",[653,731,732,733,736],{},"only with ",[351,734,735],{},"OTEL_DEV_MODE=true",": request log on the console",[635,738,739,742,747],{},[653,740,741],{},"CORS",[653,743,744],{},[351,745,746],{},"go-chi\u002Fcors",[653,748,749,750,753,754,757,758,598,761,764],{},"from ",[351,751,752],{},"CORS_ORIGINS","; ",[351,755,756],{},"GET\u002FPOST\u002FPUT\u002FPATCH\u002FDELETE\u002FOPTIONS",", ",[351,759,760],{},"Content-Type",[351,762,763],{},"Authorization"," headers",[635,766,767,770,775],{},[653,768,769],{},"body limit",[653,771,772],{},[351,773,774],{},"chi\u002Fmiddleware.RequestSize",[653,776,777,778,420,781,784],{},"wraps the body in ",[351,779,780],{},"http.MaxBytesReader",[351,782,783],{},"cfg.EffectiveBodyLimit()",": exceeding it yields a 413 problem",[635,786,787,796,798],{},[653,788,789,792,793],{},[351,790,791],{},"NotFound"," \u002F ",[351,794,795],{},"MethodNotAllowed",[653,797,658],{},[653,799,800],{},"404\u002F405 as problem+json instead of chi's stock plain-text errors; mounted subrouters inherit them",[348,802,803,804,809,810,812,813,757,816,757,819,757,822,825,826,828],{},"The ",[360,805,806],{"href":104},[351,807,808],{},"server.Config"," timeouts translate into the ",[351,811,597],{}," (",[351,814,815],{},"ReadHeaderTimeout",[351,817,818],{},"ReadTimeout",[351,820,821],{},"WriteTimeout",[351,823,824],{},"IdleTimeout","): ",[351,827,815],{}," is used as a separate field only by this engine; it is the Slowloris defense.",[830,831,833],"h3",{"id":832},"what-nethttp-forces-to-be-different","What net\u002Fhttp forces to be different",[348,835,836,837,840],{},"Unlike Fiber, net\u002Fhttp has ",[365,838,839],{},"no central error handler",": a handler doesn't \"return\" an error to the framework, it writes the response itself. The kit bridges this at three points so the on-the-wire behavior stays identical across the two engines:",[842,843,844,865,871],"ul",{},[845,846,847,848,851,852,812,858,757,861,864],"li",{},"the ",[365,849,850],{},"generated server code's error hooks"," are wired to the ",[360,853,854,857],{"href":119},[351,855,856],{},"httperr"," net\u002Fhttp writers",[351,859,860],{},"WriteError",[351,862,863],{},"WriteBadRequest","): an error returned from your handler goes through the same mapping as on Fiber;",[845,866,847,867,870],{},[365,868,869],{},"router-level cases"," (panic, 404, 405, body limit) are rendered as problems per the table above;",[845,872,873,876,877,880,881,884],{},[365,874,875],{},"request validation"," does not run at bind time (the strict server decodes with ",[351,878,879],{},"json.Decoder",") but in the ",[351,882,883],{},"chix.StrictValidator"," strict middleware (see below).",[434,886,888],{"id":887},"the-shop-entry-point","The shop entry point",[348,890,803,891,894],{},[360,892,893],{"href":41},"sample app's"," HTTP binary on chi, every line of which was written by the generator:",[386,896,899],{"className":388,"code":897,"filename":898,"language":390,"meta":391,"style":391},"package main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com\u002Fgo-chi\u002Fchi\u002Fv5\"\n\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fpg\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fenvconf\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fevents\u002Foutbox\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fserver\u002Fchix\"\n\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\"\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig\"\n)\n\nfunc main() {\n    cfg := envconf.MustLoad[config.Config]()\n    ctx := context.Background()\n    pool := pg.MustNewPool(ctx, cfg.DB)\n\n    err := chix.Run(ctx, cfg.Server, func(r chi.Router) error {\n        api := chi.NewRouter()\n        deps := shop.Dependencies{\n            DB:         pg.NewDB(pool),\n            Transactor: pg.NewTransactor(pool),\n            Dispatcher: outbox.NewDispatcher(outbox.NewStore(pg.NewDB(pool))),\n        }\n        shop.RegisterApi(api, deps)\n        shop.RegisterAdmin(api, deps)\n        r.Mount(\"\u002Fapi\u002Fv1\", api)\n        return nil\n    }, chix.WithCloser(\"pgxpool\", func(context.Context) error {\n        pool.Close()\n        return nil\n    }))\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n","cmd\u002Fshop\u002Fmain.go",[351,900,901,909,913,920,930,940,945,955,960,970,980,990,999,1004,1014,1024,1030,1035,1049,1080,1098,1129,1134,1181,1198,1217,1242,1263,1305,1311,1333,1353,1382,1391,1429,1442,1449,1455,1472,1490,1496],{"__ignoreMap":391},[395,902,903,906],{"class":397,"line":398},[395,904,905],{"class":405},"package",[395,907,908],{"class":409}," main\n",[395,910,911],{"class":397,"line":510},[395,912,558],{"emptyLinePlaceholder":557},[395,914,915,917],{"class":397,"line":554},[395,916,402],{"class":401},[395,918,919],{"class":405}," (\n",[395,921,922,925,928],{"class":397,"line":561},[395,923,924],{"class":405},"    \"",[395,926,927],{"class":409},"context",[395,929,413],{"class":405},[395,931,933,935,938],{"class":397,"line":932},5,[395,934,924],{"class":405},[395,936,937],{"class":409},"log",[395,939,413],{"class":405},[395,941,943],{"class":397,"line":942},6,[395,944,558],{"emptyLinePlaceholder":557},[395,946,948,950,953],{"class":397,"line":947},7,[395,949,924],{"class":405},[395,951,952],{"class":409},"github.com\u002Fgo-chi\u002Fchi\u002Fv5",[395,954,413],{"class":405},[395,956,958],{"class":397,"line":957},8,[395,959,558],{"emptyLinePlaceholder":557},[395,961,963,965,968],{"class":397,"line":962},9,[395,964,924],{"class":405},[395,966,967],{"class":409},"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fpg",[395,969,413],{"class":405},[395,971,973,975,978],{"class":397,"line":972},10,[395,974,924],{"class":405},[395,976,977],{"class":409},"github.com\u002Fgp-system\u002Fgpsystem\u002Fenvconf",[395,979,413],{"class":405},[395,981,983,985,988],{"class":397,"line":982},11,[395,984,924],{"class":405},[395,986,987],{"class":409},"github.com\u002Fgp-system\u002Fgpsystem\u002Fevents\u002Foutbox",[395,989,413],{"class":405},[395,991,993,995,997],{"class":397,"line":992},12,[395,994,924],{"class":405},[395,996,410],{"class":409},[395,998,413],{"class":405},[395,1000,1002],{"class":397,"line":1001},13,[395,1003,558],{"emptyLinePlaceholder":557},[395,1005,1007,1009,1012],{"class":397,"line":1006},14,[395,1008,924],{"class":405},[395,1010,1011],{"class":409},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop",[395,1013,413],{"class":405},[395,1015,1017,1019,1022],{"class":397,"line":1016},15,[395,1018,924],{"class":405},[395,1020,1021],{"class":409},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig",[395,1023,413],{"class":405},[395,1025,1027],{"class":397,"line":1026},16,[395,1028,1029],{"class":405},")\n",[395,1031,1033],{"class":397,"line":1032},17,[395,1034,558],{"emptyLinePlaceholder":557},[395,1036,1038,1040,1043,1046],{"class":397,"line":1037},18,[395,1039,447],{"class":405},[395,1041,1042],{"class":450}," main",[395,1044,1045],{"class":405},"()",[395,1047,1048],{"class":405}," {\n",[395,1050,1052,1056,1059,1062,1064,1067,1070,1073,1075,1077],{"class":397,"line":1051},19,[395,1053,1055],{"class":1054},"sTEyZ","    cfg ",[395,1057,1058],{"class":405},":=",[395,1060,1061],{"class":1054}," envconf",[395,1063,464],{"class":405},[395,1065,1066],{"class":450},"MustLoad",[395,1068,1069],{"class":405},"[",[395,1071,1072],{"class":409},"config",[395,1074,464],{"class":405},[395,1076,481],{"class":409},[395,1078,1079],{"class":405},"]()\n",[395,1081,1083,1086,1088,1090,1092,1095],{"class":397,"line":1082},20,[395,1084,1085],{"class":1054},"    ctx ",[395,1087,1058],{"class":405},[395,1089,461],{"class":1054},[395,1091,464],{"class":405},[395,1093,1094],{"class":450},"Background",[395,1096,1097],{"class":405},"()\n",[395,1099,1101,1104,1106,1109,1111,1114,1116,1118,1120,1122,1124,1127],{"class":397,"line":1100},21,[395,1102,1103],{"class":1054},"    pool ",[395,1105,1058],{"class":405},[395,1107,1108],{"class":1054}," pg",[395,1110,464],{"class":405},[395,1112,1113],{"class":450},"MustNewPool",[395,1115,454],{"class":405},[395,1117,458],{"class":1054},[395,1119,470],{"class":405},[395,1121,473],{"class":1054},[395,1123,464],{"class":405},[395,1125,1126],{"class":1054},"DB",[395,1128,1029],{"class":405},[395,1130,1132],{"class":397,"line":1131},22,[395,1133,558],{"emptyLinePlaceholder":557},[395,1135,1137,1140,1142,1145,1147,1149,1151,1153,1155,1157,1159,1162,1164,1166,1168,1170,1172,1174,1176,1179],{"class":397,"line":1136},23,[395,1138,1139],{"class":1054},"    err ",[395,1141,1058],{"class":405},[395,1143,1144],{"class":1054}," chix",[395,1146,464],{"class":405},[395,1148,589],{"class":450},[395,1150,454],{"class":405},[395,1152,458],{"class":1054},[395,1154,470],{"class":405},[395,1156,473],{"class":1054},[395,1158,464],{"class":405},[395,1160,1161],{"class":1054},"Server",[395,1163,470],{"class":405},[395,1165,569],{"class":405},[395,1167,572],{"class":457},[395,1169,575],{"class":409},[395,1171,464],{"class":405},[395,1173,580],{"class":409},[395,1175,503],{"class":405},[395,1177,1178],{"class":506}," error",[395,1180,1048],{"class":405},[395,1182,1184,1187,1189,1191,1193,1196],{"class":397,"line":1183},24,[395,1185,1186],{"class":1054},"        api ",[395,1188,1058],{"class":405},[395,1190,575],{"class":1054},[395,1192,464],{"class":405},[395,1194,1195],{"class":450},"NewRouter",[395,1197,1097],{"class":405},[395,1199,1201,1204,1206,1209,1211,1214],{"class":397,"line":1200},25,[395,1202,1203],{"class":1054},"        deps ",[395,1205,1058],{"class":405},[395,1207,1208],{"class":409}," shop",[395,1210,464],{"class":405},[395,1212,1213],{"class":409},"Dependencies",[395,1215,1216],{"class":405},"{\n",[395,1218,1220,1223,1226,1229,1231,1234,1236,1239],{"class":397,"line":1219},26,[395,1221,1222],{"class":1054},"            DB",[395,1224,1225],{"class":405},":",[395,1227,1228],{"class":1054},"         pg",[395,1230,464],{"class":405},[395,1232,1233],{"class":450},"NewDB",[395,1235,454],{"class":405},[395,1237,1238],{"class":1054},"pool",[395,1240,1241],{"class":405},"),\n",[395,1243,1245,1248,1250,1252,1254,1257,1259,1261],{"class":397,"line":1244},27,[395,1246,1247],{"class":1054},"            Transactor",[395,1249,1225],{"class":405},[395,1251,1108],{"class":1054},[395,1253,464],{"class":405},[395,1255,1256],{"class":450},"NewTransactor",[395,1258,454],{"class":405},[395,1260,1238],{"class":1054},[395,1262,1241],{"class":405},[395,1264,1266,1269,1271,1274,1276,1279,1281,1284,1286,1289,1291,1294,1296,1298,1300,1302],{"class":397,"line":1265},28,[395,1267,1268],{"class":1054},"            Dispatcher",[395,1270,1225],{"class":405},[395,1272,1273],{"class":1054}," outbox",[395,1275,464],{"class":405},[395,1277,1278],{"class":450},"NewDispatcher",[395,1280,454],{"class":405},[395,1282,1283],{"class":1054},"outbox",[395,1285,464],{"class":405},[395,1287,1288],{"class":450},"NewStore",[395,1290,454],{"class":405},[395,1292,1293],{"class":1054},"pg",[395,1295,464],{"class":405},[395,1297,1233],{"class":450},[395,1299,454],{"class":405},[395,1301,1238],{"class":1054},[395,1303,1304],{"class":405},"))),\n",[395,1306,1308],{"class":397,"line":1307},29,[395,1309,1310],{"class":405},"        }\n",[395,1312,1314,1317,1319,1322,1324,1326,1328,1331],{"class":397,"line":1313},30,[395,1315,1316],{"class":1054},"        shop",[395,1318,464],{"class":405},[395,1320,1321],{"class":450},"RegisterApi",[395,1323,454],{"class":405},[395,1325,436],{"class":1054},[395,1327,470],{"class":405},[395,1329,1330],{"class":1054}," deps",[395,1332,1029],{"class":405},[395,1334,1336,1338,1340,1343,1345,1347,1349,1351],{"class":397,"line":1335},31,[395,1337,1316],{"class":1054},[395,1339,464],{"class":405},[395,1341,1342],{"class":450},"RegisterAdmin",[395,1344,454],{"class":405},[395,1346,436],{"class":1054},[395,1348,470],{"class":405},[395,1350,1330],{"class":1054},[395,1352,1029],{"class":405},[395,1354,1356,1359,1361,1364,1366,1369,1373,1375,1377,1380],{"class":397,"line":1355},32,[395,1357,1358],{"class":1054},"        r",[395,1360,464],{"class":405},[395,1362,1363],{"class":450},"Mount",[395,1365,454],{"class":405},[395,1367,1368],{"class":405},"\"",[395,1370,1372],{"class":1371},"sfazB","\u002Fapi\u002Fv1",[395,1374,1368],{"class":405},[395,1376,470],{"class":405},[395,1378,1379],{"class":1054}," api",[395,1381,1029],{"class":405},[395,1383,1385,1388],{"class":397,"line":1384},33,[395,1386,1387],{"class":401},"        return",[395,1389,1390],{"class":405}," nil\n",[395,1392,1394,1397,1399,1401,1404,1406,1408,1411,1413,1415,1417,1419,1421,1423,1425,1427],{"class":397,"line":1393},34,[395,1395,1396],{"class":405},"    },",[395,1398,1144],{"class":1054},[395,1400,464],{"class":405},[395,1402,1403],{"class":450},"WithCloser",[395,1405,454],{"class":405},[395,1407,1368],{"class":405},[395,1409,1410],{"class":1371},"pgxpool",[395,1412,1368],{"class":405},[395,1414,470],{"class":405},[395,1416,569],{"class":405},[395,1418,927],{"class":409},[395,1420,464],{"class":405},[395,1422,467],{"class":409},[395,1424,503],{"class":405},[395,1426,1178],{"class":506},[395,1428,1048],{"class":405},[395,1430,1432,1435,1437,1440],{"class":397,"line":1431},35,[395,1433,1434],{"class":1054},"        pool",[395,1436,464],{"class":405},[395,1438,1439],{"class":450},"Close",[395,1441,1097],{"class":405},[395,1443,1445,1447],{"class":397,"line":1444},36,[395,1446,1387],{"class":401},[395,1448,1390],{"class":405},[395,1450,1452],{"class":397,"line":1451},37,[395,1453,1454],{"class":405},"    }))\n",[395,1456,1458,1461,1464,1467,1470],{"class":397,"line":1457},38,[395,1459,1460],{"class":401},"    if",[395,1462,1463],{"class":1054}," err ",[395,1465,1466],{"class":405},"!=",[395,1468,1469],{"class":405}," nil",[395,1471,1048],{"class":405},[395,1473,1475,1478,1480,1483,1485,1488],{"class":397,"line":1474},39,[395,1476,1477],{"class":1054},"        log",[395,1479,464],{"class":405},[395,1481,1482],{"class":450},"Fatal",[395,1484,454],{"class":405},[395,1486,1487],{"class":1054},"err",[395,1489,1029],{"class":405},[395,1491,1493],{"class":397,"line":1492},40,[395,1494,1495],{"class":405},"    }\n",[395,1497,1499],{"class":397,"line":1498},41,[395,1500,1501],{"class":405},"}\n",[348,1503,1504,1505,1507,1508,464],{},"The only structural difference from the Fiber variant: in chi, prefixing happens with ",[351,1506,1363],{},": the surfaces register on a fresh subrouter that gets mounted under ",[351,1509,1372],{},[434,1511,1513],{"id":1512},"options","Options",[386,1515,1517],{"className":388,"code":1516,"language":390,"meta":391,"style":391},"chix.WithCloser(\"pgxpool\", func(ctx context.Context) error { pool.Close(); return nil })\nchix.WithMiddleware(myRateLimiter, myRequestID)          \u002F\u002F ...func(http.Handler) http.Handler\nchix.WithHTTPServer(func(s *http.Server) { s.MaxHeaderBytes = 1 \u003C\u003C 16 })\nchix.WithValidator(validate.New(validate.WithRegister(registerCustomTags)))\nchix.WithoutTelemetry()                                  \u002F\u002F tests \u002F external OTel setup\n",[351,1518,1519,1573,1597,1648,1682],{"__ignoreMap":391},[395,1520,1521,1524,1526,1528,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548,1550,1552,1555,1558,1560,1562,1565,1568,1570],{"class":397,"line":398},[395,1522,1523],{"class":1054},"chix",[395,1525,464],{"class":405},[395,1527,1403],{"class":450},[395,1529,454],{"class":405},[395,1531,1368],{"class":405},[395,1533,1410],{"class":1371},[395,1535,1368],{"class":405},[395,1537,470],{"class":405},[395,1539,569],{"class":405},[395,1541,458],{"class":457},[395,1543,461],{"class":409},[395,1545,464],{"class":405},[395,1547,467],{"class":409},[395,1549,503],{"class":405},[395,1551,1178],{"class":506},[395,1553,1554],{"class":405}," {",[395,1556,1557],{"class":1054}," pool",[395,1559,464],{"class":405},[395,1561,1439],{"class":450},[395,1563,1564],{"class":405},"();",[395,1566,1567],{"class":401}," return",[395,1569,1469],{"class":405},[395,1571,1572],{"class":405}," })\n",[395,1574,1575,1577,1579,1582,1584,1587,1589,1592,1594],{"class":397,"line":510},[395,1576,1523],{"class":1054},[395,1578,464],{"class":405},[395,1580,1581],{"class":450},"WithMiddleware",[395,1583,454],{"class":405},[395,1585,1586],{"class":1054},"myRateLimiter",[395,1588,470],{"class":405},[395,1590,1591],{"class":1054}," myRequestID",[395,1593,503],{"class":405},[395,1595,1596],{"class":550},"          \u002F\u002F ...func(http.Handler) http.Handler\n",[395,1598,1599,1601,1603,1606,1609,1612,1614,1617,1619,1621,1623,1625,1628,1630,1633,1636,1640,1643,1646],{"class":397,"line":554},[395,1600,1523],{"class":1054},[395,1602,464],{"class":405},[395,1604,1605],{"class":450},"WithHTTPServer",[395,1607,1608],{"class":405},"(func(",[395,1610,1611],{"class":457},"s",[395,1613,539],{"class":405},[395,1615,1616],{"class":409},"http",[395,1618,464],{"class":405},[395,1620,1161],{"class":409},[395,1622,503],{"class":405},[395,1624,1554],{"class":405},[395,1626,1627],{"class":1054}," s",[395,1629,464],{"class":405},[395,1631,1632],{"class":1054},"MaxHeaderBytes ",[395,1634,1635],{"class":405},"=",[395,1637,1639],{"class":1638},"sbssI"," 1",[395,1641,1642],{"class":405}," \u003C\u003C",[395,1644,1645],{"class":1638}," 16",[395,1647,1572],{"class":405},[395,1649,1650,1652,1654,1656,1658,1661,1663,1665,1667,1669,1671,1674,1676,1679],{"class":397,"line":561},[395,1651,1523],{"class":1054},[395,1653,464],{"class":405},[395,1655,686],{"class":450},[395,1657,454],{"class":405},[395,1659,1660],{"class":1054},"validate",[395,1662,464],{"class":405},[395,1664,617],{"class":450},[395,1666,454],{"class":405},[395,1668,1660],{"class":1054},[395,1670,464],{"class":405},[395,1672,1673],{"class":450},"WithRegister",[395,1675,454],{"class":405},[395,1677,1678],{"class":1054},"registerCustomTags",[395,1680,1681],{"class":405},")))\n",[395,1683,1684,1686,1688,1691,1693],{"class":397,"line":932},[395,1685,1523],{"class":1054},[395,1687,464],{"class":405},[395,1689,1690],{"class":450},"WithoutTelemetry",[395,1692,1045],{"class":405},[395,1694,1695],{"class":550},"                                  \u002F\u002F tests \u002F external OTel setup\n",[842,1697,1698,1710,1722,1741,1756],{},[845,1699,1700,1705,1706,1709],{},[365,1701,1702],{},[351,1703,1704],{},"WithCloser(name, fn)",": cleanup during graceful shutdown, after the server has stopped accepting requests. Closers run in ",[365,1707,1708],{},"LIFO"," order; telemetry flushes last, so a span emitted in a closer still gets exported.",[845,1711,1712,1717,1718,1721],{},[365,1713,1714],{},[351,1715,1716],{},"WithMiddleware(...func(http.Handler) http.Handler)",": router-level middleware after the kit defaults. The standard net\u002Fhttp middleware shape: any ecosystem middleware fits here. Anything needed by a single surface only should go on that surface's ",[351,1719,1720],{},"Route"," instead (see below).",[845,1723,1724,1729,1730,1732,1733,1736,1737,1740],{},[365,1725,1726],{},[351,1727,1728],{},"WithHTTPServer(func(*http.Server))",": escape hatch for ",[351,1731,597],{}," fields the kit does not expose (TLS, ",[351,1734,1735],{},"MaxHeaderBytes",", ...); the counterpart of ",[351,1738,1739],{},"fiberx.WithConfig",". You may also override the kit-set fields: your mutation runs last.",[845,1742,1743,1748,1749,753,1753,1755],{},[365,1744,1745],{},[351,1746,1747],{},"WithValidator(*validate.Validator)",": the application-wide request validator, typically one carrying ",[360,1750,1752],{"href":1751},"\u002Fen\u002Fhttp\u002Fvalidation#custom-tags","custom validation tags",[351,1754,690],{}," middlewares resolve it at request time.",[845,1757,1758,1763,1764,1767],{},[365,1759,1760],{},[351,1761,1762],{},"WithoutTelemetry()",": skips ",[351,1765,1766],{},"telemetry.Setup"," and the OTel middleware; for tests, or when the process configures telemetry itself.",[434,1769,1771],{"id":1770},"the-generated-wiring-registerapi","The generated wiring: RegisterApi",[348,1773,1774,1778,1779,1782,1783,1786,1787,1789],{},[360,1775,1776],{"href":315},[351,1777,314],{}," splices one ",[351,1780,1781],{},"Register\u003CSurface>"," function per surface into the module's ",[351,1784,1785],{},"register.go",". For the shop's ",[351,1788,436],{}," surface this is generated verbatim:",[386,1791,1794],{"className":388,"code":1792,"filename":1793,"language":390,"meta":391,"style":391},"func RegisterApi(router chi.Router, deps Dependencies) {\n    apiSvc := apiservice.New()\n    apiHandler := apihttp.New(apiSvc)\n    apiPolicies := apipolicy.New()\n    router.Route(\"\u002Fshop\", func(r chi.Router) {\n        shopapigen.HandlerWithOptions(shopapigen.NewStrictHandlerWithOptions(\n            apiHandler,\n            []shopapigen.StrictMiddlewareFunc{\n                chix.StrictValidator[shopapigen.StrictHandlerFunc](nil),\n                \u002F\u002F Last = outermost: authorization runs before validation.\n                kitpolicy.HTTPEnforcer[shopapigen.StrictHandlerFunc](apiPolicies, shopapigen.PermissionByOperation, shopapigen.PolicyByOperation),\n            },\n            shopapigen.StrictHTTPServerOptions{\n                RequestErrorHandlerFunc:  httperr.WriteBadRequest,\n                ResponseErrorHandlerFunc: httperr.WriteError,\n            },\n        ), shopapigen.ChiServerOptions{\n            BaseRouter:       r,\n            ErrorHandlerFunc: httperr.WriteBadRequest,\n        })\n    })\n}\n","internal\u002Fmodules\u002Fshop\u002Fregister.go",[351,1795,1796,1825,1841,1862,1878,1912,1935,1943,1957,1979,1984,2029,2034,2046,2062,2078,2082,2096,2108,2123,2128,2133],{"__ignoreMap":391},[395,1797,1798,1800,1803,1805,1808,1810,1812,1814,1816,1818,1821,1823],{"class":397,"line":398},[395,1799,447],{"class":405},[395,1801,1802],{"class":450}," RegisterApi",[395,1804,454],{"class":405},[395,1806,1807],{"class":457},"router",[395,1809,575],{"class":409},[395,1811,464],{"class":405},[395,1813,580],{"class":409},[395,1815,470],{"class":405},[395,1817,1330],{"class":457},[395,1819,1820],{"class":409}," Dependencies",[395,1822,503],{"class":405},[395,1824,1048],{"class":405},[395,1826,1827,1830,1832,1835,1837,1839],{"class":397,"line":510},[395,1828,1829],{"class":1054},"    apiSvc ",[395,1831,1058],{"class":405},[395,1833,1834],{"class":1054}," apiservice",[395,1836,464],{"class":405},[395,1838,617],{"class":450},[395,1840,1097],{"class":405},[395,1842,1843,1846,1848,1851,1853,1855,1857,1860],{"class":397,"line":554},[395,1844,1845],{"class":1054},"    apiHandler ",[395,1847,1058],{"class":405},[395,1849,1850],{"class":1054}," apihttp",[395,1852,464],{"class":405},[395,1854,617],{"class":450},[395,1856,454],{"class":405},[395,1858,1859],{"class":1054},"apiSvc",[395,1861,1029],{"class":405},[395,1863,1864,1867,1869,1872,1874,1876],{"class":397,"line":561},[395,1865,1866],{"class":1054},"    apiPolicies ",[395,1868,1058],{"class":405},[395,1870,1871],{"class":1054}," apipolicy",[395,1873,464],{"class":405},[395,1875,617],{"class":450},[395,1877,1097],{"class":405},[395,1879,1880,1883,1885,1887,1889,1891,1894,1896,1898,1900,1902,1904,1906,1908,1910],{"class":397,"line":932},[395,1881,1882],{"class":1054},"    router",[395,1884,464],{"class":405},[395,1886,1720],{"class":450},[395,1888,454],{"class":405},[395,1890,1368],{"class":405},[395,1892,1893],{"class":1371},"\u002Fshop",[395,1895,1368],{"class":405},[395,1897,470],{"class":405},[395,1899,569],{"class":405},[395,1901,572],{"class":457},[395,1903,575],{"class":409},[395,1905,464],{"class":405},[395,1907,580],{"class":409},[395,1909,503],{"class":405},[395,1911,1048],{"class":405},[395,1913,1914,1917,1919,1922,1924,1927,1929,1932],{"class":397,"line":942},[395,1915,1916],{"class":1054},"        shopapigen",[395,1918,464],{"class":405},[395,1920,1921],{"class":450},"HandlerWithOptions",[395,1923,454],{"class":405},[395,1925,1926],{"class":1054},"shopapigen",[395,1928,464],{"class":405},[395,1930,1931],{"class":450},"NewStrictHandlerWithOptions",[395,1933,1934],{"class":405},"(\n",[395,1936,1937,1940],{"class":397,"line":947},[395,1938,1939],{"class":1054},"            apiHandler",[395,1941,1942],{"class":405},",\n",[395,1944,1945,1948,1950,1952,1955],{"class":397,"line":957},[395,1946,1947],{"class":405},"            []",[395,1949,1926],{"class":409},[395,1951,464],{"class":405},[395,1953,1954],{"class":409},"StrictMiddlewareFunc",[395,1956,1216],{"class":405},[395,1958,1959,1962,1964,1967,1969,1971,1973,1976],{"class":397,"line":962},[395,1960,1961],{"class":1054},"                chix",[395,1963,464],{"class":405},[395,1965,1966],{"class":450},"StrictValidator",[395,1968,1069],{"class":405},[395,1970,1926],{"class":409},[395,1972,464],{"class":405},[395,1974,1975],{"class":409},"StrictHandlerFunc",[395,1977,1978],{"class":405},"](nil),\n",[395,1980,1981],{"class":397,"line":972},[395,1982,1983],{"class":550},"                \u002F\u002F Last = outermost: authorization runs before validation.\n",[395,1985,1986,1989,1991,1994,1996,1998,2000,2002,2005,2008,2010,2013,2015,2018,2020,2022,2024,2027],{"class":397,"line":982},[395,1987,1988],{"class":1054},"                kitpolicy",[395,1990,464],{"class":405},[395,1992,1993],{"class":450},"HTTPEnforcer",[395,1995,1069],{"class":405},[395,1997,1926],{"class":409},[395,1999,464],{"class":405},[395,2001,1975],{"class":409},[395,2003,2004],{"class":405},"](",[395,2006,2007],{"class":1054},"apiPolicies",[395,2009,470],{"class":405},[395,2011,2012],{"class":1054}," shopapigen",[395,2014,464],{"class":405},[395,2016,2017],{"class":1054},"PermissionByOperation",[395,2019,470],{"class":405},[395,2021,2012],{"class":1054},[395,2023,464],{"class":405},[395,2025,2026],{"class":1054},"PolicyByOperation",[395,2028,1241],{"class":405},[395,2030,2031],{"class":397,"line":992},[395,2032,2033],{"class":405},"            },\n",[395,2035,2036,2039,2041,2044],{"class":397,"line":1001},[395,2037,2038],{"class":409},"            shopapigen",[395,2040,464],{"class":405},[395,2042,2043],{"class":409},"StrictHTTPServerOptions",[395,2045,1216],{"class":405},[395,2047,2048,2051,2053,2056,2058,2060],{"class":397,"line":1006},[395,2049,2050],{"class":1054},"                RequestErrorHandlerFunc",[395,2052,1225],{"class":405},[395,2054,2055],{"class":1054},"  httperr",[395,2057,464],{"class":405},[395,2059,863],{"class":1054},[395,2061,1942],{"class":405},[395,2063,2064,2067,2069,2072,2074,2076],{"class":397,"line":1016},[395,2065,2066],{"class":1054},"                ResponseErrorHandlerFunc",[395,2068,1225],{"class":405},[395,2070,2071],{"class":1054}," httperr",[395,2073,464],{"class":405},[395,2075,860],{"class":1054},[395,2077,1942],{"class":405},[395,2079,2080],{"class":397,"line":1026},[395,2081,2033],{"class":405},[395,2083,2084,2087,2089,2091,2094],{"class":397,"line":1032},[395,2085,2086],{"class":405},"        ),",[395,2088,2012],{"class":409},[395,2090,464],{"class":405},[395,2092,2093],{"class":409},"ChiServerOptions",[395,2095,1216],{"class":405},[395,2097,2098,2101,2103,2106],{"class":397,"line":1037},[395,2099,2100],{"class":1054},"            BaseRouter",[395,2102,1225],{"class":405},[395,2104,2105],{"class":1054},"       r",[395,2107,1942],{"class":405},[395,2109,2110,2113,2115,2117,2119,2121],{"class":397,"line":1051},[395,2111,2112],{"class":1054},"            ErrorHandlerFunc",[395,2114,1225],{"class":405},[395,2116,2071],{"class":1054},[395,2118,464],{"class":405},[395,2120,863],{"class":1054},[395,2122,1942],{"class":405},[395,2124,2125],{"class":397,"line":1082},[395,2126,2127],{"class":405},"        })\n",[395,2129,2130],{"class":397,"line":1100},[395,2131,2132],{"class":405},"    })\n",[395,2134,2135],{"class":397,"line":1131},[395,2136,1501],{"class":405},[348,2138,2139],{},"Line by line:",[2141,2142,2143,2149,2168,2197],"ol",{},[845,2144,2145,2148],{},[365,2146,2147],{},"Service → handler → policy registry",": the same explicit construction as on Fiber.",[845,2150,2151,2156,2157,2159,2160,2163,2164,2167],{},[365,2152,2153],{},[351,2154,2155],{},"router.Route(\"\u002Fshop\", ...)",": the ",[351,2158,436],{}," surface sits at the module root (",[351,2161,2162],{},"\u002Fapi\u002Fv1\u002Fshop\u002F...","); every other surface under its own prefix (",[351,2165,2166],{},"\u002Fadmin\u002Fshop",").",[845,2169,2170,2156,2174,2176,2177,757,2180,2183,2184,2186,2187,2190,2191,2193,2194,464],{},[365,2171,2172],{},[351,2173,1931],{},[360,2175,419],{"href":88}," strict server, plus the error hooks: request-decoding failures (",[351,2178,2179],{},"RequestErrorHandlerFunc",[351,2181,2182],{},"ErrorHandlerFunc",") render as 400 via ",[351,2185,863],{},", your handler's errors (",[351,2188,2189],{},"ResponseErrorHandlerFunc",") via ",[351,2192,860],{}," through the ",[360,2195,2196],{"href":119},"full mapping",[845,2198,2199,2206,2207,2210],{},[365,2200,2201,598,2203],{},[351,2202,883],{},[351,2204,2205],{},"kitpolicy.HTTPEnforcer",": validation and ",[360,2208,2209],{"href":214},"policy enforcement"," as strict middleware; the last element of the slice wraps outermost, so the enforcer runs before the validator.",[830,2212,2214],{"id":2213},"strictvalidator-validation-before-the-handler","StrictValidator: validation before the handler",[386,2216,2218],{"className":388,"code":2217,"language":390,"meta":391,"style":391},"func StrictValidator[H ~func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error)](\n    v *validate.Validator,\n) func(f H, operationID string) H\n",[351,2219,2220,2292,2308],{"__ignoreMap":391},[395,2221,2222,2224,2227,2229,2232,2235,2237,2239,2241,2243,2245,2248,2251,2253,2256,2258,2261,2263,2265,2267,2270,2272,2275,2278,2280,2282,2285,2287,2289],{"class":397,"line":398},[395,2223,447],{"class":405},[395,2225,2226],{"class":450}," StrictValidator",[395,2228,1069],{"class":405},[395,2230,2231],{"class":457},"H",[395,2233,2234],{"class":405}," ~func(",[395,2236,458],{"class":457},[395,2238,461],{"class":409},[395,2240,464],{"class":405},[395,2242,467],{"class":409},[395,2244,470],{"class":405},[395,2246,2247],{"class":457}," w",[395,2249,2250],{"class":409}," http",[395,2252,464],{"class":405},[395,2254,2255],{"class":409},"ResponseWriter",[395,2257,470],{"class":405},[395,2259,2260],{"class":457}," r",[395,2262,539],{"class":405},[395,2264,1616],{"class":409},[395,2266,464],{"class":405},[395,2268,2269],{"class":409},"Request",[395,2271,470],{"class":405},[395,2273,2274],{"class":457}," request",[395,2276,2277],{"class":409}," any",[395,2279,503],{"class":405},[395,2281,812],{"class":405},[395,2283,2284],{"class":409},"any",[395,2286,470],{"class":405},[395,2288,1178],{"class":506},[395,2290,2291],{"class":405},")](\n",[395,2293,2294,2297,2299,2301,2303,2306],{"class":397,"line":510},[395,2295,2296],{"class":457},"    v",[395,2298,539],{"class":405},[395,2300,1660],{"class":409},[395,2302,464],{"class":405},[395,2304,2305],{"class":409},"Validator",[395,2307,1942],{"class":405},[395,2309,2310,2312,2314,2317,2320,2322,2325,2328,2330],{"class":397,"line":554},[395,2311,503],{"class":405},[395,2313,569],{"class":405},[395,2315,2316],{"class":457},"f",[395,2318,2319],{"class":409}," H",[395,2321,470],{"class":405},[395,2323,2324],{"class":457}," operationID",[395,2326,2327],{"class":506}," string",[395,2329,503],{"class":405},[395,2331,2332],{"class":409}," H\n",[348,2334,2335,2336,2339,2340,2342,2343,2345,2346,2348,2349,2351],{},"On Fiber, validation runs at bind time (",[351,2337,2338],{},"StructValidator","); the net\u002Fhttp strict server instead decodes with ",[351,2341,879],{},", without validation. ",[351,2344,1966],{}," brings the two engines to parity: it validates the fully bound request object (parameters + body) with the kit validator before your handler runs; a failure surfaces as a 422 problem through ",[351,2347,2189],{},". The type parameter exists because oapi-codegen defines ",[351,2350,1975],{}," per generated package.",[348,2353,2354,2355,2358,2359,2362,2363,2366,2367,2369,2370,2372,2373,2375],{},"The validator is resolved per request, in this order: the explicit ",[351,2356,2357],{},"v"," argument → the one set via ",[351,2360,2361],{},"chix.WithValidator"," (from the context) → ",[351,2364,2365],{},"validate.New()",". That's why the generated code passes ",[351,2368,613],{},": a single ",[351,2371,686],{}," option in ",[351,2374,431],{}," applies application-wide, and the generated files need no edits.",[434,2377,2379],{"id":2378},"surface-level-middleware-the-admin-jwt","Surface-level middleware: the admin JWT",[348,2381,2382,2383,2385,2386,2388],{},"Router-level middleware comes from ",[351,2384,1581],{},"; anything scoped to one surface (like the JWT protection of the shop's admin surface) you wire inside the surface's ",[351,2387,1720],{}," block with the net\u002Fhttp auth middleware pair:",[386,2390,2392],{"className":388,"code":2391,"filename":1793,"language":390,"meta":391,"style":391},"import (\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fauth\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Frbac\"\n    \u002F\u002F ...\n)\n\ntype Dependencies struct {\n    DB         *pg.DB\n    Transactor dbx.Transactor\n    Auth       auth.Config \u002F\u002F JWT_SECRET, JWT_ISSUER, ..., from the platform config\n    \u002F\u002F gpsystem:dependencies\n}\n\nfunc RegisterAdmin(router chi.Router, deps Dependencies) {\n    adminSvc := adminservice.New()\n    adminHandler := adminhttp.New(adminSvc)\n    adminPolicies := adminpolicy.New()\n    router.Route(\"\u002Fadmin\u002Fshop\", func(r chi.Router) {\n        r.Use(auth.MiddlewareHTTP(deps.Auth), rbac.RequireRoleHTTP(\"admin\"))\n        shopadmingen.HandlerWithOptions(shopadmingen.NewStrictHandlerWithOptions(\n            adminHandler,\n            []shopadmingen.StrictMiddlewareFunc{\n                chix.StrictValidator[shopadmingen.StrictHandlerFunc](nil),\n                kitpolicy.HTTPEnforcer[shopadmingen.StrictHandlerFunc](adminPolicies, shopadmingen.PermissionByOperation, shopadmingen.PolicyByOperation),\n            },\n            shopadmingen.StrictHTTPServerOptions{\n                RequestErrorHandlerFunc:  httperr.WriteBadRequest,\n                ResponseErrorHandlerFunc: httperr.WriteError,\n            },\n        ), shopadmingen.ChiServerOptions{\n            BaseRouter:       r,\n            ErrorHandlerFunc: httperr.WriteBadRequest,\n        })\n    })\n}\n",[351,2393,2394,2400,2409,2418,2423,2427,2431,2442,2457,2470,2485,2490,2494,2498,2525,2541,2562,2578,2610,2661,2681,2688,2700,2718,2758,2762,2773,2787,2801,2805,2817,2827,2841,2845,2849],{"__ignoreMap":391},[395,2395,2396,2398],{"class":397,"line":398},[395,2397,402],{"class":401},[395,2399,919],{"class":405},[395,2401,2402,2404,2407],{"class":397,"line":510},[395,2403,924],{"class":405},[395,2405,2406],{"class":409},"github.com\u002Fgp-system\u002Fgpsystem\u002Fauth",[395,2408,413],{"class":405},[395,2410,2411,2413,2416],{"class":397,"line":554},[395,2412,924],{"class":405},[395,2414,2415],{"class":409},"github.com\u002Fgp-system\u002Fgpsystem\u002Frbac",[395,2417,413],{"class":405},[395,2419,2420],{"class":397,"line":561},[395,2421,2422],{"class":550},"    \u002F\u002F ...\n",[395,2424,2425],{"class":397,"line":932},[395,2426,1029],{"class":405},[395,2428,2429],{"class":397,"line":942},[395,2430,558],{"emptyLinePlaceholder":557},[395,2432,2433,2435,2437,2440],{"class":397,"line":947},[395,2434,564],{"class":405},[395,2436,1820],{"class":409},[395,2438,2439],{"class":405}," struct",[395,2441,1048],{"class":405},[395,2443,2444,2447,2450,2452,2454],{"class":397,"line":957},[395,2445,2446],{"class":1054},"    DB         ",[395,2448,2449],{"class":405},"*",[395,2451,1293],{"class":409},[395,2453,464],{"class":405},[395,2455,2456],{"class":409},"DB\n",[395,2458,2459,2462,2465,2467],{"class":397,"line":962},[395,2460,2461],{"class":1054},"    Transactor ",[395,2463,2464],{"class":409},"dbx",[395,2466,464],{"class":405},[395,2468,2469],{"class":409},"Transactor\n",[395,2471,2472,2475,2478,2480,2482],{"class":397,"line":972},[395,2473,2474],{"class":1054},"    Auth       ",[395,2476,2477],{"class":409},"auth",[395,2479,464],{"class":405},[395,2481,481],{"class":409},[395,2483,2484],{"class":550}," \u002F\u002F JWT_SECRET, JWT_ISSUER, ..., from the platform config\n",[395,2486,2487],{"class":397,"line":982},[395,2488,2489],{"class":550},"    \u002F\u002F gpsystem:dependencies\n",[395,2491,2492],{"class":397,"line":992},[395,2493,1501],{"class":405},[395,2495,2496],{"class":397,"line":1001},[395,2497,558],{"emptyLinePlaceholder":557},[395,2499,2500,2502,2505,2507,2509,2511,2513,2515,2517,2519,2521,2523],{"class":397,"line":1006},[395,2501,447],{"class":405},[395,2503,2504],{"class":450}," RegisterAdmin",[395,2506,454],{"class":405},[395,2508,1807],{"class":457},[395,2510,575],{"class":409},[395,2512,464],{"class":405},[395,2514,580],{"class":409},[395,2516,470],{"class":405},[395,2518,1330],{"class":457},[395,2520,1820],{"class":409},[395,2522,503],{"class":405},[395,2524,1048],{"class":405},[395,2526,2527,2530,2532,2535,2537,2539],{"class":397,"line":1016},[395,2528,2529],{"class":1054},"    adminSvc ",[395,2531,1058],{"class":405},[395,2533,2534],{"class":1054}," adminservice",[395,2536,464],{"class":405},[395,2538,617],{"class":450},[395,2540,1097],{"class":405},[395,2542,2543,2546,2548,2551,2553,2555,2557,2560],{"class":397,"line":1026},[395,2544,2545],{"class":1054},"    adminHandler ",[395,2547,1058],{"class":405},[395,2549,2550],{"class":1054}," adminhttp",[395,2552,464],{"class":405},[395,2554,617],{"class":450},[395,2556,454],{"class":405},[395,2558,2559],{"class":1054},"adminSvc",[395,2561,1029],{"class":405},[395,2563,2564,2567,2569,2572,2574,2576],{"class":397,"line":1032},[395,2565,2566],{"class":1054},"    adminPolicies ",[395,2568,1058],{"class":405},[395,2570,2571],{"class":1054}," adminpolicy",[395,2573,464],{"class":405},[395,2575,617],{"class":450},[395,2577,1097],{"class":405},[395,2579,2580,2582,2584,2586,2588,2590,2592,2594,2596,2598,2600,2602,2604,2606,2608],{"class":397,"line":1037},[395,2581,1882],{"class":1054},[395,2583,464],{"class":405},[395,2585,1720],{"class":450},[395,2587,454],{"class":405},[395,2589,1368],{"class":405},[395,2591,2166],{"class":1371},[395,2593,1368],{"class":405},[395,2595,470],{"class":405},[395,2597,569],{"class":405},[395,2599,572],{"class":457},[395,2601,575],{"class":409},[395,2603,464],{"class":405},[395,2605,580],{"class":409},[395,2607,503],{"class":405},[395,2609,1048],{"class":405},[395,2611,2612,2614,2616,2619,2621,2623,2625,2628,2630,2633,2635,2638,2641,2644,2646,2649,2651,2653,2656,2658],{"class":397,"line":1051},[395,2613,1358],{"class":1054},[395,2615,464],{"class":405},[395,2617,2618],{"class":450},"Use",[395,2620,454],{"class":405},[395,2622,2477],{"class":1054},[395,2624,464],{"class":405},[395,2626,2627],{"class":450},"MiddlewareHTTP",[395,2629,454],{"class":405},[395,2631,2632],{"class":1054},"deps",[395,2634,464],{"class":405},[395,2636,2637],{"class":1054},"Auth",[395,2639,2640],{"class":405},"),",[395,2642,2643],{"class":1054}," rbac",[395,2645,464],{"class":405},[395,2647,2648],{"class":450},"RequireRoleHTTP",[395,2650,454],{"class":405},[395,2652,1368],{"class":405},[395,2654,2655],{"class":1371},"admin",[395,2657,1368],{"class":405},[395,2659,2660],{"class":405},"))\n",[395,2662,2663,2666,2668,2670,2672,2675,2677,2679],{"class":397,"line":1082},[395,2664,2665],{"class":1054},"        shopadmingen",[395,2667,464],{"class":405},[395,2669,1921],{"class":450},[395,2671,454],{"class":405},[395,2673,2674],{"class":1054},"shopadmingen",[395,2676,464],{"class":405},[395,2678,1931],{"class":450},[395,2680,1934],{"class":405},[395,2682,2683,2686],{"class":397,"line":1100},[395,2684,2685],{"class":1054},"            adminHandler",[395,2687,1942],{"class":405},[395,2689,2690,2692,2694,2696,2698],{"class":397,"line":1131},[395,2691,1947],{"class":405},[395,2693,2674],{"class":409},[395,2695,464],{"class":405},[395,2697,1954],{"class":409},[395,2699,1216],{"class":405},[395,2701,2702,2704,2706,2708,2710,2712,2714,2716],{"class":397,"line":1136},[395,2703,1961],{"class":1054},[395,2705,464],{"class":405},[395,2707,1966],{"class":450},[395,2709,1069],{"class":405},[395,2711,2674],{"class":409},[395,2713,464],{"class":405},[395,2715,1975],{"class":409},[395,2717,1978],{"class":405},[395,2719,2720,2722,2724,2726,2728,2730,2732,2734,2736,2739,2741,2744,2746,2748,2750,2752,2754,2756],{"class":397,"line":1183},[395,2721,1988],{"class":1054},[395,2723,464],{"class":405},[395,2725,1993],{"class":450},[395,2727,1069],{"class":405},[395,2729,2674],{"class":409},[395,2731,464],{"class":405},[395,2733,1975],{"class":409},[395,2735,2004],{"class":405},[395,2737,2738],{"class":1054},"adminPolicies",[395,2740,470],{"class":405},[395,2742,2743],{"class":1054}," shopadmingen",[395,2745,464],{"class":405},[395,2747,2017],{"class":1054},[395,2749,470],{"class":405},[395,2751,2743],{"class":1054},[395,2753,464],{"class":405},[395,2755,2026],{"class":1054},[395,2757,1241],{"class":405},[395,2759,2760],{"class":397,"line":1200},[395,2761,2033],{"class":405},[395,2763,2764,2767,2769,2771],{"class":397,"line":1219},[395,2765,2766],{"class":409},"            shopadmingen",[395,2768,464],{"class":405},[395,2770,2043],{"class":409},[395,2772,1216],{"class":405},[395,2774,2775,2777,2779,2781,2783,2785],{"class":397,"line":1244},[395,2776,2050],{"class":1054},[395,2778,1225],{"class":405},[395,2780,2055],{"class":1054},[395,2782,464],{"class":405},[395,2784,863],{"class":1054},[395,2786,1942],{"class":405},[395,2788,2789,2791,2793,2795,2797,2799],{"class":397,"line":1265},[395,2790,2066],{"class":1054},[395,2792,1225],{"class":405},[395,2794,2071],{"class":1054},[395,2796,464],{"class":405},[395,2798,860],{"class":1054},[395,2800,1942],{"class":405},[395,2802,2803],{"class":397,"line":1307},[395,2804,2033],{"class":405},[395,2806,2807,2809,2811,2813,2815],{"class":397,"line":1313},[395,2808,2086],{"class":405},[395,2810,2743],{"class":409},[395,2812,464],{"class":405},[395,2814,2093],{"class":409},[395,2816,1216],{"class":405},[395,2818,2819,2821,2823,2825],{"class":397,"line":1335},[395,2820,2100],{"class":1054},[395,2822,1225],{"class":405},[395,2824,2105],{"class":1054},[395,2826,1942],{"class":405},[395,2828,2829,2831,2833,2835,2837,2839],{"class":397,"line":1355},[395,2830,2112],{"class":1054},[395,2832,1225],{"class":405},[395,2834,2071],{"class":1054},[395,2836,464],{"class":405},[395,2838,863],{"class":1054},[395,2840,1942],{"class":405},[395,2842,2843],{"class":397,"line":1384},[395,2844,2127],{"class":405},[395,2846,2847],{"class":397,"line":1393},[395,2848,2132],{"class":405},[395,2850,2851],{"class":397,"line":1431},[395,2852,1501],{"class":405},[348,2854,2855,2860,2861,2864,2865,2868,2869,2874,2875,2878,2879,2881],{},[360,2856,2857],{"href":204},[351,2858,2859],{},"auth.MiddlewareHTTP"," parses the ",[351,2862,2863],{},"Bearer"," token and puts an ",[351,2866,2867],{},"rbac.Identity"," into the context; ",[360,2870,2871],{"href":209},[351,2872,2873],{},"rbac.RequireRoleHTTP(\"admin\")"," short-circuits with a 403 problem without that role. Every ",[351,2876,2877],{},"\u002Fapi\u002Fv1\u002Fadmin\u002Fshop\u002F..."," route is protected. The ",[351,2880,436],{}," surface's public routes are untouched.",[2883,2884,2885,2886,2889,2890,753,2893,2896,2897,2900,2901,2903,2904,2907,2908,606,2911,2914],"tip",{},"Rule of thumb for middleware levels: ",[365,2887,2888],{},"process-level"," (rate limit, request ID) → ",[351,2891,2892],{},"chix.WithMiddleware",[365,2894,2895],{},"surface-level"," (auth, role) → ",[351,2898,2899],{},"r.Use"," in the surface's ",[351,2902,1720],{}," block; ",[365,2905,2906],{},"operation-level"," (permission, ownership check) → ",[351,2909,2910],{},"@permission",[351,2912,2913],{},"@policy"," in the spec, enforced by the enforcer.",[434,2916,2918],{"id":2917},"the-point-a-standard-httpserver","The point: a standard http.Server",[348,2920,2921,2922,2927,2928,2930],{},"With the chi engine your server ",[365,2923,2924,2925],{},"is a standard ",[351,2926,597],{},". Everything the Go world knows about net\u002Fhttp (middleware patterns, testing with ",[351,2929,383],{},", pprof, any reverse-proxy recipe) applies unmodified.",[434,2932,2934],{"id":2933},"testing","Testing",[348,2936,2937,2939,2940,1225],{},[351,2938,617],{}," returns the fully wired router; you test with plain ",[351,2941,2942],{},"net\u002Fhttp\u002Fhttptest",[386,2944,2946],{"className":388,"code":2945,"language":390,"meta":391,"style":391},"r := chix.New(server.Config{}, chix.WithoutTelemetry())\nr.Post(\"\u002Fthings\", createThing)\n\nsrv := httptest.NewServer(r)\ndefer srv.Close()\nresp, err := http.Post(srv.URL+\"\u002Fthings\", \"application\u002Fjson\", body)\n",[351,2947,2948,2982,3007,3011,3032,3046],{"__ignoreMap":391},[395,2949,2950,2953,2955,2957,2959,2961,2963,2966,2968,2970,2973,2975,2977,2979],{"class":397,"line":398},[395,2951,2952],{"class":1054},"r ",[395,2954,1058],{"class":405},[395,2956,1144],{"class":1054},[395,2958,464],{"class":405},[395,2960,617],{"class":450},[395,2962,454],{"class":405},[395,2964,2965],{"class":409},"server",[395,2967,464],{"class":405},[395,2969,481],{"class":409},[395,2971,2972],{"class":405},"{},",[395,2974,1144],{"class":1054},[395,2976,464],{"class":405},[395,2978,1690],{"class":450},[395,2980,2981],{"class":405},"())\n",[395,2983,2984,2986,2988,2991,2993,2995,2998,3000,3002,3005],{"class":397,"line":510},[395,2985,572],{"class":1054},[395,2987,464],{"class":405},[395,2989,2990],{"class":450},"Post",[395,2992,454],{"class":405},[395,2994,1368],{"class":405},[395,2996,2997],{"class":1371},"\u002Fthings",[395,2999,1368],{"class":405},[395,3001,470],{"class":405},[395,3003,3004],{"class":1054}," createThing",[395,3006,1029],{"class":405},[395,3008,3009],{"class":397,"line":554},[395,3010,558],{"emptyLinePlaceholder":557},[395,3012,3013,3016,3018,3021,3023,3026,3028,3030],{"class":397,"line":561},[395,3014,3015],{"class":1054},"srv ",[395,3017,1058],{"class":405},[395,3019,3020],{"class":1054}," httptest",[395,3022,464],{"class":405},[395,3024,3025],{"class":450},"NewServer",[395,3027,454],{"class":405},[395,3029,572],{"class":1054},[395,3031,1029],{"class":405},[395,3033,3034,3037,3040,3042,3044],{"class":397,"line":932},[395,3035,3036],{"class":401},"defer",[395,3038,3039],{"class":1054}," srv",[395,3041,464],{"class":405},[395,3043,1439],{"class":450},[395,3045,1097],{"class":405},[395,3047,3048,3051,3053,3055,3057,3059,3061,3063,3065,3068,3070,3073,3076,3078,3080,3082,3084,3086,3089,3091,3093,3096],{"class":397,"line":942},[395,3049,3050],{"class":1054},"resp",[395,3052,470],{"class":405},[395,3054,1463],{"class":1054},[395,3056,1058],{"class":405},[395,3058,2250],{"class":1054},[395,3060,464],{"class":405},[395,3062,2990],{"class":450},[395,3064,454],{"class":405},[395,3066,3067],{"class":1054},"srv",[395,3069,464],{"class":405},[395,3071,3072],{"class":1054},"URL",[395,3074,3075],{"class":405},"+",[395,3077,1368],{"class":405},[395,3079,2997],{"class":1371},[395,3081,1368],{"class":405},[395,3083,470],{"class":405},[395,3085,406],{"class":405},[395,3087,3088],{"class":1371},"application\u002Fjson",[395,3090,1368],{"class":405},[395,3092,470],{"class":405},[395,3094,3095],{"class":1054}," body",[395,3097,1029],{"class":405},[348,3099,3100],{},"The panic recoverer, the 404\u002F405 problem handlers and the body limit are active here too, so the test sees exactly the problem+json response the client will.",[434,3102,3104],{"id":3103},"patterns-used","Patterns used",[842,3106,3107,3122],{},[845,3108,3109,812,3112,606,3115,3118,3119,464],{},[365,3110,3111],{},"Context-injected validator",[351,3113,3114],{},"injectValidator",[351,3116,3117],{},"validatorFromContext",", with an unexported context key): ",[360,3120,92],{"href":3121},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#context-injected-dependency",[845,3123,3124,812,3127,3130,3131,825,3133,464],{},[365,3125,3126],{},"Generic middleware adapter",[351,3128,3129],{},"StrictValidator[H ~func(...)]",", fitting every generated ",[351,3132,1975],{},[360,3134,92],{"href":3135},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#generic-middleware-adapter",[3137,3138],"hr",{},[348,3140,3141,3142,3145,3146,464],{},"The same page for the other engine: ",[360,3143,3144],{"href":109},"the Fiber engine",". Error rendering in detail: ",[360,3147,118],{"href":119},[3149,3150,3151],"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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":391,"searchDepth":510,"depth":510,"links":3153},[3154,3155,3158,3159,3160,3163,3164,3165,3166],{"id":436,"depth":510,"text":437},{"id":623,"depth":510,"text":624,"children":3156},[3157],{"id":832,"depth":554,"text":833},{"id":887,"depth":510,"text":888},{"id":1512,"depth":510,"text":1513},{"id":1770,"depth":510,"text":1771,"children":3161},[3162],{"id":2213,"depth":554,"text":2214},{"id":2378,"depth":510,"text":2379},{"id":2917,"depth":510,"text":2918},{"id":2933,"depth":510,"text":2934},{"id":3103,"depth":510,"text":3104},"server\u002Fchix, a chi router over the standard net\u002Fhttp with the kit's error handling and telemetry wired in, run with graceful shutdown.","md",null,{},{"icon":116},{"title":113,"description":3167},"Xz-bMylCERghD9vzvjqoE1BhWHBAn_Uo1jHgbatWjQ8",[3175,3177],{"title":108,"path":109,"stem":110,"description":3176,"icon":111,"children":-1},"server\u002Ffiberx, a Fiber v3 app with the kit's error handling, validation and telemetry wired in, run with graceful shutdown.",{"title":118,"path":119,"stem":120,"description":3178,"icon":121,"children":-1},"The httperr package, RFC 9457 problem+json for every error, through one central mapping, on both engines.",1784668707169]