[{"data":1,"prerenderedAt":1988},["ShallowReactive",2],{"navigation_docs_en":3,"-en-httperr-validation":441,"-en-httperr-validation-surround":1983},[4,45,60,81,112,129,146,173,197,213,242,279,356,372,384,403,429],{"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},"queue","i-lucide-layers","\u002Fen\u002Fqueue","en\u002F10.queue",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Overview","\u002Fen\u002Fqueue\u002Foverview","en\u002F10.queue\u002F1.overview","i-lucide-list-ordered",{"title":57,"path":58,"stem":59,"icon":47},"Tasks","\u002Fen\u002Fqueue\u002Ftasks","en\u002F10.queue\u002F2.tasks",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":44},"events","i-lucide-workflow","\u002Fen\u002Fevents","en\u002F11.events",[66,71,76],{"title":67,"path":68,"stem":69,"icon":70},"Events","\u002Fen\u002Fevents\u002Foverview","en\u002F11.events\u002F1.overview","i-lucide-radio",{"title":72,"path":73,"stem":74,"icon":75},"Outbox","\u002Fen\u002Fevents\u002Foutbox","en\u002F11.events\u002F2.outbox","i-lucide-inbox",{"title":77,"path":78,"stem":79,"icon":80},"Scheduling","\u002Fen\u002Fevents\u002Fscheduler","en\u002F11.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":82,"icon":83,"path":84,"stem":85,"children":86,"page":44},"auth","i-lucide-shield-check","\u002Fen\u002Fauth","en\u002F12.auth",[87,92,97,102,107],{"title":88,"path":89,"stem":90,"icon":91},"Authentication","\u002Fen\u002Fauth\u002Foverview","en\u002F12.auth\u002F1.overview","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"RBAC","\u002Fen\u002Fauth\u002Frbac","en\u002F12.auth\u002F2.rbac","i-lucide-users",{"title":98,"path":99,"stem":100,"icon":101},"Policies","\u002Fen\u002Fauth\u002Fpolicy","en\u002F12.auth\u002F3.policy","i-lucide-gavel",{"title":103,"path":104,"stem":105,"icon":106},"Using auth standalone","\u002Fen\u002Fauth\u002Fstandalone","en\u002F12.auth\u002F4.standalone","i-lucide-plug",{"title":108,"path":109,"stem":110,"icon":111},"Social login with goth, standalone","\u002Fen\u002Fauth\u002Fsocial-standalone","en\u002F12.auth\u002F5.social-standalone","i-lucide-log-in",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":44},"mail","i-lucide-mail","\u002Fen\u002Fmail","en\u002F13.mail",[118,121,125],{"title":52,"path":119,"stem":120,"icon":114},"\u002Fen\u002Fmail\u002Foverview","en\u002F13.mail\u002F1.overview",{"title":122,"path":123,"stem":124,"icon":114},"SMTP","\u002Fen\u002Fmail\u002Fsmtp","en\u002F13.mail\u002F2.smtp",{"title":126,"path":127,"stem":128,"icon":114},"MJML","\u002Fen\u002Fmail\u002Fmjml","en\u002F13.mail\u002F3.mjml",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":44},"notify","i-lucide-bell","\u002Fen\u002Fnotify","en\u002F14.notify",[135,138,142],{"title":52,"path":136,"stem":137,"icon":131},"\u002Fen\u002Fnotify\u002Foverview","en\u002F14.notify\u002F1.overview",{"title":139,"path":140,"stem":141,"icon":131},"Database channel","\u002Fen\u002Fnotify\u002Fdatabase","en\u002F14.notify\u002F2.database",{"title":143,"path":144,"stem":145,"icon":131},"Broadcast channel","\u002Fen\u002Fnotify\u002Fbroadcast","en\u002F14.notify\u002F3.broadcast",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":44},"storage","i-lucide-hard-drive","\u002Fen\u002Fstorage","en\u002F15.storage",[152,155,159,163,168],{"title":52,"path":153,"stem":154,"icon":148},"\u002Fen\u002Fstorage\u002Foverview","en\u002F15.storage\u002F1.overview",{"title":156,"path":157,"stem":158},"Disk API","\u002Fen\u002Fstorage\u002Fdisk-api","en\u002F15.storage\u002F2.disk-api",{"title":160,"path":161,"stem":162,"icon":148},"Memory and local disk","\u002Fen\u002Fstorage\u002Flocal","en\u002F15.storage\u002F3.local",{"title":164,"path":165,"stem":166,"icon":167},"S3","\u002Fen\u002Fstorage\u002Fs3","en\u002F15.storage\u002F4.s3","i-lucide-cloud",{"title":169,"path":170,"stem":171,"icon":172},"Testing","\u002Fen\u002Fstorage\u002Ftesting","en\u002F15.storage\u002F5.testing","i-lucide-flask-conical",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"telemetry","i-lucide-activity","\u002Fen\u002Ftelemetry","en\u002F16.telemetry",[179,182,187,192],{"title":52,"path":180,"stem":181,"icon":175},"\u002Fen\u002Ftelemetry\u002Foverview","en\u002F16.telemetry\u002F1.overview",{"title":183,"path":184,"stem":185,"icon":186},"Logging","\u002Fen\u002Ftelemetry\u002Flogging","en\u002F16.telemetry\u002F2.logging","i-lucide-scroll-text",{"title":188,"path":189,"stem":190,"icon":191},"OpenTelemetry","\u002Fen\u002Ftelemetry\u002Fopentelemetry","en\u002F16.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":193,"path":194,"stem":195,"icon":196},"Sentry","\u002Fen\u002Ftelemetry\u002Fsentry","en\u002F16.telemetry\u002F4.sentry","i-lucide-bug",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F17.reference",[203,208],{"title":204,"path":205,"stem":206,"icon":207},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F17.reference\u002F1.configuration","i-lucide-settings",{"title":209,"path":210,"stem":211,"icon":212},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F17.reference\u002F2.dependencies","i-lucide-package",{"title":214,"icon":215,"path":216,"stem":217,"children":218,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[219,223,228,232,237],{"title":220,"path":221,"stem":222,"icon":47},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture",{"title":224,"path":225,"stem":226,"icon":227},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":204,"path":229,"stem":230,"icon":231},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F3.configuration","i-lucide-settings-2",{"title":233,"path":234,"stem":235,"icon":236},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":238,"path":239,"stem":240,"icon":241},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":243,"icon":244,"path":245,"stem":246,"children":247,"page":44},"Kit","i-lucide-box","\u002Fen\u002Fkit","en\u002F3.kit",[248,251,256,261,266,270,275],{"title":52,"path":249,"stem":250,"icon":244},"\u002Fen\u002Fkit\u002Foverview","en\u002F3.kit\u002F1.overview",{"title":252,"path":253,"stem":254,"icon":255},"Application lifecycle","\u002Fen\u002Fkit\u002Fapp","en\u002F3.kit\u002F2.app","i-lucide-power",{"title":257,"path":258,"stem":259,"icon":260},"Server","\u002Fen\u002Fkit\u002Fserver","en\u002F3.kit\u002F3.server","i-lucide-server",{"title":262,"path":263,"stem":264,"icon":265},"Worker","\u002Fen\u002Fkit\u002Fworker","en\u002F3.kit\u002F4.worker","i-lucide-cog",{"title":267,"path":268,"stem":269,"icon":70},"Realtime","\u002Fen\u002Fkit\u002Frealtime","en\u002F3.kit\u002F5.realtime",{"title":271,"path":272,"stem":273,"icon":274},"Migrations","\u002Fen\u002Fkit\u002Fmigrations","en\u002F3.kit\u002F6.migrations","i-lucide-file-stack",{"title":276,"path":277,"stem":278,"icon":111},"Social login","\u002Fen\u002Fkit\u002Fsocial-login","en\u002F3.kit\u002F7.social-login",{"title":280,"icon":281,"path":282,"stem":283,"children":284,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F4.cli",[285,288,293,298,303,308,313,318,323,328,333,337,342,347,351],{"title":52,"path":286,"stem":287,"icon":281},"\u002Fen\u002Fcli\u002Foverview","en\u002F4.cli\u002F1.overview",{"title":289,"path":290,"stem":291,"icon":292},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F4.cli\u002F10.add-db","i-lucide-database-zap",{"title":294,"path":295,"stem":296,"icon":297},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F4.cli\u002F11.add-compose","i-lucide-container",{"title":299,"path":300,"stem":301,"icon":302},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F4.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":304,"path":305,"stem":306,"icon":307},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F4.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":309,"path":310,"stem":311,"icon":312},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F4.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":314,"path":315,"stem":316,"icon":317},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F4.cli\u002F15.add-seeder","i-lucide-sprout",{"title":319,"path":320,"stem":321,"icon":322},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F4.cli\u002F2.new-project","i-lucide-folder-plus",{"title":324,"path":325,"stem":326,"icon":327},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F4.cli\u002F3.new-module","i-lucide-blocks",{"title":329,"path":330,"stem":331,"icon":332},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F4.cli\u002F4.add-surface","i-lucide-layers-2",{"title":334,"path":335,"stem":336,"icon":244},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F4.cli\u002F5.add-core",{"title":338,"path":339,"stem":340,"icon":341},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F4.cli\u002F6.add-handler","i-lucide-webhook",{"title":343,"path":344,"stem":345,"icon":346},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F4.cli\u002F7.new-migration","i-lucide-file-plus",{"title":348,"path":349,"stem":350,"icon":265},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F4.cli\u002F8.worker-generators",{"title":352,"path":353,"stem":354,"icon":355},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F4.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"title":357,"icon":227,"path":358,"stem":359,"children":360,"page":44},"errs","\u002Fen\u002Ferrs","en\u002F5.errs",[361,364,368],{"title":52,"path":362,"stem":363,"icon":227},"\u002Fen\u002Ferrs\u002Foverview","en\u002F5.errs\u002F1.overview",{"title":365,"path":366,"stem":367},"API","\u002Fen\u002Ferrs\u002Fapi","en\u002F5.errs\u002F2.api",{"title":369,"path":370,"stem":371,"icon":227},"Integration","\u002Fen\u002Ferrs\u002Fintegration","en\u002F5.errs\u002F3.integration",{"title":373,"icon":231,"path":374,"stem":375,"children":376,"page":44},"envconf","\u002Fen\u002Fenvconf","en\u002F6.envconf",[377,380],{"title":52,"path":378,"stem":379,"icon":231},"\u002Fen\u002Fenvconf\u002Foverview","en\u002F6.envconf\u002F1.overview",{"title":381,"path":382,"stem":383},"Recipes","\u002Fen\u002Fenvconf\u002Frecipes","en\u002F6.envconf\u002F2.recipes",{"title":385,"icon":386,"path":387,"stem":388,"children":389,"page":44},"httperr","i-lucide-globe","\u002Fen\u002Fhttperr","en\u002F7.httperr",[390,393,398],{"title":52,"path":391,"stem":392,"icon":386},"\u002Fen\u002Fhttperr\u002Foverview","en\u002F7.httperr\u002F1.overview",{"title":394,"path":395,"stem":396,"icon":397},"Error responses","\u002Fen\u002Fhttperr\u002Ferror-responses","en\u002F7.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":399,"path":400,"stem":401,"icon":402},"Validation","\u002Fen\u002Fhttperr\u002Fvalidation","en\u002F7.httperr\u002F3.validation","i-lucide-badge-check",{"title":404,"icon":405,"path":406,"stem":407,"children":408,"page":44},"dbx","i-lucide-database","\u002Fen\u002Fdbx","en\u002F8.dbx",[409,412,416,420,425],{"title":52,"path":410,"stem":411,"icon":405},"\u002Fen\u002Fdbx\u002Foverview","en\u002F8.dbx\u002F1.overview",{"title":413,"path":414,"stem":415,"icon":106},"pgx","\u002Fen\u002Fdbx\u002Fpg","en\u002F8.dbx\u002F2.pg",{"title":417,"path":418,"stem":419,"icon":47},"bun","\u002Fen\u002Fdbx\u002Fbunx","en\u002F8.dbx\u002F3.bunx",{"title":421,"path":422,"stem":423,"icon":424},"Transactions","\u002Fen\u002Fdbx\u002Ftransactions","en\u002F8.dbx\u002F4.transactions","i-lucide-git-merge",{"title":426,"path":427,"stem":428,"icon":317},"Seeders","\u002Fen\u002Fdbx\u002Fseed","en\u002F8.dbx\u002F5.seed",{"title":430,"icon":55,"path":431,"stem":432,"children":433,"page":44},"paginate","\u002Fen\u002Fpaginate","en\u002F9.paginate",[434,437],{"title":52,"path":435,"stem":436,"icon":55},"\u002Fen\u002Fpaginate\u002Foverview","en\u002F9.paginate\u002F1.overview",{"title":438,"path":439,"stem":440,"icon":19},"Cursor pagination","\u002Fen\u002Fpaginate\u002Fcursor","en\u002F9.paginate\u002F2.cursor",{"id":442,"title":399,"body":443,"description":1976,"extension":1977,"links":1978,"meta":1979,"navigation":1980,"path":400,"seo":1981,"stem":401,"__hash__":1982},"docs_en\u002Fen\u002F7.httperr\u002F3.validation.md",{"type":444,"value":445,"toc":1964},"minimark",[446,471,500,505,659,665,704,753,757,794,885,911,926,930,933,1005,1016,1321,1335,1421,1425,1434,1621,1641,1645,1656,1771,1775,1784,1836,1845,1849,1870,1874,1888,1892,1919,1923,1960],[447,448,449,450,454,455,462,463,466,467,470],"p",{},"Request validation in gpsystem is declarative: you write rules as tags on struct fields, and ",[451,452,453],"code",{},"httperr\u002Fvalidate"," handles execution and error rendering. The engine is ",[456,457,461],"a",{"href":458,"rel":459},"https:\u002F\u002Fgithub.com\u002Fgo-playground\u002Fvalidator",[460],"nofollow","go-playground\u002Fvalidator v10"," (Go's de facto standard validator); the ",[451,464,465],{},"validate"," package wires it so that every failure becomes an ",[456,468,469],{"href":395},"RFC 9457 problem response"," with a per-field error list, with no handler code involved.",[472,473,478],"pre",{"className":474,"code":475,"language":476,"meta":477,"style":477},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fhttperr\u002Fvalidate\"\n","go","",[451,479,480],{"__ignoreMap":477},[481,482,485,489,493,497],"span",{"class":483,"line":484},"line",1,[481,486,488],{"class":487},"s7zQu","import",[481,490,492],{"class":491},"sMK4o"," \"",[481,494,496],{"class":495},"sBMFI","github.com\u002Fgp-system\u002Fhttperr\u002Fvalidate",[481,498,499],{"class":491},"\"\n",[501,502,504],"h2",{"id":503},"the-validator-type","The Validator type",[472,506,508],{"className":474,"code":507,"language":476,"meta":477,"style":477},"func New(opts ...Option) *Validator\n\nfunc (v *Validator) Validate(out any) error \u002F\u002F validates out, wraps failures as a *httperr.Problem (422)\nfunc (v *Validator) Struct(s any) error     \u002F\u002F the same, named for use outside a binding pipeline\n\nfunc Translate(verrs validator.ValidationErrors) []httperr.FieldError\n",[451,509,510,541,548,588,620,625],{"__ignoreMap":477},[481,511,512,515,519,522,526,529,532,535,538],{"class":483,"line":484},[481,513,514],{"class":491},"func",[481,516,518],{"class":517},"s2Zo4"," New",[481,520,521],{"class":491},"(",[481,523,525],{"class":524},"sHdIc","opts",[481,527,528],{"class":491}," ...",[481,530,531],{"class":495},"Option",[481,533,534],{"class":491},")",[481,536,537],{"class":491}," *",[481,539,540],{"class":495},"Validator\n",[481,542,544],{"class":483,"line":543},2,[481,545,547],{"emptyLinePlaceholder":546},true,"\n",[481,549,551,553,556,559,562,565,567,570,572,575,578,580,584],{"class":483,"line":550},3,[481,552,514],{"class":491},[481,554,555],{"class":491}," (",[481,557,558],{"class":524},"v ",[481,560,561],{"class":491},"*",[481,563,564],{"class":495},"Validator",[481,566,534],{"class":491},[481,568,569],{"class":517}," Validate",[481,571,521],{"class":491},[481,573,574],{"class":524},"out",[481,576,577],{"class":495}," any",[481,579,534],{"class":491},[481,581,583],{"class":582},"spNyl"," error",[481,585,587],{"class":586},"sHwdD"," \u002F\u002F validates out, wraps failures as a *httperr.Problem (422)\n",[481,589,591,593,595,597,599,601,603,606,608,611,613,615,617],{"class":483,"line":590},4,[481,592,514],{"class":491},[481,594,555],{"class":491},[481,596,558],{"class":524},[481,598,561],{"class":491},[481,600,564],{"class":495},[481,602,534],{"class":491},[481,604,605],{"class":517}," Struct",[481,607,521],{"class":491},[481,609,610],{"class":524},"s",[481,612,577],{"class":495},[481,614,534],{"class":491},[481,616,583],{"class":582},[481,618,619],{"class":586},"     \u002F\u002F the same, named for use outside a binding pipeline\n",[481,621,623],{"class":483,"line":622},5,[481,624,547],{"emptyLinePlaceholder":546},[481,626,628,630,633,635,638,641,644,647,649,652,654,656],{"class":483,"line":627},6,[481,629,514],{"class":491},[481,631,632],{"class":517}," Translate",[481,634,521],{"class":491},[481,636,637],{"class":524},"verrs",[481,639,640],{"class":495}," validator",[481,642,643],{"class":491},".",[481,645,646],{"class":495},"ValidationErrors",[481,648,534],{"class":491},[481,650,651],{"class":491}," []",[481,653,385],{"class":495},[481,655,643],{"class":491},[481,657,658],{"class":495},"FieldError\n",[447,660,661,664],{},[451,662,663],{},"New"," builds the validator with two fixed defaults:",[666,667,668,682],"ul",{},[669,670,671,677,678,681],"li",{},[672,673,674],"strong",{},[451,675,676],{},"WithRequiredStructEnabled()",": ",[451,679,680],{},"required"," semantics for nested structs follow the validator v11 default;",[669,683,684,687,688,691,692,695,696,699,700,703],{},[672,685,686],{},"json-tag field names",": field names in errors come from the ",[451,689,690],{},"json"," tag (",[451,693,694],{},"customerEmail",", not ",[451,697,698],{},"CustomerEmail","), with dotted paths for nested structs (",[451,701,702],{},"address.city",").",[447,705,706,707,710,711,714,715,718,719,722,723,726,727,729,730,732,733,732,736,732,739,732,742,732,745,748,749,752],{},"On failure, ",[451,708,709],{},"Validate","\u002F",[451,712,713],{},"Struct"," does not return a raw validator error but a ready ",[451,716,717],{},"*httperr.Problem"," (422), with the field errors translated into ",[451,720,721],{},"httperr.FieldError","s via ",[451,724,725],{},"Translate",": the error renderer has nothing left to do with it. ",[451,728,725],{}," writes human-readable messages for the common rules (",[451,731,680],{},", ",[451,734,735],{},"email",[451,737,738],{},"min",[451,740,741],{},"max",[451,743,744],{},"oneof",[451,746,747],{},"gt",", ...); unknown rules fall back to the ",[451,750,751],{},"failed rule \u003Ctag>=\u003Cparam>"," form.",[501,754,756],{"id":755},"wiring-strict-middleware-before-the-handler","Wiring: strict middleware before the handler",[447,758,759,762,763,768,769,771,772,775,776,778,779,785,786,789,790,793],{},[451,760,761],{},"net\u002Fhttp","'s standard strict server (generated by ",[456,764,767],{"href":765,"rel":766},"https:\u002F\u002Fgithub.com\u002Foapi-codegen\u002Foapi-codegen",[460],"oapi-codegen"," from the OpenAPI spec, see ",[456,770,233],{"href":234},") decodes the request with ",[451,773,774],{},"json.Decoder",", without validating it: ",[451,777,465],{}," closes that gap as strict middleware, validating the fully bound request object (parameters + body) before the handler runs. In a generated gpsystem project, the kit's ",[456,780,782],{"href":781},"\u002Fen\u002Fkit\u002Fserver#strictvalidator-validation-before-the-handler",[451,783,784],{},"server.StrictValidator"," is the thin adapter that wires a ",[451,787,788],{},"*validate.Validator"," into that middleware slot; the generated ",[451,791,792],{},"register.go"," wires it automatically:",[472,795,797],{"className":474,"code":796,"language":476,"meta":477,"style":477},"shopapigen.NewStrictHandlerWithOptions(apiHandler,\n    []shopapigen.StrictMiddlewareFunc{\n        server.StrictValidator[shopapigen.StrictHandlerFunc](nil),\n        \u002F\u002F ...\n    },\n    shopapigen.StrictHTTPServerOptions{ \u002F* httperr hooks *\u002F })\n",[451,798,799,818,833,856,861,866],{"__ignoreMap":477},[481,800,801,805,807,810,812,815],{"class":483,"line":484},[481,802,804],{"class":803},"sTEyZ","shopapigen",[481,806,643],{"class":491},[481,808,809],{"class":517},"NewStrictHandlerWithOptions",[481,811,521],{"class":491},[481,813,814],{"class":803},"apiHandler",[481,816,817],{"class":491},",\n",[481,819,820,823,825,827,830],{"class":483,"line":543},[481,821,822],{"class":491},"    []",[481,824,804],{"class":495},[481,826,643],{"class":491},[481,828,829],{"class":495},"StrictMiddlewareFunc",[481,831,832],{"class":491},"{\n",[481,834,835,838,840,843,846,848,850,853],{"class":483,"line":550},[481,836,837],{"class":803},"        server",[481,839,643],{"class":491},[481,841,842],{"class":517},"StrictValidator",[481,844,845],{"class":491},"[",[481,847,804],{"class":495},[481,849,643],{"class":491},[481,851,852],{"class":495},"StrictHandlerFunc",[481,854,855],{"class":491},"](nil),\n",[481,857,858],{"class":483,"line":590},[481,859,860],{"class":586},"        \u002F\u002F ...\n",[481,862,863],{"class":483,"line":622},[481,864,865],{"class":491},"    },\n",[481,867,868,871,873,876,879,882],{"class":483,"line":627},[481,869,870],{"class":495},"    shopapigen",[481,872,643],{"class":491},[481,874,875],{"class":495},"StrictHTTPServerOptions",[481,877,878],{"class":491},"{",[481,880,881],{"class":586}," \u002F* httperr hooks *\u002F",[481,883,884],{"class":491}," })\n",[447,886,887,888,891,892,895,896,899,900,903,904,907,908,643],{},"The ",[451,889,890],{},"nil"," argument is deliberate: ",[451,893,894],{},"server.StrictValidator(nil)"," resolves the validator at request time, first looking for the one set via ",[451,897,898],{},"server.WithValidator"," in the context, and only then falling back to ",[451,901,902],{},"validate.New()",". A single option in ",[451,905,906],{},"main.go"," thus applies to the whole application, with no edits to generated code. Details, including the exact middleware ordering relative to policy enforcement: ",[456,909,910],{"href":781},"the server core",[447,912,913,914,916,917,920,921,923,924,643],{},"Outside a generated project, wiring ",[451,915,902],{}," needs nothing kit-specific: call ",[451,918,919],{},"v.Struct(req)"," at whatever point in your own ",[451,922,761],{}," handler or middleware chain you'd otherwise validate a decoded request, and return the resulting error unchanged; it's already a ",[451,925,717],{},[501,927,929],{"id":928},"the-shop-the-placeorder-request","The shop: the PlaceOrder request",[447,931,932],{},"The order-placement body carries three rules:",[472,934,936],{"className":474,"code":935,"language":476,"meta":477,"style":477},"type placeOrderBody struct {\n    ProductID     string `json:\"productId\" validate:\"required,uuid4\"`\n    Quantity      int    `json:\"quantity\" validate:\"required,gt=0\"`\n    CustomerEmail string `json:\"customerEmail\" validate:\"required,email\"`\n}\n",[451,937,938,952,970,986,1000],{"__ignoreMap":477},[481,939,940,943,946,949],{"class":483,"line":484},[481,941,942],{"class":491},"type",[481,944,945],{"class":495}," placeOrderBody",[481,947,948],{"class":491}," struct",[481,950,951],{"class":491}," {\n",[481,953,954,957,960,963,967],{"class":483,"line":543},[481,955,956],{"class":803},"    ProductID     ",[481,958,959],{"class":582},"string",[481,961,962],{"class":491}," `",[481,964,966],{"class":965},"sfazB","json:\"productId\" validate:\"required,uuid4\"",[481,968,969],{"class":491},"`\n",[481,971,972,975,978,981,984],{"class":483,"line":550},[481,973,974],{"class":803},"    Quantity      ",[481,976,977],{"class":582},"int",[481,979,980],{"class":491},"    `",[481,982,983],{"class":965},"json:\"quantity\" validate:\"required,gt=0\"",[481,985,969],{"class":491},[481,987,988,991,993,995,998],{"class":483,"line":590},[481,989,990],{"class":803},"    CustomerEmail ",[481,992,959],{"class":582},[481,994,962],{"class":491},[481,996,997],{"class":965},"json:\"customerEmail\" validate:\"required,email\"",[481,999,969],{"class":491},[481,1001,1002],{"class":483,"line":622},[481,1003,1004],{"class":491},"}\n",[447,1006,1007,1008,1011,1012,1015],{},"A request violating all three (",[451,1009,1010],{},"quantity: 0",", a malformed email, a missing ",[451,1013,1014],{},"productId",") gets this response:",[472,1017,1020],{"className":1018,"code":1019,"language":690,"meta":477,"style":477},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"about:blank\",\n  \"title\": \"Unprocessable Entity\",\n  \"status\": 422,\n  \"detail\": \"One or more fields failed validation.\",\n  \"instance\": \"\u002Fapi\u002Fv1\u002Fshop\u002Forders\",\n  \"errors\": [\n    { \"field\": \"productId\", \"rule\": \"required\", \"message\": \"is required\" },\n    { \"field\": \"quantity\", \"rule\": \"gt\", \"message\": \"must be greater than 0\" },\n    { \"field\": \"customerEmail\", \"rule\": \"email\", \"message\": \"must be a valid email address\" }\n  ]\n}\n",[451,1021,1022,1026,1048,1068,1085,1105,1125,1140,1200,1255,1310,1316],{"__ignoreMap":477},[481,1023,1024],{"class":483,"line":484},[481,1025,832],{"class":491},[481,1027,1028,1031,1033,1036,1039,1041,1044,1046],{"class":483,"line":543},[481,1029,1030],{"class":491},"  \"",[481,1032,942],{"class":582},[481,1034,1035],{"class":491},"\"",[481,1037,1038],{"class":491},":",[481,1040,492],{"class":491},[481,1042,1043],{"class":965},"about:blank",[481,1045,1035],{"class":491},[481,1047,817],{"class":491},[481,1049,1050,1052,1055,1057,1059,1061,1064,1066],{"class":483,"line":550},[481,1051,1030],{"class":491},[481,1053,1054],{"class":582},"title",[481,1056,1035],{"class":491},[481,1058,1038],{"class":491},[481,1060,492],{"class":491},[481,1062,1063],{"class":965},"Unprocessable Entity",[481,1065,1035],{"class":491},[481,1067,817],{"class":491},[481,1069,1070,1072,1075,1077,1079,1083],{"class":483,"line":590},[481,1071,1030],{"class":491},[481,1073,1074],{"class":582},"status",[481,1076,1035],{"class":491},[481,1078,1038],{"class":491},[481,1080,1082],{"class":1081},"sbssI"," 422",[481,1084,817],{"class":491},[481,1086,1087,1089,1092,1094,1096,1098,1101,1103],{"class":483,"line":622},[481,1088,1030],{"class":491},[481,1090,1091],{"class":582},"detail",[481,1093,1035],{"class":491},[481,1095,1038],{"class":491},[481,1097,492],{"class":491},[481,1099,1100],{"class":965},"One or more fields failed validation.",[481,1102,1035],{"class":491},[481,1104,817],{"class":491},[481,1106,1107,1109,1112,1114,1116,1118,1121,1123],{"class":483,"line":627},[481,1108,1030],{"class":491},[481,1110,1111],{"class":582},"instance",[481,1113,1035],{"class":491},[481,1115,1038],{"class":491},[481,1117,492],{"class":491},[481,1119,1120],{"class":965},"\u002Fapi\u002Fv1\u002Fshop\u002Forders",[481,1122,1035],{"class":491},[481,1124,817],{"class":491},[481,1126,1128,1130,1133,1135,1137],{"class":483,"line":1127},7,[481,1129,1030],{"class":491},[481,1131,1132],{"class":582},"errors",[481,1134,1035],{"class":491},[481,1136,1038],{"class":491},[481,1138,1139],{"class":491}," [\n",[481,1141,1143,1146,1148,1151,1153,1155,1157,1159,1161,1164,1166,1169,1171,1173,1175,1177,1179,1181,1183,1186,1188,1190,1192,1195,1197],{"class":483,"line":1142},8,[481,1144,1145],{"class":491},"    {",[481,1147,492],{"class":491},[481,1149,1150],{"class":495},"field",[481,1152,1035],{"class":491},[481,1154,1038],{"class":491},[481,1156,492],{"class":491},[481,1158,1014],{"class":965},[481,1160,1035],{"class":491},[481,1162,1163],{"class":491},",",[481,1165,492],{"class":491},[481,1167,1168],{"class":495},"rule",[481,1170,1035],{"class":491},[481,1172,1038],{"class":491},[481,1174,492],{"class":491},[481,1176,680],{"class":965},[481,1178,1035],{"class":491},[481,1180,1163],{"class":491},[481,1182,492],{"class":491},[481,1184,1185],{"class":495},"message",[481,1187,1035],{"class":491},[481,1189,1038],{"class":491},[481,1191,492],{"class":491},[481,1193,1194],{"class":965},"is required",[481,1196,1035],{"class":491},[481,1198,1199],{"class":491}," },\n",[481,1201,1203,1205,1207,1209,1211,1213,1215,1218,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248,1251,1253],{"class":483,"line":1202},9,[481,1204,1145],{"class":491},[481,1206,492],{"class":491},[481,1208,1150],{"class":495},[481,1210,1035],{"class":491},[481,1212,1038],{"class":491},[481,1214,492],{"class":491},[481,1216,1217],{"class":965},"quantity",[481,1219,1035],{"class":491},[481,1221,1163],{"class":491},[481,1223,492],{"class":491},[481,1225,1168],{"class":495},[481,1227,1035],{"class":491},[481,1229,1038],{"class":491},[481,1231,492],{"class":491},[481,1233,747],{"class":965},[481,1235,1035],{"class":491},[481,1237,1163],{"class":491},[481,1239,492],{"class":491},[481,1241,1185],{"class":495},[481,1243,1035],{"class":491},[481,1245,1038],{"class":491},[481,1247,492],{"class":491},[481,1249,1250],{"class":965},"must be greater than 0",[481,1252,1035],{"class":491},[481,1254,1199],{"class":491},[481,1256,1258,1260,1262,1264,1266,1268,1270,1272,1274,1276,1278,1280,1282,1284,1286,1288,1290,1292,1294,1296,1298,1300,1302,1305,1307],{"class":483,"line":1257},10,[481,1259,1145],{"class":491},[481,1261,492],{"class":491},[481,1263,1150],{"class":495},[481,1265,1035],{"class":491},[481,1267,1038],{"class":491},[481,1269,492],{"class":491},[481,1271,694],{"class":965},[481,1273,1035],{"class":491},[481,1275,1163],{"class":491},[481,1277,492],{"class":491},[481,1279,1168],{"class":495},[481,1281,1035],{"class":491},[481,1283,1038],{"class":491},[481,1285,492],{"class":491},[481,1287,735],{"class":965},[481,1289,1035],{"class":491},[481,1291,1163],{"class":491},[481,1293,492],{"class":491},[481,1295,1185],{"class":495},[481,1297,1035],{"class":491},[481,1299,1038],{"class":491},[481,1301,492],{"class":491},[481,1303,1304],{"class":965},"must be a valid email address",[481,1306,1035],{"class":491},[481,1308,1309],{"class":491}," }\n",[481,1311,1313],{"class":483,"line":1312},11,[481,1314,1315],{"class":491},"  ]\n",[481,1317,1319],{"class":483,"line":1318},12,[481,1320,1004],{"class":491},[447,1322,887,1323,1325,1326,1328,1329,1331,1332,1334],{},[451,1324,1168],{}," is machine-readable (the frontend can branch on it per field), the ",[451,1327,1185],{}," is a human-readable default: the final, localized text is typically mapped by the client from the ",[451,1330,1150],{}," + ",[451,1333,1168],{}," pair.",[1336,1337,1339,1354,1414],"note",{"title":1338},"Where do tags on generated models come from?",[447,1340,1341,1342,1345,1346,1349,1350,1353],{},"On your own hand-written structs (service input, internal DTOs) you write the tags yourself. For ",[456,1343,1344],{"href":234},"spec-first"," generated request models, the tag source is the OpenAPI ",[451,1347,1348],{},"x-oapi-codegen-extra-tags"," extension, which you attach to a field from TypeSpec with the ",[451,1351,1352],{},"@extension"," decorator:",[472,1355,1359],{"className":1356,"code":1357,"language":1358,"meta":477,"style":477},"language-tsp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","model PlaceOrderBody {\n  @extension(\"x-oapi-codegen-extra-tags\", #{ validate: \"required,gt=0\" })\n  quantity: int32;\n}\n","tsp",[451,1360,1361,1371,1397,1410],{"__ignoreMap":477},[481,1362,1363,1366,1369],{"class":483,"line":484},[481,1364,1365],{"class":1081},"model",[481,1367,1368],{"class":495}," PlaceOrderBody",[481,1370,951],{"class":491},[481,1372,1373,1377,1379,1382,1384,1387,1390,1392,1395],{"class":483,"line":543},[481,1374,1376],{"class":1375},"swJcz","  @extension",[481,1378,521],{"class":491},[481,1380,1381],{"class":965},"\"x-oapi-codegen-extra-tags\"",[481,1383,1163],{"class":491},[481,1385,1386],{"class":491}," #{",[481,1388,1389],{"class":803}," validate",[481,1391,1038],{"class":491},[481,1393,1394],{"class":965}," \"required,gt=0\"",[481,1396,884],{"class":491},[481,1398,1399,1402,1404,1407],{"class":483,"line":550},[481,1400,1401],{"class":803},"  quantity",[481,1403,1038],{"class":491},[481,1405,1406],{"class":495}," int32",[481,1408,1409],{"class":491},";\n",[481,1411,1412],{"class":483,"line":590},[481,1413,1004],{"class":491},[447,1415,1416,1417,1420],{},"oapi-codegen emits a ",[451,1418,1419],{},"validate:\"required,gt=0\""," tag onto the generated field; from there, the pipeline above runs on it just the same.",[501,1422,1424],{"id":1423},"custom-tags","Custom tags",[447,1426,1427,1428,1431,1432,1038],{},"Beyond the built-in rule set, you register your own with ",[451,1429,1430],{},"WithRegister"," and make it application-wide with ",[451,1433,898],{},[472,1435,1437],{"className":474,"code":1436,"language":476,"meta":477,"style":477},"v := validate.New(validate.WithRegister(func(v *validator.Validate) {\n    _ = v.RegisterValidation(\"slug\", func(fl validator.FieldLevel) bool {\n        return slugRe.MatchString(fl.Field().String())\n    })\n}))\n\nerr := server.Run(ctx, cfg.Server, register, server.WithValidator(v))\n",[451,1438,1439,1479,1526,1557,1562,1567,1571],{"__ignoreMap":477},[481,1440,1441,1443,1446,1448,1450,1452,1454,1456,1458,1460,1463,1466,1468,1471,1473,1475,1477],{"class":483,"line":484},[481,1442,558],{"class":803},[481,1444,1445],{"class":491},":=",[481,1447,1389],{"class":803},[481,1449,643],{"class":491},[481,1451,663],{"class":517},[481,1453,521],{"class":491},[481,1455,465],{"class":803},[481,1457,643],{"class":491},[481,1459,1430],{"class":517},[481,1461,1462],{"class":491},"(func(",[481,1464,1465],{"class":524},"v",[481,1467,537],{"class":491},[481,1469,1470],{"class":495},"validator",[481,1472,643],{"class":491},[481,1474,709],{"class":495},[481,1476,534],{"class":491},[481,1478,951],{"class":491},[481,1480,1481,1484,1487,1490,1492,1495,1497,1499,1502,1504,1506,1509,1512,1514,1516,1519,1521,1524],{"class":483,"line":543},[481,1482,1483],{"class":803},"    _ ",[481,1485,1486],{"class":491},"=",[481,1488,1489],{"class":803}," v",[481,1491,643],{"class":491},[481,1493,1494],{"class":517},"RegisterValidation",[481,1496,521],{"class":491},[481,1498,1035],{"class":491},[481,1500,1501],{"class":965},"slug",[481,1503,1035],{"class":491},[481,1505,1163],{"class":491},[481,1507,1508],{"class":491}," func(",[481,1510,1511],{"class":524},"fl",[481,1513,640],{"class":495},[481,1515,643],{"class":491},[481,1517,1518],{"class":495},"FieldLevel",[481,1520,534],{"class":491},[481,1522,1523],{"class":582}," bool",[481,1525,951],{"class":491},[481,1527,1528,1531,1534,1536,1539,1541,1543,1545,1548,1551,1554],{"class":483,"line":550},[481,1529,1530],{"class":487},"        return",[481,1532,1533],{"class":803}," slugRe",[481,1535,643],{"class":491},[481,1537,1538],{"class":517},"MatchString",[481,1540,521],{"class":491},[481,1542,1511],{"class":803},[481,1544,643],{"class":491},[481,1546,1547],{"class":517},"Field",[481,1549,1550],{"class":491},"().",[481,1552,1553],{"class":517},"String",[481,1555,1556],{"class":491},"())\n",[481,1558,1559],{"class":483,"line":590},[481,1560,1561],{"class":491},"    })\n",[481,1563,1564],{"class":483,"line":622},[481,1565,1566],{"class":491},"}))\n",[481,1568,1569],{"class":483,"line":627},[481,1570,547],{"emptyLinePlaceholder":546},[481,1572,1573,1576,1578,1581,1583,1586,1588,1591,1593,1596,1598,1600,1602,1605,1607,1609,1611,1614,1616,1618],{"class":483,"line":1127},[481,1574,1575],{"class":803},"err ",[481,1577,1445],{"class":491},[481,1579,1580],{"class":803}," server",[481,1582,643],{"class":491},[481,1584,1585],{"class":517},"Run",[481,1587,521],{"class":491},[481,1589,1590],{"class":803},"ctx",[481,1592,1163],{"class":491},[481,1594,1595],{"class":803}," cfg",[481,1597,643],{"class":491},[481,1599,257],{"class":803},[481,1601,1163],{"class":491},[481,1603,1604],{"class":803}," register",[481,1606,1163],{"class":491},[481,1608,1580],{"class":803},[481,1610,643],{"class":491},[481,1612,1613],{"class":517},"WithValidator",[481,1615,521],{"class":491},[481,1617,1465],{"class":803},[481,1619,1620],{"class":491},"))\n",[447,1622,1623,1624,1627,1628,1630,1631,1633,1634,1636,1637,1640],{},"From then on the ",[451,1625,1626],{},"validate:\"slug\""," tag works everywhere: in the ",[451,1629,842],{}," middleware and in manual ",[451,1632,713],{}," calls. A custom tag's message gets ",[451,1635,725],{},"'s fallback form (",[451,1638,1639],{},"failed rule slug","); if you need something nicer, map it on the client side.",[501,1642,1644],{"id":1643},"customizing-field-names","Customizing field names",[447,1646,1647,1648,1651,1652,1655],{},"The default json-tag naming can be replaced with ",[451,1649,1650],{},"WithTagNameFunc",", for instance when you want the ",[451,1653,1654],{},"form"," tag in errors:",[472,1657,1659],{"className":474,"code":1658,"language":476,"meta":477,"style":477},"v := validate.New(validate.WithTagNameFunc(func(field reflect.StructField) string {\n    name, _, _ := strings.Cut(field.Tag.Get(\"form\"), \",\")\n    return name\n}))\n",[451,1660,1661,1700,1759,1767],{"__ignoreMap":477},[481,1662,1663,1665,1667,1669,1671,1673,1675,1677,1679,1681,1683,1685,1688,1690,1693,1695,1698],{"class":483,"line":484},[481,1664,558],{"class":803},[481,1666,1445],{"class":491},[481,1668,1389],{"class":803},[481,1670,643],{"class":491},[481,1672,663],{"class":517},[481,1674,521],{"class":491},[481,1676,465],{"class":803},[481,1678,643],{"class":491},[481,1680,1650],{"class":517},[481,1682,1462],{"class":491},[481,1684,1150],{"class":524},[481,1686,1687],{"class":495}," reflect",[481,1689,643],{"class":491},[481,1691,1692],{"class":495},"StructField",[481,1694,534],{"class":491},[481,1696,1697],{"class":582}," string",[481,1699,951],{"class":491},[481,1701,1702,1705,1707,1710,1712,1715,1717,1720,1722,1725,1727,1729,1731,1734,1736,1739,1741,1743,1745,1747,1750,1752,1754,1756],{"class":483,"line":543},[481,1703,1704],{"class":803},"    name",[481,1706,1163],{"class":491},[481,1708,1709],{"class":803}," _",[481,1711,1163],{"class":491},[481,1713,1714],{"class":803}," _ ",[481,1716,1445],{"class":491},[481,1718,1719],{"class":803}," strings",[481,1721,643],{"class":491},[481,1723,1724],{"class":517},"Cut",[481,1726,521],{"class":491},[481,1728,1150],{"class":803},[481,1730,643],{"class":491},[481,1732,1733],{"class":803},"Tag",[481,1735,643],{"class":491},[481,1737,1738],{"class":517},"Get",[481,1740,521],{"class":491},[481,1742,1035],{"class":491},[481,1744,1654],{"class":965},[481,1746,1035],{"class":491},[481,1748,1749],{"class":491},"),",[481,1751,492],{"class":491},[481,1753,1163],{"class":965},[481,1755,1035],{"class":491},[481,1757,1758],{"class":491},")\n",[481,1760,1761,1764],{"class":483,"line":550},[481,1762,1763],{"class":487},"    return",[481,1765,1766],{"class":803}," name\n",[481,1768,1769],{"class":483,"line":590},[481,1770,1566],{"class":491},[501,1772,1774],{"id":1773},"outside-the-binding-pipeline","Outside the binding pipeline",[447,1776,1777,1779,1780,1783],{},[451,1778,713],{}," returns the same error shape anywhere, for checking service input, ",[456,1781,1782],{"href":229},"configuration"," or worker payloads:",[472,1785,1787],{"className":474,"code":1786,"language":476,"meta":477,"style":477},"if err := v.Struct(input); err != nil {\n    return err \u002F\u002F already a *httperr.Problem (422), ready for the handler chain to render\n}\n",[451,1788,1789,1823,1832],{"__ignoreMap":477},[481,1790,1791,1794,1797,1799,1801,1803,1805,1807,1810,1813,1815,1818,1821],{"class":483,"line":484},[481,1792,1793],{"class":487},"if",[481,1795,1796],{"class":803}," err ",[481,1798,1445],{"class":491},[481,1800,1489],{"class":803},[481,1802,643],{"class":491},[481,1804,713],{"class":517},[481,1806,521],{"class":491},[481,1808,1809],{"class":803},"input",[481,1811,1812],{"class":491},");",[481,1814,1796],{"class":803},[481,1816,1817],{"class":491},"!=",[481,1819,1820],{"class":491}," nil",[481,1822,951],{"class":491},[481,1824,1825,1827,1829],{"class":483,"line":543},[481,1826,1763],{"class":487},[481,1828,1796],{"class":803},[481,1830,1831],{"class":586},"\u002F\u002F already a *httperr.Problem (422), ready for the handler chain to render\n",[481,1833,1834],{"class":483,"line":550},[481,1835,1004],{"class":491},[447,1837,1838,1839,1842,1843,643],{},"Raw ",[451,1840,1841],{},"validator.ValidationErrors"," (e.g. from a directly used validator instance) translate to the same field-error shape via ",[451,1844,725],{},[501,1846,1848],{"id":1847},"what-struct-tags-dont-cover","What struct tags don't cover",[447,1850,887,1851,1853,1854,1856,1857,1860,1861,1865,1866,1869],{},[451,1852,465],{}," struct tags (",[451,1855,1419],{},") cover syntactic\u002Fshape rules. Database-touching checks (uniqueness, for instance) are ",[672,1858,1859],{},"not"," a tag: they go to the service layer, surfacing as ",[456,1862,1863],{"href":225},[451,1864,357],{}," errors. Authorization is a separate concern again, handled by the ",[456,1867,1868],{"href":99},"policy layer",", not by a validation tag.",[501,1871,1873],{"id":1872},"with-the-kit","With the kit",[447,1875,1876,1878,1879,1881,1882,1884,1885,1887],{},[451,1877,784],{}," (see ",[456,1880,910],{"href":781},") wraps exactly the ",[451,1883,788],{}," documented above: nothing about the validator itself changes when it runs inside a generated project versus a hand-rolled ",[451,1886,761],{}," handler, only where it's invoked.",[501,1889,1891],{"id":1890},"patterns-used","Patterns used",[666,1893,1894,1906],{},[669,1895,1896,555,1899,1902,1903,643],{},[672,1897,1898],{},"Functional options directly on the wrapped type",[451,1900,1901],{},"Option func(*validator.Validate)","): ",[456,1904,238],{"href":1905},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#functional-options",[669,1907,1908,555,1911,677,1913,1915,1916,1918],{},[672,1909,1910],{},"Adapter + translation layer",[451,1912,725],{},[451,1914,1841],{}," → ",[451,1917,721],{},") between the validation library and the wire format.",[501,1920,1922],{"id":1921},"related-pages","Related pages",[666,1924,1925,1938,1945,1955],{},[669,1926,1927,1930,1931,710,1934,1937],{},[456,1928,1929],{"href":391},"httperr: Overview",": the ",[451,1932,1933],{},"Problem",[451,1935,1936],{},"FieldError"," shape this page produces.",[669,1939,1940,1942,1943,643],{},[456,1941,394],{"href":395},": the mapping row that reads a validation ",[451,1944,1933],{},[669,1946,1947,677,1950,732,1952,1954],{},[456,1948,1949],{"href":258},"The server core",[451,1951,842],{},[451,1953,1613],{},", and where validation sits in the middleware chain.",[669,1956,1957,1959],{},[456,1958,98],{"href":99},": authorization, the layer validation deliberately doesn't cover.",[1961,1962,1963],"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}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":477,"searchDepth":543,"depth":543,"links":1965},[1966,1967,1968,1969,1970,1971,1972,1973,1974,1975],{"id":503,"depth":543,"text":504},{"id":755,"depth":543,"text":756},{"id":928,"depth":543,"text":929},{"id":1423,"depth":543,"text":1424},{"id":1643,"depth":543,"text":1644},{"id":1773,"depth":543,"text":1774},{"id":1847,"depth":543,"text":1848},{"id":1872,"depth":543,"text":1873},{"id":1890,"depth":543,"text":1891},{"id":1921,"depth":543,"text":1922},"Struct-tag-based request validation with go-playground\u002Fvalidator, run as strict middleware, with 422 problem responses.","md",null,{},{"icon":402},{"title":399,"description":1976},"lFWCbcXBde-AfvHRlyrwBgI0TmXFe7Obvn4L_tB5ysU",[1984,1986],{"title":394,"path":395,"stem":396,"description":1985,"icon":397,"children":-1},"The httperr package, RFC 9457 problem+json for every error, through one central mapping.",{"title":52,"path":410,"stem":411,"description":1987,"icon":405,"children":-1},"The dbx module, the database-agnostic contract your code depends on, and the two implementations you choose by import.",1785445891832]