[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-nestjs-weekly-standard-schema-validation":44,"search-suggestions":474,"related-nestjs-weekly-standard-schema-validation":513,"code:ts:true:9icei8":603,"code:ts:true:1smof76":604,"code:ts:true:1u9nd8n":605,"code:ts:true:1lrvn47":606,"code:ts:true:1m3da19":607,"code:json:true:wl2fs":608,"code:ts:true:n721zx":609,"code:ts:true:1e0rom8":610,"code:ts:true:1yijbws":611,"comments-01a0f0ef-ccc8-73e8-9b53-e6ce4c7fe9ff":612},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":74,"playground":76,"body":78,"bodyMd":432,"seo":433,"translationGroupId":435,"series":436,"podcastUrl":48,"verticalId":5,"thread":463,"assessments":465,"translations":470,"quiz":472},"01a0f0ef-ccc8-73e8-9b53-e6ce4c7fe9ff","nestjs-weekly-standard-schema-validation","NestJS 12: Standard Schema Validation Without class-validator",null,"NestJS 12 adds Standard Schema validation for @Body(), @Query(), and @Param() — validate with Zod or Valibot, no class-validator DTO. Cheat sheet included.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-standard-schema-validation.png",14,"2026-10-02T06:11:02.679Z",42,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"nestjs","Nestjs",[64,65,68,71],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},"node","Node",{"slug":69,"name":70,"color":48},"typescript","Typescript",{"slug":72,"name":73,"color":48},"tutorial","Tutorial",{"assessments":75},1,{"slug":46,"title":77},"NestJS Standard Schema validation — pipe playground",{"blocks":79,"version":75},[80,84,87,92,95,104,107,110,113,128,131,134,140,143,147,150,153,156,159,165,168,171,175,178,181,184,187,191,194,197,200,204,207,210,213,216,219,224,227,231,234,237,240,244,247,250,259,262,270,273,276,279,282,285,289,292,296,299,302,305,308,348,352,355,363,366,369,372,375,378,381,384,387,390,393,396,399,402,405,408,414,417,420,423,426],{"id":81,"html":82,"type":83},"b1","\u003Cp>A NestJS 12 upgrade lands, and a \u003Ccode>POST \u002Fusers\u003C\u002Fcode> handler gets a small facelift: instead of a \u003Ccode>CreateUserDto\u003C\u002Fcode> class decorated with \u003Ccode>@IsString()\u003C\u002Fcode> and \u003Ccode>@IsInt()\u003C\u002Fcode>, there&#39;s a Zod schema and \u003Ccode>@Body({ schema: createUserSchema })\u003C\u002Fcode>. It looks like the new, cleaner way to do the exact same job. It compiles. It ships. And the first payload missing a required field sails straight through to the handler, \u003Ccode>undefined\u003C\u002Fcode> and all — no \u003Ccode>400\u003C\u002Fcode>, no thrown exception, nothing in the logs. The schema was real. The validation never ran.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>This is written against \u003Cstrong>\u003Ccode>@nestjs\u002Fcore\u003C\u002Fcode> 12.1.1\u003C\u002Fstrong> (verified 2026-09-29 via \u003Ccode>npm view @nestjs\u002Fcore dist-tags\u003C\u002Fcode>; \u003Ccode>12.0.0\u003C\u002Fcode> shipped 2026-08-27, and \u003Ccode>11.x\u003C\u002Fcode> is now on the \u003Ccode>legacy\u003C\u002Fcode> npm tag). Everything below — \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode>, the \u003Ccode>schema\u003C\u002Fcode> option on \u003Ccode>@Body()\u003C\u002Fcode>\u002F\u003Ccode>@Query()\u003C\u002Fcode>\u002F\u003Ccode>@Param()\u003C\u002Fcode>, and the response-side \u003Ccode>StandardSchemaSerializerInterceptor\u003C\u002Fcode> — is new in the v12 line; it does not exist in v11. The behavior described here was read directly from the published \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode> and \u003Ccode>@nestjs\u002Fcore\u003C\u002Fcode> source for \u003Ccode>12.1.1\u003C\u002Fcode>, not from a blog post about it.\u003C\u002Fp>",{"id":88,"html":89,"text":90,"type":91,"level":31},"b3","What you&#39;ll learn","What you'll learn","heading",{"id":93,"html":94,"type":83},"b4","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":96,"type":97,"items":98,"ordered":18},"b5","list",[99,100,101,102,103],"Explain what actually happens when you attach \u003Ccode>schema\u003C\u002Fcode> to \u003Ccode>@Body()\u003C\u002Fcode>, \u003Ccode>@Query()\u003C\u002Fcode>, or \u003Ccode>@Param()\u003C\u002Fcode> — and why nothing validates until a pipe is registered to read it","Wire up \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> correctly, globally or per-route, next to your existing \u003Ccode>ValidationPipe\u003C\u002Fcode> usage","Predict when the pipe returns the schema&#39;s \u003Cem>transformed\u003C\u002Fem> value versus the original input, and control it with \u003Ccode>transform\u003C\u002Fcode>","Read the exact shape of a Standard Schema validation error and customize it with \u003Ccode>exceptionFactory\u003C\u002Fcode>","Decide, for a given route, whether a Standard Schema or a class-validator DTO is the better fit — and confirm the two can coexist in one app",{"id":105,"html":106,"text":106,"type":91,"level":31},"b6","Who this is for",{"id":108,"html":109,"type":83},"b7","\u003Cp>You&#39;ve written a NestJS controller and used \u003Ccode>class-validator\u003C\u002Fcode> DTOs with \u003Ccode>ValidationPipe\u003C\u002Fcode> at least once. If you haven&#39;t read the \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-request-lifecycle-explained-with-cheat-sheet-227l\">NestJS request lifecycle\u003C\u002Fa> episode of this series, it&#39;s a useful map of exactly where a pipe runs relative to guards and interceptors — but this article is self-contained. Some familiarity with \u003Ca href=\"https:\u002F\u002Fzod.dev\">Zod\u003C\u002Fa> or a similar schema library helps but isn&#39;t required; every example is explained inline.\u003C\u002Fp>",{"id":111,"html":112,"text":112,"type":91,"level":31},"b8","Table of contents",{"id":114,"type":97,"items":115,"ordered":18},"b9",[116,117,118,119,120,121,122,123,124,125,126,127],"\u003Ca href=\"#the-problem-a-schema-that-validates-nothing\">The problem: a schema that validates nothing\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-schema-is-metadata-the-pipe-is-the-reader\">The mental model: schema is metadata, the pipe is the reader\u003C\u002Fa>","\u003Ca href=\"#stage-1-registering-standardschemavalidationpipe\">Stage 1: registering StandardSchemaValidationPipe\u003C\u002Fa>","\u003Ca href=\"#stage-2-schema-on-body-query-and-param\">Stage 2: schema on @Body(), @Query(), and @Param()\u003C\u002Fa>","\u003Ca href=\"#stage-3-transform--coercion-not-just-checking\">Stage 3: transform — coercion, not just checking\u003C\u002Fa>","\u003Ca href=\"#stage-4-reading-and-customizing-validation-errors\">Stage 4: reading and customizing validation errors\u003C\u002Fa>","\u003Ca href=\"#stage-5-the-response-side--standardschemaserializerinterceptor\">Stage 5: the response side — StandardSchemaSerializerInterceptor\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":129,"html":130,"text":130,"type":91,"level":31},"b10","The problem: a schema that validates nothing",{"id":132,"html":133,"type":83},"b11","\u003Cp>Here&#39;s the handler from the intro, in full:\u003C\u002Fp>",{"id":135,"code":136,"type":137,"language":138,"highlight":139},"b12","import { Body, Controller, Post } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\n@Controller(\"users\")\nexport class UsersController {\n  @Post()\n  create(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n    return { created: body };\n  }\n}","code","ts",[],{"id":141,"html":142,"type":83},"b13","\u003Cp>And here&#39;s \u003Ccode>main.ts\u003C\u002Fcode>, unchanged from before the upgrade:\u003C\u002Fp>",{"id":144,"code":145,"type":137,"language":138,"highlight":146},"b14","\u002F\u002F main.ts — looks fine, nothing here reads `schema`\nconst app = await NestFactory.create(AppModule);\napp.useGlobalPipes(); \u002F\u002F never called — no pipes registered at all\nawait app.listen(3000);",[],{"id":148,"html":149,"type":83},"b15","\u003Cp>Send \u003Ccode>POST \u002Fusers\u003C\u002Fcode> with \u003Ccode>{}\u003C\u002Fcode> — no \u003Ccode>name\u003C\u002Fcode>, no \u003Ccode>age\u003C\u002Fcode> — and the handler runs anyway, with \u003Ccode>body\u003C\u002Fcode> equal to \u003Ccode>{}\u003C\u002Fcode>. Nothing threw. \u003Ccode>@Body({ schema: createUserSchema })\u003C\u002Fcode> reads like a self-contained instruction: &quot;validate the body against this schema.&quot; It isn&#39;t one. \u003Ccode>schema\u003C\u002Fcode> is data attached to the parameter&#39;s metadata; something else has to notice it and act. In v11, that &quot;something else&quot; was \u003Ccode>ValidationPipe\u003C\u002Fcode>, and you had to register it. In v12, the equivalent for a Standard Schema is \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode>, and the rule hasn&#39;t changed: register it, or the schema is just an inert object sitting on the metadata, doing nothing.\u003C\u002Fp>",{"id":151,"html":152,"text":152,"type":91,"level":31},"b16","The mental model: schema is metadata, the pipe is the reader",{"id":154,"html":155,"type":83},"b17","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> every parameter decorator that accepts \u003Ccode>{ schema }\u003C\u002Fcode> — \u003Ccode>@Body()\u003C\u002Fcode>, \u003Ccode>@Query()\u003C\u002Fcode>, \u003Ccode>@Param()\u003C\u002Fcode>, \u003Ccode>@RawBody()\u003C\u002Fcode> — does exactly one thing with it: it attaches the schema object to that parameter&#39;s \u003Ccode>ArgumentMetadata\u003C\u002Fcode>, alongside the existing \u003Ccode>type\u003C\u002Fcode> (\u003Ccode>&#39;body&#39; | &#39;query&#39; | &#39;param&#39; | &#39;custom&#39;\u003C\u002Fcode>), \u003Ccode>data\u003C\u002Fcode>, and \u003Ccode>metatype\u003C\u002Fcode> fields. Nest&#39;s router then runs \u003Cem>every pipe configured for that parameter\u003C\u002Fem> — global, controller-level, method-level, and param-level, in that order — and hands each one the same \u003Ccode>ArgumentMetadata\u003C\u002Fcode>, \u003Ccode>schema\u003C\u002Fcode> included. A pipe that has no idea \u003Ccode>schema\u003C\u002Fcode> exists (a hand-written \u003Ccode>ParseIntPipe\u003C\u002Fcode>, your own custom pipe, even \u003Ccode>ValidationPipe\u003C\u002Fcode>) just ignores the extra field and does its own thing. \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> is the one pipe in \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode> that looks at \u003Ccode>metadata.schema\u003C\u002Fcode> and acts on it — and, like every other pipe, it only runs if you put it in the pipe chain.\u003C\u002Fp>",{"id":157,"html":158,"type":83},"b18","\u003Cp>That single fact explains the whole feature:\u003C\u002Fp>",{"id":160,"type":97,"items":161,"ordered":17},"b19",[162,163,164],"\u003Cstrong>\u003Ccode>schema\u003C\u002Fcode> is a request for validation, not validation itself.\u003C\u002Fstrong> Attaching it costs nothing at runtime unless a pipe reads it.","\u003Cstrong>\u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> does the actual work\u003C\u002Fstrong>, by calling the schema&#39;s own \u003Ccode>~standard.validate()\u003C\u002Fcode> method — the one method every \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema\">Standard Schema\u003C\u002Fa>-compatible library (Zod, Valibot, ArkType, and others) implements. NestJS doesn&#39;t depend on any specific schema library to do this; \u003Ccode>@standard-schema\u002Fspec\u003C\u002Fcode> is only a TypeScript type import in \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode>, not a runtime dependency.","\u003Cstrong>The pipe&#39;s job ends at \u003Ccode>transform()\u003C\u002Fcode>.\u003C\u002Fstrong> It either returns a value (the request continues to the handler) or throws (the request stops, same as any other pipe failure — a filter turns it into an HTTP response).",{"id":166,"html":167,"text":167,"type":91,"level":31},"b20","Stage 1: registering StandardSchemaValidationPipe",{"id":169,"html":170,"type":83},"b21","\u003Cp>The smallest version that actually validates something:\u003C\u002Fp>",{"id":172,"code":173,"type":137,"language":138,"highlight":174},"b22","\u002F\u002F main.ts\nimport { NestFactory } from \"@nestjs\u002Fcore\";\nimport { StandardSchemaValidationPipe } from \"@nestjs\u002Fcommon\";\nimport { AppModule } from \".\u002Fapp.module\";\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalPipes(new StandardSchemaValidationPipe());\n  await app.listen(3000);\n}\nbootstrap();",[],{"id":176,"html":177,"type":83},"b23","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> this is the exact same registration shape as \u003Ccode>app.useGlobalPipes(new ValidationPipe())\u003C\u002Fcode> — because it&#39;s the exact same mechanism. \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> is a normal \u003Ccode>PipeTransform\u003C\u002Fcode>; nothing about \u003Ccode>schema\u003C\u002Fcode> makes it auto-register itself. You can also scope it to one controller or one route instead of the whole app, the same way you&#39;d scope any pipe with \u003Ccode>@UsePipes()\u003C\u002Fcode>, if only part of your API has moved to Standard Schema.\u003C\u002Fp>",{"id":179,"html":180,"type":83},"b24","\u003Cp>With the pipe registered, the intro&#39;s \u003Ccode>POST \u002Fusers\u003C\u002Fcode> with \u003Ccode>{}\u003C\u002Fcode> now behaves correctly: the handler never runs, and the caller gets a \u003Ccode>400\u003C\u002Fcode> (covered in \u003Ca href=\"#stage-4-reading-and-customizing-validation-errors\">Stage 4\u003C\u002Fa>).\u003C\u002Fp>",{"id":182,"html":183,"text":183,"type":91,"level":31},"b25","Stage 2: schema on @Body(), @Query(), and @Param()",{"id":185,"html":186,"type":83},"b26","\u003Cp>The \u003Ccode>schema\u003C\u002Fcode> option is available on every parameter decorator that pulls data out of the request: \u003Ccode>@Body()\u003C\u002Fcode>, \u003Ccode>@Query()\u003C\u002Fcode>, \u003Ccode>@Param()\u003C\u002Fcode>, and \u003Ccode>@RawBody()\u003C\u002Fcode>. Attaching it is consistent across all of them — but whether \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> actually validates it by default isn&#39;t; see the \u003Ccode>@RawBody()\u003C\u002Fcode> gotcha below.\u003C\u002Fp>",{"id":188,"code":189,"type":137,"language":138,"highlight":190},"b27","import { Body, Controller, Get, Param, Post, Query } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\nconst listUsersQuerySchema = z.object({\n  page: z.coerce.number().int().min(1).default(1),\n  role: z.enum([\"admin\", \"editor\", \"viewer\"]).optional(),\n});\n\nconst idParamSchema = z.string().uuid();\n\n@Controller(\"users\")\nexport class UsersController {\n  @Post()\n  create(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n    return { created: body };\n  }\n\n  @Get()\n  list(@Query({ schema: listUsersQuerySchema }) query: z.infer\u003Ctypeof listUsersQuerySchema>) {\n    return { page: query.page, role: query.role ?? \"all\" };\n  }\n\n  @Get(\":id\")\n  findOne(@Param(\"id\", { schema: idParamSchema }) id: string) {\n    return { id };\n  }\n}",[],{"id":192,"html":193,"type":83},"b28","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>@Param(&#39;id&#39;, { schema })\u003C\u002Fcode> validates a single named parameter, the same way \u003Ccode>@Param(&#39;id&#39;, ParseUUIDPipe)\u003C\u002Fcode> always has; \u003Ccode>@Param({ schema })\u003C\u002Fcode> with no property name validates the \u003Cem>entire\u003C\u002Fem> params object against a schema shaped like \u003Ccode>{ id: string }\u003C\u002Fcode>. \u003Ccode>@Query()\u003C\u002Fcode> follows the identical pattern. There&#39;s no new mental model per decorator — it&#39;s the same \u003Ccode>{ schema }\u003C\u002Fcode> option everywhere, because it&#39;s the same \u003Ccode>ArgumentMetadata.schema\u003C\u002Fcode> field everywhere.\u003C\u002Fp>",{"id":195,"html":196,"text":196,"type":91,"level":31},"b29","Stage 3: transform — coercion, not just checking",{"id":198,"html":199,"type":83},"b30","\u003Cp>By default, \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> doesn&#39;t just check that a value is valid — it replaces the value with whatever the schema \u003Cem>produces\u003C\u002Fem>. That matters because HTTP bodies and query strings arrive as strings and raw JSON, and a schema like \u003Ccode>z.coerce.number()\u003C\u002Fcode> in the \u003Ccode>listUsersQuerySchema\u003C\u002Fcode> above turns the string \u003Ccode>&quot;2&quot;\u003C\u002Fcode> into the number \u003Ccode>2\u003C\u002Fcode> before your handler ever sees it.\u003C\u002Fp>",{"id":201,"code":202,"type":137,"language":138,"highlight":203},"b31","new StandardSchemaValidationPipe({\n  transform: true, \u002F\u002F default — handler receives the schema's parsed\u002Fcoerced output\n});\n\nnew StandardSchemaValidationPipe({\n  transform: false, \u002F\u002F handler receives the original, unmodified input\n});",[],{"id":205,"html":206,"type":83},"b32","\u003Cp>With the default \u003Ccode>transform: true\u003C\u002Fcode>, \u003Ccode>GET \u002Fusers?page=2\u003C\u002Fcode> gives your handler \u003Ccode>query.page === 2\u003C\u002Fcode>, a real number, not the string \u003Ccode>&quot;2&quot;\u003C\u002Fcode>. Set \u003Ccode>transform: false\u003C\u002Fcode> and the pipe only checks that the value is valid — it hands the handler back the exact object it received, untouched. That&#39;s the right choice when a downstream pipe or your own code needs the raw shape, or when you&#39;re validating something you don&#39;t want silently rewritten (a raw file buffer, for instance).\u003C\u002Fp>",{"id":208,"html":209,"type":83},"b33","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> this mirrors what \u003Ccode>ValidationPipe\u003C\u002Fcode>&#39;s own \u003Ccode>transform\u003C\u002Fcode> option has always done for \u003Ccode>class-validator\u003C\u002Fcode> DTOs — coercion is opt-out, not opt-in, and it&#39;s easy to forget that a \u003Ccode>&quot;42&quot;\u003C\u002Fcode> in the request became a \u003Ccode>42\u003C\u002Fcode> by the time your handler logs it.\u003C\u002Fp>",{"id":211,"html":212,"text":212,"type":91,"level":31},"b34","Stage 4: reading and customizing validation errors",{"id":214,"html":215,"type":83},"b35","\u003Cp>When \u003Ccode>schema[&#39;~standard&#39;].validate()\u003C\u002Fcode> returns issues instead of a value, \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> formats each one into a single string — the issue&#39;s path, joined with \u003Ccode>.\u003C\u002Fcode>, prefixed to its message — and throws an exception built from the full list.\u003C\u002Fp>",{"id":217,"html":218,"type":83},"b36","\u003Cp>Sending \u003Ccode>POST \u002Fusers\u003C\u002Fcode> with \u003Ccode>{ &quot;name&quot;: &quot;&quot;, &quot;age&quot;: &quot;not-a-number&quot; }\u003C\u002Fcode> against the schema from Stage 1 produces something like:\u003C\u002Fp>",{"id":220,"code":221,"type":137,"language":222,"highlight":223},"b37","{\n  \"statusCode\": 400,\n  \"message\": [\n    \"name: String must contain at least 1 character(s)\",\n    \"age: Expected number, received nan\"\n  ],\n  \"error\": \"Bad Request\"\n}","json",[],{"id":225,"html":226,"type":83},"b38","\u003Cp>Both the HTTP status and the exception itself are configurable:\u003C\u002Fp>",{"id":228,"code":229,"type":137,"language":138,"highlight":230},"b39","new StandardSchemaValidationPipe({\n  errorHttpStatusCode: 422, \u002F\u002F default is 400 (Bad Request)\n  exceptionFactory: (issues) =>\n    new UnprocessableEntityException({\n      code: \"VALIDATION_FAILED\",\n      fields: issues.map((i) => ({ path: i.path, message: i.message })),\n    }),\n});",[],{"id":232,"html":233,"type":83},"b40","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>exceptionFactory\u003C\u002Fcode> receives the raw, unformatted issue list from the schema — not the joined strings — so you control the response shape completely. This is the same escape hatch \u003Ccode>ValidationPipe\u003C\u002Fcode> has always offered; only the shape of the input (Standard Schema issues instead of \u003Ccode>class-validator\u003C\u002Fcode> errors) is different.\u003C\u002Fp>",{"id":235,"html":236,"text":236,"type":91,"level":31},"b41","Stage 5: the response side — StandardSchemaSerializerInterceptor",{"id":238,"html":239,"type":83},"b42","\u003Cp>Validation on the way in has a counterpart on the way out. \u003Ccode>StandardSchemaSerializerInterceptor\u003C\u002Fcode> validates (and optionally transforms) what a handler returns, the same role \u003Ccode>ClassSerializerInterceptor\u003C\u002Fcode> plays for \u003Ccode>class-transformer\u003C\u002Fcode>-decorated classes:\u003C\u002Fp>",{"id":241,"code":242,"type":137,"language":138,"highlight":243},"b43","import { Controller, Get, SerializeOptions, UseInterceptors } from \"@nestjs\u002Fcommon\";\nimport { StandardSchemaSerializerInterceptor } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst userResponseSchema = z.object({ id: z.string(), name: z.string() });\n\n@UseInterceptors(StandardSchemaSerializerInterceptor)\n@Controller(\"users\")\nexport class UsersController {\n  @Get(\":id\")\n  @SerializeOptions({ schema: userResponseSchema })\n  findOne() {\n    return { id: \"abc\", name: \"Ada\", passwordHash: \"…\" }; \u002F\u002F stripped down to the schema's shape\n  }\n}",[],{"id":245,"html":246,"type":83},"b44","\u003Cp>This article focuses on the request side, since that&#39;s where the &quot;it looks validated but isn&#39;t&quot; mistake bites — but it&#39;s worth knowing this exists so you&#39;re not reaching for \u003Ccode>ClassSerializerInterceptor\u003C\u002Fcode> out of habit on a route that already validates with a schema.\u003C\u002Fp>",{"id":248,"html":249,"text":249,"type":91,"level":31},"b45","Edge cases and gotchas",{"id":251,"type":97,"items":252,"ordered":18},"b46",[253,254,255,256,257,258],"\u003Cstrong>A \u003Ccode>schema\u003C\u002Fcode> with no registered pipe is silent, not an error.\u003C\u002Fstrong> This is the entire bug from the intro — no warning at boot, no lint rule, just metadata nobody reads. Always confirm \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> is actually in the chain for that route.","\u003Cstrong>Custom parameter decorators are skipped by default.\u003C\u002Fstrong> A parameter built with \u003Ccode>createParamDecorator()\u003C\u002Fcode> has \u003Ccode>ArgumentMetadata.type === &#39;custom&#39;\u003C\u002Fcode>, and the pipe skips those unless you pass \u003Ccode>validateCustomDecorators: true\u003C\u002Fcode>.","\u003Cstrong>\u003Ccode>@RawBody({ schema })\u003C\u002Fcode> gets skipped by that same default, too.\u003C\u002Fstrong> Internally, \u003Ccode>@nestjs\u002Fcore\u003C\u002Fcode>&#39;s \u003Ccode>ParamsTokenFactory.exchangeEnumForString()\u003C\u002Fcode> only maps the body\u002Fquery\u002Fparam paramtypes to the strings \u003Ccode>&#39;body&#39;\u003C\u002Fcode>, \u003Ccode>&#39;query&#39;\u003C\u002Fcode>, and \u003Ccode>&#39;param&#39;\u003C\u002Fcode> — every other paramtype, \u003Ccode>RAW_BODY\u003C\u002Fcode> included, falls through to \u003Ccode>&#39;custom&#39;\u003C\u002Fcode>. So a \u003Ccode>@RawBody({ schema })\u003C\u002Fcode> parameter reaches \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> with \u003Ccode>metadata.type === &#39;custom&#39;\u003C\u002Fcode> and is silently skipped unless the pipe is constructed with \u003Ccode>validateCustomDecorators: true\u003C\u002Fcode> — the exact &quot;attached but never read&quot; failure this article opens with, just on a decorator that looks like it should behave like \u003Ccode>@Body()\u003C\u002Fcode>.","\u003Cstrong>\u003Ccode>class-validator\u003C\u002Fcode> and \u003Ccode>class-transformer\u003C\u002Fcode> are now optional peer dependencies\u003C\u002Fstrong> — \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode>&#39;s own \u003Ccode>package.json\u003C\u002Fcode> lists both under \u003Ccode>peerDependenciesMeta\u003C\u002Fcode> with \u003Ccode>optional: true\u003C\u002Fcode>. A project fully on Standard Schema doesn&#39;t need either installed.","\u003Cstrong>The two approaches coexist without conflict.\u003C\u002Fstrong> Both are ordinary pipes reading different parts of the same \u003Ccode>ArgumentMetadata\u003C\u002Fcode>; registering both globally is safe, since a class-based DTO parameter has no \u003Ccode>schema\u003C\u002Fcode> and a Standard Schema parameter has no class \u003Ccode>metatype\u003C\u002Fcode>.","\u003Cstrong>Values are sanitized before validation.\u003C\u002Fstrong> The pipe strips dangerous prototype-pollution keys (\u003Ccode>__proto__\u003C\u002Fcode> and friends) from the input before handing it to the schema.",{"id":260,"html":261,"text":261,"type":91,"level":31},"b47","Best practices",{"id":263,"type":97,"items":264,"ordered":18},"b48",[265,266,267,268,269],"\u003Cstrong>Register \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> globally once\u003C\u002Fstrong>, the same way you&#39;d register \u003Ccode>ValidationPipe\u003C\u002Fcode> — don&#39;t scatter \u003Ccode>@UsePipes()\u003C\u002Fcode> per route unless it genuinely needs different options.","\u003Cstrong>Reach for a Standard Schema library when validation logic is shared outside NestJS\u003C\u002Fstrong> — a Zod schema also used on a frontend form, for instance — since the schema itself is portable in a way a \u003Ccode>class-validator\u003C\u002Fcode> DTO class isn&#39;t.","\u003Cstrong>Keep \u003Ccode>class-validator\u003C\u002Fcode> DTOs where decorators already carry the app&#39;s conventions\u003C\u002Fstrong> (Swagger \u003Ccode>@ApiProperty()\u003C\u002Fcode>, serialization groups). Migrate per-module, not per-app.","\u003Cstrong>Set \u003Ccode>transform: false\u003C\u002Fcode> explicitly when the raw input matters elsewhere in the pipe chain\u003C\u002Fstrong> — don&#39;t assume the default coercion is a no-op.","\u003Cstrong>Write a custom \u003Ccode>exceptionFactory\u003C\u002Fcode> once, at the global registration\u003C\u002Fstrong>, if your API has a house error shape.",{"id":271,"html":272,"text":272,"type":91,"level":31},"b49","FAQ",{"id":274,"html":275,"text":275,"type":91,"level":43},"b50","Does Standard Schema validation replace ValidationPipe and class-validator?",{"id":277,"html":278,"type":83},"b51","\u003Cp>No. Both ship in \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode> 12.x. \u003Ccode>class-validator\u003C\u002Fcode> and \u003Ccode>class-transformer\u003C\u002Fcode> moved to optional peer dependencies, so a Standard-Schema-only project can drop them — but \u003Ccode>ValidationPipe\u003C\u002Fcode> itself hasn&#39;t been deprecated.\u003C\u002Fp>",{"id":280,"html":281,"text":281,"type":91,"level":43},"b52","Do I have to install Zod for this to work?",{"id":283,"html":284,"type":83},"b53","\u003Cp>No. \u003Ccode>@nestjs\u002Fcommon\u003C\u002Fcode> only imports \u003Ccode>@standard-schema\u002Fspec\u003C\u002Fcode> as a TypeScript type, not as a runtime dependency, so it has no opinion on which schema library you use. Any library implementing the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema\">Standard Schema\u003C\u002Fa> spec — Zod, Valibot, and ArkType are the best-known ones — works with \u003Ccode>{ schema }\u003C\u002Fcode> the same way.\u003C\u002Fp>",{"id":286,"html":287,"text":288,"type":91,"level":43},"b54","Why does \u003Ccode>@Body({ schema })\u003C\u002Fcode> compile and run but never validate anything?","Why does @Body({ schema }) compile and run but never validate anything?",{"id":290,"html":291,"type":83},"b55","\u003Cp>Almost always because \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> isn&#39;t in the pipe chain for that route — see \u003Ca href=\"#stage-1-registering-standardschemavalidationpipe\">Stage 1\u003C\u002Fa>. \u003Ccode>schema\u003C\u002Fcode> is metadata the decorator attaches; nothing enforces it by itself.\u003C\u002Fp>",{"id":293,"html":294,"text":295,"type":91,"level":43},"b56","Can I use \u003Ccode>schema\u003C\u002Fcode> with a custom decorator built from \u003Ccode>createParamDecorator()\u003C\u002Fcode>?","Can I use schema with a custom decorator built from createParamDecorator()?",{"id":297,"html":298,"type":83},"b57","\u003Cp>Only if you construct the pipe with \u003Ccode>validateCustomDecorators: true\u003C\u002Fcode>. By default, \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> treats any parameter of type \u003Ccode>&#39;custom&#39;\u003C\u002Fcode> as out of scope, since custom decorators can return arbitrary shapes the author may not intend to run through a schema at all.\u003C\u002Fp>",{"id":300,"html":301,"text":301,"type":91,"level":43},"b58","What does a nested validation error look like?",{"id":303,"html":304,"type":83},"b59","\u003Cp>Each issue&#39;s \u003Ccode>path\u003C\u002Fcode> array is joined with \u003Ccode>.\u003C\u002Fcode> and prefixed to its message — a failure on \u003Ccode>address.zip\u003C\u002Fcode> in a nested object becomes the string \u003Ccode>&quot;address.zip: Invalid input&quot;\u003C\u002Fcode> in the default error list. Pass your own \u003Ccode>exceptionFactory\u003C\u002Fcode> if you need the raw, unflattened \u003Ccode>path\u003C\u002Fcode> array instead of the joined string.\u003C\u002Fp>",{"id":306,"html":307,"text":307,"type":91,"level":31},"b60","Cheat sheet",{"id":309,"head":310,"rows":314,"type":347},"b61",[311,312,313],"Task","Code","Notes",[315,319,323,327,331,335,339,343],[316,317,318],"Attach a schema to a param","\u003Ccode>@Body({ schema: userSchema })\u003C\u002Fcode>","Also works on \u003Ccode>@Query()\u003C\u002Fcode>, \u003Ccode>@Param()\u003C\u002Fcode>, \u003Ccode>@RawBody()\u003C\u002Fcode> — but \u003Ccode>@RawBody()\u003C\u002Fcode> needs \u003Ccode>validateCustomDecorators: true\u003C\u002Fcode> to actually validate",[320,321,322],"Register the pipe globally","\u003Ccode>app.useGlobalPipes(new StandardSchemaValidationPipe())\u003C\u002Fcode>","Required — \u003Ccode>schema\u003C\u002Fcode> alone validates nothing",[324,325,326],"Validate a single named param","\u003Ccode>@Param(&#39;id&#39;, { schema: idSchema })\u003C\u002Fcode>","Validates just that property",[328,329,330],"Validate the whole params object","\u003Ccode>@Param({ schema: paramsSchema })\u003C\u002Fcode>","No property name argument",[332,333,334],"Keep the raw input (no coercion)","\u003Ccode>new StandardSchemaValidationPipe({ transform: false })\u003C\u002Fcode>","Default is \u003Ccode>transform: true\u003C\u002Fcode>",[336,337,338],"Custom error shape\u002Fstatus","\u003Ccode>new StandardSchemaValidationPipe({ exceptionFactory, errorHttpStatusCode })\u003C\u002Fcode>","\u003Ccode>exceptionFactory\u003C\u002Fcode> receives raw, unformatted issues",[340,341,342],"Validate a \u003Ccode>createParamDecorator()\u003C\u002Fcode> value","\u003Ccode>new StandardSchemaValidationPipe({ validateCustomDecorators: true })\u003C\u002Fcode>","Default is \u003Ccode>false\u003C\u002Fcode> — custom decorators are skipped",[344,345,346],"Validate the response instead of the request","\u003Ccode>@UseInterceptors(StandardSchemaSerializerInterceptor)\u003C\u002Fcode> + \u003Ccode>@SerializeOptions({ schema })\u003C\u002Fcode>","The output-side counterpart","table",{"id":349,"code":350,"type":137,"language":138,"highlight":351},"b62","\u002F\u002F main.ts\nconst app = await NestFactory.create(AppModule);\napp.useGlobalPipes(new StandardSchemaValidationPipe()); \u002F\u002F required — schema is inert without this\n\n\u002F\u002F users.controller.ts\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\n@Post()\ncreate(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n  return { created: body }; \u002F\u002F body.age is a number, coerced from the JSON string\n}",[],{"id":353,"html":354,"text":354,"type":91,"level":31},"b63","Key takeaways",{"id":356,"type":97,"items":357,"ordered":18},"b64",[358,359,360,361,362],"\u003Ccode>{ schema }\u003C\u002Fcode> on \u003Ccode>@Body()\u003C\u002Fcode>, \u003Ccode>@Query()\u003C\u002Fcode>, \u003Ccode>@Param()\u003C\u002Fcode>, and \u003Ccode>@RawBody()\u003C\u002Fcode> only attaches metadata — it does nothing until a pipe reads it, exactly like a class-based DTO does nothing without \u003Ccode>ValidationPipe\u003C\u002Fcode>.","\u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode>, new in \u003Ccode>@nestjs\u002Fcore\u003C\u002Fcode> 12.x, is that pipe, and it must be registered globally or per-route yourself.","\u003Ccode>transform\u003C\u002Fcode> defaults to \u003Ccode>true\u003C\u002Fcode>: the handler gets the schema&#39;s parsed\u002Fcoerced output, not necessarily the original request value.","Any \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema\">Standard Schema\u003C\u002Fa>-compatible library works — NestJS depends on the spec, not on Zod specifically — and \u003Ccode>class-validator\u003C\u002Fcode>\u002F\u003Ccode>class-transformer\u003C\u002Fcode> are now optional, so the two validation styles can run side by side or replace each other module by module.","\u003Ccode>StandardSchemaSerializerInterceptor\u003C\u002Fcode> is the matching piece for outgoing responses, the same role \u003Ccode>ClassSerializerInterceptor\u003C\u002Fcode> plays today.",{"id":364,"html":365,"text":365,"type":91,"level":31},"b65","Ending",{"id":367,"html":368,"type":83},"b66","\u003Cp>The bug in the intro wasn&#39;t a broken schema or a NestJS defect — it was a decorator that reads like an instruction and is actually a label. \u003Ccode>{ schema: createUserSchema }\u003C\u002Fcode> tells Nest &quot;here is a schema, should anyone ask&quot; — it doesn&#39;t tell Nest to ask. That&#39;s the same shape of mistake \u003Ccode>new RolesGuard(...)\u003C\u002Fcode> outside the DI container makes, or a \u003Ccode>ValidationPipe\u003C\u002Fcode> nobody registered: a piece that looks complete on its own only works because something else, wired up separately, is watching for it. Once \u003Ccode>StandardSchemaValidationPipe\u003C\u002Fcode> is in the chain, the rest — coercion, custom error shapes, response-side serialization — is just configuration on a pipe you already understand.\u003C\u002Fp>",{"id":370,"html":371,"type":83},"b67","\u003Cp>Have you moved a route to Standard Schema validation yet, or are you holding the line with \u003Ccode>class-validator\u003C\u002Fcode> for now? Drop your reasoning in the comments.\u003C\u002Fp>",{"id":373,"html":374,"type":83},"b68","\u003C!-- playground:start -->",{"id":376,"html":377,"text":377,"type":91,"level":31},"b69","🎮 Try it yourself",{"id":379,"html":380,"type":83},"b70","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-standard-schema-validation\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":382,"html":383,"type":83},"b71","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":385,"html":386,"type":83},"b72","\u003C!-- playground:end -->",{"id":388,"html":389,"type":83},"b73","\u003C!-- quiz:start -->",{"id":391,"html":392,"text":392,"type":91,"level":31},"b74","🧠 Test yourself",{"id":394,"html":395,"type":83},"b75","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-standard-schema-validation\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":397,"html":398,"type":83},"b76","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":400,"html":401,"type":83},"b77","\u003C!-- quiz:end -->",{"id":403,"html":404,"type":83},"b78","\u003C!-- related:start -->",{"id":406,"html":407,"text":407,"type":91,"level":31},"b79","📚 Read next",{"id":409,"type":97,"items":410,"ordered":18},"b80",[411,412,413],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides\">NestJS Testing Module: Provider Overrides (with Cheat Sheet)\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-guards-canactivate-reflector\">NestJS Guards: CanActivate, ExecutionContext &amp; Reflector\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-request-lifecycle\">NestJS Request Lifecycle Explained (with Cheat Sheet)\u003C\u002Fa>",{"id":415,"html":416,"type":83},"b81","\u003C!-- related:end -->",{"id":418,"type":419},"b82","divider",{"id":421,"html":422,"type":83},"b83","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":424,"html":425,"type":83},"b84","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":427,"type":97,"items":428,"ordered":18},"b85",[429,430,431],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","A NestJS 12 upgrade lands, and a `POST \u002Fusers` handler gets a small facelift: instead of a `CreateUserDto` class decorated with `@IsString()` and `@IsInt()`, there's a Zod schema and `@Body({ schema: createUserSchema })`. It looks like the new, cleaner way to do the exact same job. It compiles. It ships. And the first payload missing a required field sails straight through to the handler, `undefined` and all — no `400`, no thrown exception, nothing in the logs. The schema was real. The validation never ran.\n\nThis is written against **`@nestjs\u002Fcore` 12.1.1** (verified 2026-09-29 via `npm view @nestjs\u002Fcore dist-tags`; `12.0.0` shipped 2026-08-27, and `11.x` is now on the `legacy` npm tag). Everything below — `StandardSchemaValidationPipe`, the `schema` option on `@Body()`\u002F`@Query()`\u002F`@Param()`, and the response-side `StandardSchemaSerializerInterceptor` — is new in the v12 line; it does not exist in v11. The behavior described here was read directly from the published `@nestjs\u002Fcommon` and `@nestjs\u002Fcore` source for `12.1.1`, not from a blog post about it.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain what actually happens when you attach `schema` to `@Body()`, `@Query()`, or `@Param()` — and why nothing validates until a pipe is registered to read it\n- Wire up `StandardSchemaValidationPipe` correctly, globally or per-route, next to your existing `ValidationPipe` usage\n- Predict when the pipe returns the schema's *transformed* value versus the original input, and control it with `transform`\n- Read the exact shape of a Standard Schema validation error and customize it with `exceptionFactory`\n- Decide, for a given route, whether a Standard Schema or a class-validator DTO is the better fit — and confirm the two can coexist in one app\n\n## Who this is for\n\nYou've written a NestJS controller and used `class-validator` DTOs with `ValidationPipe` at least once. If you haven't read the [NestJS request lifecycle](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-request-lifecycle-explained-with-cheat-sheet-227l) episode of this series, it's a useful map of exactly where a pipe runs relative to guards and interceptors — but this article is self-contained. Some familiarity with [Zod](https:\u002F\u002Fzod.dev) or a similar schema library helps but isn't required; every example is explained inline.\n\n## Table of contents\n\n- [The problem: a schema that validates nothing](#the-problem-a-schema-that-validates-nothing)\n- [The mental model: schema is metadata, the pipe is the reader](#the-mental-model-schema-is-metadata-the-pipe-is-the-reader)\n- [Stage 1: registering StandardSchemaValidationPipe](#stage-1-registering-standardschemavalidationpipe)\n- [Stage 2: schema on @Body(), @Query(), and @Param()](#stage-2-schema-on-body-query-and-param)\n- [Stage 3: transform — coercion, not just checking](#stage-3-transform--coercion-not-just-checking)\n- [Stage 4: reading and customizing validation errors](#stage-4-reading-and-customizing-validation-errors)\n- [Stage 5: the response side — StandardSchemaSerializerInterceptor](#stage-5-the-response-side--standardschemaserializerinterceptor)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## The problem: a schema that validates nothing\n\nHere's the handler from the intro, in full:\n\n```typescript\nimport { Body, Controller, Post } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\n@Controller(\"users\")\nexport class UsersController {\n  @Post()\n  create(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n    return { created: body };\n  }\n}\n```\n\nAnd here's `main.ts`, unchanged from before the upgrade:\n\n```typescript\n\u002F\u002F main.ts — looks fine, nothing here reads `schema`\nconst app = await NestFactory.create(AppModule);\napp.useGlobalPipes(); \u002F\u002F never called — no pipes registered at all\nawait app.listen(3000);\n```\n\nSend `POST \u002Fusers` with `{}` — no `name`, no `age` — and the handler runs anyway, with `body` equal to `{}`. Nothing threw. `@Body({ schema: createUserSchema })` reads like a self-contained instruction: \"validate the body against this schema.\" It isn't one. `schema` is data attached to the parameter's metadata; something else has to notice it and act. In v11, that \"something else\" was `ValidationPipe`, and you had to register it. In v12, the equivalent for a Standard Schema is `StandardSchemaValidationPipe`, and the rule hasn't changed: register it, or the schema is just an inert object sitting on the metadata, doing nothing.\n\n## The mental model: schema is metadata, the pipe is the reader\n\n**The mental model:** every parameter decorator that accepts `{ schema }` — `@Body()`, `@Query()`, `@Param()`, `@RawBody()` — does exactly one thing with it: it attaches the schema object to that parameter's `ArgumentMetadata`, alongside the existing `type` (`'body' | 'query' | 'param' | 'custom'`), `data`, and `metatype` fields. Nest's router then runs *every pipe configured for that parameter* — global, controller-level, method-level, and param-level, in that order — and hands each one the same `ArgumentMetadata`, `schema` included. A pipe that has no idea `schema` exists (a hand-written `ParseIntPipe`, your own custom pipe, even `ValidationPipe`) just ignores the extra field and does its own thing. `StandardSchemaValidationPipe` is the one pipe in `@nestjs\u002Fcommon` that looks at `metadata.schema` and acts on it — and, like every other pipe, it only runs if you put it in the pipe chain.\n\nThat single fact explains the whole feature:\n\n1. **`schema` is a request for validation, not validation itself.** Attaching it costs nothing at runtime unless a pipe reads it.\n2. **`StandardSchemaValidationPipe` does the actual work**, by calling the schema's own `~standard.validate()` method — the one method every [Standard Schema](https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema)-compatible library (Zod, Valibot, ArkType, and others) implements. NestJS doesn't depend on any specific schema library to do this; `@standard-schema\u002Fspec` is only a TypeScript type import in `@nestjs\u002Fcommon`, not a runtime dependency.\n3. **The pipe's job ends at `transform()`.** It either returns a value (the request continues to the handler) or throws (the request stops, same as any other pipe failure — a filter turns it into an HTTP response).\n\n## Stage 1: registering StandardSchemaValidationPipe\n\nThe smallest version that actually validates something:\n\n```typescript\n\u002F\u002F main.ts\nimport { NestFactory } from \"@nestjs\u002Fcore\";\nimport { StandardSchemaValidationPipe } from \"@nestjs\u002Fcommon\";\nimport { AppModule } from \".\u002Fapp.module\";\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.useGlobalPipes(new StandardSchemaValidationPipe());\n  await app.listen(3000);\n}\nbootstrap();\n```\n\n**Key concept:** this is the exact same registration shape as `app.useGlobalPipes(new ValidationPipe())` — because it's the exact same mechanism. `StandardSchemaValidationPipe` is a normal `PipeTransform`; nothing about `schema` makes it auto-register itself. You can also scope it to one controller or one route instead of the whole app, the same way you'd scope any pipe with `@UsePipes()`, if only part of your API has moved to Standard Schema.\n\nWith the pipe registered, the intro's `POST \u002Fusers` with `{}` now behaves correctly: the handler never runs, and the caller gets a `400` (covered in [Stage 4](#stage-4-reading-and-customizing-validation-errors)).\n\n## Stage 2: schema on @Body(), @Query(), and @Param()\n\nThe `schema` option is available on every parameter decorator that pulls data out of the request: `@Body()`, `@Query()`, `@Param()`, and `@RawBody()`. Attaching it is consistent across all of them — but whether `StandardSchemaValidationPipe` actually validates it by default isn't; see the `@RawBody()` gotcha below.\n\n```typescript\nimport { Body, Controller, Get, Param, Post, Query } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\nconst listUsersQuerySchema = z.object({\n  page: z.coerce.number().int().min(1).default(1),\n  role: z.enum([\"admin\", \"editor\", \"viewer\"]).optional(),\n});\n\nconst idParamSchema = z.string().uuid();\n\n@Controller(\"users\")\nexport class UsersController {\n  @Post()\n  create(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n    return { created: body };\n  }\n\n  @Get()\n  list(@Query({ schema: listUsersQuerySchema }) query: z.infer\u003Ctypeof listUsersQuerySchema>) {\n    return { page: query.page, role: query.role ?? \"all\" };\n  }\n\n  @Get(\":id\")\n  findOne(@Param(\"id\", { schema: idParamSchema }) id: string) {\n    return { id };\n  }\n}\n```\n\n**Key concept:** `@Param('id', { schema })` validates a single named parameter, the same way `@Param('id', ParseUUIDPipe)` always has; `@Param({ schema })` with no property name validates the *entire* params object against a schema shaped like `{ id: string }`. `@Query()` follows the identical pattern. There's no new mental model per decorator — it's the same `{ schema }` option everywhere, because it's the same `ArgumentMetadata.schema` field everywhere.\n\n## Stage 3: transform — coercion, not just checking\n\nBy default, `StandardSchemaValidationPipe` doesn't just check that a value is valid — it replaces the value with whatever the schema *produces*. That matters because HTTP bodies and query strings arrive as strings and raw JSON, and a schema like `z.coerce.number()` in the `listUsersQuerySchema` above turns the string `\"2\"` into the number `2` before your handler ever sees it.\n\n```typescript\nnew StandardSchemaValidationPipe({\n  transform: true, \u002F\u002F default — handler receives the schema's parsed\u002Fcoerced output\n});\n\nnew StandardSchemaValidationPipe({\n  transform: false, \u002F\u002F handler receives the original, unmodified input\n});\n```\n\nWith the default `transform: true`, `GET \u002Fusers?page=2` gives your handler `query.page === 2`, a real number, not the string `\"2\"`. Set `transform: false` and the pipe only checks that the value is valid — it hands the handler back the exact object it received, untouched. That's the right choice when a downstream pipe or your own code needs the raw shape, or when you're validating something you don't want silently rewritten (a raw file buffer, for instance).\n\n**Key concept:** this mirrors what `ValidationPipe`'s own `transform` option has always done for `class-validator` DTOs — coercion is opt-out, not opt-in, and it's easy to forget that a `\"42\"` in the request became a `42` by the time your handler logs it.\n\n## Stage 4: reading and customizing validation errors\n\nWhen `schema['~standard'].validate()` returns issues instead of a value, `StandardSchemaValidationPipe` formats each one into a single string — the issue's path, joined with `.`, prefixed to its message — and throws an exception built from the full list.\n\nSending `POST \u002Fusers` with `{ \"name\": \"\", \"age\": \"not-a-number\" }` against the schema from Stage 1 produces something like:\n\n```json\n{\n  \"statusCode\": 400,\n  \"message\": [\n    \"name: String must contain at least 1 character(s)\",\n    \"age: Expected number, received nan\"\n  ],\n  \"error\": \"Bad Request\"\n}\n```\n\nBoth the HTTP status and the exception itself are configurable:\n\n```typescript\nnew StandardSchemaValidationPipe({\n  errorHttpStatusCode: 422, \u002F\u002F default is 400 (Bad Request)\n  exceptionFactory: (issues) =>\n    new UnprocessableEntityException({\n      code: \"VALIDATION_FAILED\",\n      fields: issues.map((i) => ({ path: i.path, message: i.message })),\n    }),\n});\n```\n\n**Key concept:** `exceptionFactory` receives the raw, unformatted issue list from the schema — not the joined strings — so you control the response shape completely. This is the same escape hatch `ValidationPipe` has always offered; only the shape of the input (Standard Schema issues instead of `class-validator` errors) is different.\n\n## Stage 5: the response side — StandardSchemaSerializerInterceptor\n\nValidation on the way in has a counterpart on the way out. `StandardSchemaSerializerInterceptor` validates (and optionally transforms) what a handler returns, the same role `ClassSerializerInterceptor` plays for `class-transformer`-decorated classes:\n\n```typescript\nimport { Controller, Get, SerializeOptions, UseInterceptors } from \"@nestjs\u002Fcommon\";\nimport { StandardSchemaSerializerInterceptor } from \"@nestjs\u002Fcommon\";\nimport { z } from \"zod\";\n\nconst userResponseSchema = z.object({ id: z.string(), name: z.string() });\n\n@UseInterceptors(StandardSchemaSerializerInterceptor)\n@Controller(\"users\")\nexport class UsersController {\n  @Get(\":id\")\n  @SerializeOptions({ schema: userResponseSchema })\n  findOne() {\n    return { id: \"abc\", name: \"Ada\", passwordHash: \"…\" }; \u002F\u002F stripped down to the schema's shape\n  }\n}\n```\n\nThis article focuses on the request side, since that's where the \"it looks validated but isn't\" mistake bites — but it's worth knowing this exists so you're not reaching for `ClassSerializerInterceptor` out of habit on a route that already validates with a schema.\n\n## Edge cases and gotchas\n\n- **A `schema` with no registered pipe is silent, not an error.** This is the entire bug from the intro — no warning at boot, no lint rule, just metadata nobody reads. Always confirm `StandardSchemaValidationPipe` is actually in the chain for that route.\n- **Custom parameter decorators are skipped by default.** A parameter built with `createParamDecorator()` has `ArgumentMetadata.type === 'custom'`, and the pipe skips those unless you pass `validateCustomDecorators: true`.\n- **`@RawBody({ schema })` gets skipped by that same default, too.** Internally, `@nestjs\u002Fcore`'s `ParamsTokenFactory.exchangeEnumForString()` only maps the body\u002Fquery\u002Fparam paramtypes to the strings `'body'`, `'query'`, and `'param'` — every other paramtype, `RAW_BODY` included, falls through to `'custom'`. So a `@RawBody({ schema })` parameter reaches `StandardSchemaValidationPipe` with `metadata.type === 'custom'` and is silently skipped unless the pipe is constructed with `validateCustomDecorators: true` — the exact \"attached but never read\" failure this article opens with, just on a decorator that looks like it should behave like `@Body()`.\n- **`class-validator` and `class-transformer` are now optional peer dependencies** — `@nestjs\u002Fcommon`'s own `package.json` lists both under `peerDependenciesMeta` with `optional: true`. A project fully on Standard Schema doesn't need either installed.\n- **The two approaches coexist without conflict.** Both are ordinary pipes reading different parts of the same `ArgumentMetadata`; registering both globally is safe, since a class-based DTO parameter has no `schema` and a Standard Schema parameter has no class `metatype`.\n- **Values are sanitized before validation.** The pipe strips dangerous prototype-pollution keys (`__proto__` and friends) from the input before handing it to the schema.\n\n## Best practices\n\n- **Register `StandardSchemaValidationPipe` globally once**, the same way you'd register `ValidationPipe` — don't scatter `@UsePipes()` per route unless it genuinely needs different options.\n- **Reach for a Standard Schema library when validation logic is shared outside NestJS** — a Zod schema also used on a frontend form, for instance — since the schema itself is portable in a way a `class-validator` DTO class isn't.\n- **Keep `class-validator` DTOs where decorators already carry the app's conventions** (Swagger `@ApiProperty()`, serialization groups). Migrate per-module, not per-app.\n- **Set `transform: false` explicitly when the raw input matters elsewhere in the pipe chain** — don't assume the default coercion is a no-op.\n- **Write a custom `exceptionFactory` once, at the global registration**, if your API has a house error shape.\n\n## FAQ\n\n### Does Standard Schema validation replace ValidationPipe and class-validator?\n\nNo. Both ship in `@nestjs\u002Fcommon` 12.x. `class-validator` and `class-transformer` moved to optional peer dependencies, so a Standard-Schema-only project can drop them — but `ValidationPipe` itself hasn't been deprecated.\n\n### Do I have to install Zod for this to work?\n\nNo. `@nestjs\u002Fcommon` only imports `@standard-schema\u002Fspec` as a TypeScript type, not as a runtime dependency, so it has no opinion on which schema library you use. Any library implementing the [Standard Schema](https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema) spec — Zod, Valibot, and ArkType are the best-known ones — works with `{ schema }` the same way.\n\n### Why does `@Body({ schema })` compile and run but never validate anything?\n\nAlmost always because `StandardSchemaValidationPipe` isn't in the pipe chain for that route — see [Stage 1](#stage-1-registering-standardschemavalidationpipe). `schema` is metadata the decorator attaches; nothing enforces it by itself.\n\n### Can I use `schema` with a custom decorator built from `createParamDecorator()`?\n\nOnly if you construct the pipe with `validateCustomDecorators: true`. By default, `StandardSchemaValidationPipe` treats any parameter of type `'custom'` as out of scope, since custom decorators can return arbitrary shapes the author may not intend to run through a schema at all.\n\n### What does a nested validation error look like?\n\nEach issue's `path` array is joined with `.` and prefixed to its message — a failure on `address.zip` in a nested object becomes the string `\"address.zip: Invalid input\"` in the default error list. Pass your own `exceptionFactory` if you need the raw, unflattened `path` array instead of the joined string.\n\n## Cheat sheet\n\n| Task | Code | Notes |\n| --- | --- | --- |\n| Attach a schema to a param | `@Body({ schema: userSchema })` | Also works on `@Query()`, `@Param()`, `@RawBody()` — but `@RawBody()` needs `validateCustomDecorators: true` to actually validate |\n| Register the pipe globally | `app.useGlobalPipes(new StandardSchemaValidationPipe())` | Required — `schema` alone validates nothing |\n| Validate a single named param | `@Param('id', { schema: idSchema })` | Validates just that property |\n| Validate the whole params object | `@Param({ schema: paramsSchema })` | No property name argument |\n| Keep the raw input (no coercion) | `new StandardSchemaValidationPipe({ transform: false })` | Default is `transform: true` |\n| Custom error shape\u002Fstatus | `new StandardSchemaValidationPipe({ exceptionFactory, errorHttpStatusCode })` | `exceptionFactory` receives raw, unformatted issues |\n| Validate a `createParamDecorator()` value | `new StandardSchemaValidationPipe({ validateCustomDecorators: true })` | Default is `false` — custom decorators are skipped |\n| Validate the response instead of the request | `@UseInterceptors(StandardSchemaSerializerInterceptor)` + `@SerializeOptions({ schema })` | The output-side counterpart |\n\n```typescript\n\u002F\u002F main.ts\nconst app = await NestFactory.create(AppModule);\napp.useGlobalPipes(new StandardSchemaValidationPipe()); \u002F\u002F required — schema is inert without this\n\n\u002F\u002F users.controller.ts\nconst createUserSchema = z.object({\n  name: z.string().min(1),\n  age: z.coerce.number().int().positive(),\n});\n\n@Post()\ncreate(@Body({ schema: createUserSchema }) body: z.infer\u003Ctypeof createUserSchema>) {\n  return { created: body }; \u002F\u002F body.age is a number, coerced from the JSON string\n}\n```\n\n## Key takeaways\n\n- `{ schema }` on `@Body()`, `@Query()`, `@Param()`, and `@RawBody()` only attaches metadata — it does nothing until a pipe reads it, exactly like a class-based DTO does nothing without `ValidationPipe`.\n- `StandardSchemaValidationPipe`, new in `@nestjs\u002Fcore` 12.x, is that pipe, and it must be registered globally or per-route yourself.\n- `transform` defaults to `true`: the handler gets the schema's parsed\u002Fcoerced output, not necessarily the original request value.\n- Any [Standard Schema](https:\u002F\u002Fgithub.com\u002Fstandard-schema\u002Fstandard-schema)-compatible library works — NestJS depends on the spec, not on Zod specifically — and `class-validator`\u002F`class-transformer` are now optional, so the two validation styles can run side by side or replace each other module by module.\n- `StandardSchemaSerializerInterceptor` is the matching piece for outgoing responses, the same role `ClassSerializerInterceptor` plays today.\n\n## Ending\n\nThe bug in the intro wasn't a broken schema or a NestJS defect — it was a decorator that reads like an instruction and is actually a label. `{ schema: createUserSchema }` tells Nest \"here is a schema, should anyone ask\" — it doesn't tell Nest to ask. That's the same shape of mistake `new RolesGuard(...)` outside the DI container makes, or a `ValidationPipe` nobody registered: a piece that looks complete on its own only works because something else, wired up separately, is watching for it. Once `StandardSchemaValidationPipe` is in the chain, the rest — coercion, custom error shapes, response-side serialization — is just configuration on a pipe you already understand.\n\nHave you moved a route to Standard Schema validation yet, or are you holding the line with `class-validator` for now? Drop your reasoning in the comments.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-standard-schema-validation\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-standard-schema-validation\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [NestJS Testing Module: Provider Overrides (with Cheat Sheet)](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides)\n- [NestJS Guards: CanActivate, ExecutionContext & Reflector](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-guards-canactivate-reflector)\n- [NestJS Request Lifecycle Explained (with Cheat Sheet)](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-request-lifecycle)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":434,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-standard-schema-validation","01a0f0ef-ccc9-726d-a5e9-c18b18af7b4d",{"name":437,"part":438,"total":438,"items":439},"NestJS Deep Dive",6,[440,445,449,454,458,462],{"slug":441,"title":442,"publishedAt":443,"readingMinutes":444},"nestjs-weekly-request-lifecycle","NestJS Request Lifecycle Explained (with Cheat Sheet)","2026-08-28T17:01:41.941Z",13,{"slug":446,"title":447,"publishedAt":448,"readingMinutes":51},"nestjs-weekly-dependency-injection-providers-scopes","NestJS Dependency Injection Explained (with Cheat Sheet)","2026-09-13T08:40:18.434Z",{"slug":450,"title":451,"publishedAt":452,"readingMinutes":453},"nestjs-weekly-module-encapsulation-exports","NestJS Module Encapsulation Explained (with Cheat Sheet)","2026-09-13T08:40:50.070Z",15,{"slug":455,"title":456,"publishedAt":457,"readingMinutes":444},"nestjs-weekly-testing-provider-overrides","NestJS Testing Module: Provider Overrides (with Cheat Sheet)","2026-09-18T17:23:03.842Z",{"slug":459,"title":460,"publishedAt":461,"readingMinutes":51},"nestjs-weekly-guards-canactivate-reflector","NestJS Guards: CanActivate, ExecutionContext & Reflector","2026-09-26T12:25:39.001Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":464,"locked":18},"01a0f0ef-cd62-70c5-8dfb-0e513ffeb26e",[466],{"id":467,"slug":46,"title":47,"_count":468},"01a0f0ef-cd7f-75f8-84f7-642c3e25dcf9",{"questions":469},8,[471],{"locale":13,"slug":46},{"id":467,"slug":46,"title":47,"_count":473,"questionCount":469},{"questions":469},[475,479,483,487,489,493,495,498,501,505,506,509],{"slug":476,"name":477,"articles":478},"webdev","Webdev",119,{"slug":480,"name":481,"articles":482},"javascript","Javascript",98,{"slug":484,"name":485,"articles":486},"frontend","Frontend",78,{"slug":72,"name":73,"articles":488},45,{"slug":490,"name":491,"articles":492},"css","Css",39,{"slug":69,"name":70,"articles":494},18,{"slug":496,"name":497,"articles":51},"performance","Performance",{"slug":499,"name":500,"articles":444},"react","React",{"slug":502,"name":503,"articles":504},"browser","Browser",11,{"slug":66,"name":67,"articles":504},{"slug":507,"name":508,"articles":469},"html","Html",{"slug":510,"name":511,"articles":512},"accessibility","Accessibility",7,{"items":514,"meta":602},[515,527,542,557,572,587],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":516,"likeCount":19,"commentCount":19,"author":517,"vertical":518,"topic":519,"tags":520,"_count":525,"playground":526,"hasQuiz":17,"hasPlayground":17},43,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[521,522,523,524],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":46},{"id":528,"slug":459,"title":460,"subtitle":48,"excerpt":529,"coverUrl":530,"locale":13,"readingMinutes":51,"publishedAt":461,"viewCount":531,"likeCount":19,"commentCount":19,"author":532,"vertical":533,"topic":534,"tags":535,"_count":540,"playground":541,"hasQuiz":17,"hasPlayground":17},"01a0ddae-48ac-73c6-bdee-1420cdfb17c2","A complete guide to NestJS guards: how CanActivate, ExecutionContext, and the Reflector decide who reaches your route handler, with a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-guards-canactivate-reflector.png",204,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[536,537,538,539],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":459},{"id":543,"slug":455,"title":456,"subtitle":48,"excerpt":544,"coverUrl":545,"locale":13,"readingMinutes":444,"publishedAt":457,"viewCount":546,"likeCount":19,"commentCount":19,"author":547,"vertical":548,"topic":549,"tags":550,"_count":555,"playground":556,"hasQuiz":17,"hasPlayground":17},"01a0a630-c856-72d3-bae5-3339e38a8564","A complete guide to NestJS's testing module: how overrideProvider really wires mocks, why get() fails on scoped providers, and using useMocker.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-testing-provider-overrides.png",343,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[551,552,553,554],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":455},{"id":558,"slug":450,"title":451,"subtitle":48,"excerpt":559,"coverUrl":560,"locale":13,"readingMinutes":453,"publishedAt":452,"viewCount":561,"likeCount":19,"commentCount":19,"author":562,"vertical":563,"topic":564,"tags":565,"_count":570,"playground":571,"hasQuiz":17,"hasPlayground":17},"01a0821b-4733-762d-bf96-b68c7eb140c0","How NestJS module boundaries actually work: what exports really cross, why @Global() isn't a shortcut, and how forwardRef breaks circular module dependencies.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-module-encapsulation-exports.png",304,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[566,567,568,569],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":450},{"id":573,"slug":446,"title":447,"subtitle":48,"excerpt":574,"coverUrl":575,"locale":13,"readingMinutes":51,"publishedAt":448,"viewCount":576,"likeCount":19,"commentCount":19,"author":577,"vertical":578,"topic":579,"tags":580,"_count":585,"playground":586,"hasQuiz":17,"hasPlayground":17},"01a05e06-53b7-7415-9cd3-05504882514b","How the NestJS DI container resolves providers: tokens, module scope, singleton vs request vs transient, and why 'singleton' can still mean two instances.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-dependency-injection-providers-scopes.png",330,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[581,582,583,584],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":446},{"id":588,"slug":441,"title":442,"subtitle":48,"excerpt":589,"coverUrl":590,"locale":13,"readingMinutes":444,"publishedAt":443,"viewCount":591,"likeCount":19,"commentCount":19,"author":592,"vertical":593,"topic":594,"tags":595,"_count":600,"playground":601,"hasQuiz":17,"hasPlayground":17},"01a03cdb-22ef-748a-b555-8071c7a905e6","A complete guide to the NestJS request lifecycle: the exact order middleware, guards, interceptors, pipes, and filters run, and why it matters.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-request-lifecycle.png",504,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[596,597,598,599],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":441},{"page":75,"perPage":512,"total":438,"totalPages":75},"\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Body\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Post\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcommon\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> z \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"zod\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">object\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">min\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  age\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coerce\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">int\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">positive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n@\u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"users\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">class\u003C\u002Fspan> \u003Cspan class=\"shj-class\">UsersController\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  @\u003Cspan class=\"shj-class\">Post\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>@\u003Cspan class=\"shj-class\">Body\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> body\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>infer\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">typeof\u003C\u002Fspan> createUserSchema\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> created\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> body \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F main.ts — looks fine, nothing here reads `schema`\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> app \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-class\">NestFactory\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-class\">AppModule\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\napp\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">useGlobalPipes\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F never called — no pipes registered at all\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> app\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">listen\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">3000\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F main.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">NestFactory\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcore\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcommon\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">AppModule\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\".\u002Fapp.module\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">bootstrap\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> app \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-class\">NestFactory\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-class\">AppModule\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  app\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">useGlobalPipes\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> app\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">listen\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">3000\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-func\">bootstrap\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003Cdiv>25\u003C\u002Fdiv>\u003Cdiv>26\u003C\u002Fdiv>\u003Cdiv>27\u003C\u002Fdiv>\u003Cdiv>28\u003C\u002Fdiv>\u003Cdiv>29\u003C\u002Fdiv>\u003Cdiv>30\u003C\u002Fdiv>\u003Cdiv>31\u003C\u002Fdiv>\u003Cdiv>32\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Body\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Get\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Param\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Post\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Query\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcommon\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> z \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"zod\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">object\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">min\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  age\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coerce\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">int\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">positive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> listUsersQuerySchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">object\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  page\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coerce\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">int\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">min\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">default\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  role\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">enum\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"admin\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"editor\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"viewer\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">optional\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> idParamSchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">uuid\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n@\u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"users\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">class\u003C\u002Fspan> \u003Cspan class=\"shj-class\">UsersController\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  @\u003Cspan class=\"shj-class\">Post\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>@\u003Cspan class=\"shj-class\">Body\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> body\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>infer\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">typeof\u003C\u002Fspan> createUserSchema\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> created\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> body \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  @\u003Cspan class=\"shj-class\">Get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">list\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>@\u003Cspan class=\"shj-class\">Query\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> listUsersQuerySchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> query\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>infer\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">typeof\u003C\u002Fspan> listUsersQuerySchema\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> page\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> query\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>page\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> role\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> query\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>role \u003Cspan class=\"shj-oper\">??\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"all\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  @\u003Cspan class=\"shj-class\">Get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\":id\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">findOne\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>@\u003Cspan class=\"shj-class\">Param\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"id\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> idParamSchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> id\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  transform\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F default — handler receives the schema's parsed\u002Fcoerced output\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  transform\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F handler receives the original, unmodified input\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-json shj-multiline\" data-lang=\"json\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">\"statusCode\"\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">400\u003C\u002Fspan>,\n  \u003Cspan class=\"shj-var\">\"message\"\u003C\u002Fspan>: \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\n    \u003Cspan class=\"shj-str\">\"name: String must contain at least 1 character(s)\"\u003C\u002Fspan>,\n    \u003Cspan class=\"shj-str\">\"age: Expected number, received nan\"\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>,\n  \u003Cspan class=\"shj-var\">\"error\"\u003C\u002Fspan>: \u003Cspan class=\"shj-str\">\"Bad Request\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  errorHttpStatusCode\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">422\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-cmnt\">\u002F\u002F default is 400 (Bad Request)\n\u003C\u002Fspan>  exceptionFactory\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>issues\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">UnprocessableEntityException\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n      code\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"VALIDATION_FAILED\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n      fields\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> issues\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> path\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> i\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>path\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> message\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> i\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>message \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Get\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">SerializeOptions\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-class\">UseInterceptors\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcommon\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaSerializerInterceptor\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"@nestjs\u002Fcommon\"\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> z \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"zod\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> userResponseSchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">object\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n@\u003Cspan class=\"shj-class\">UseInterceptors\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-class\">StandardSchemaSerializerInterceptor\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n@\u003Cspan class=\"shj-class\">Controller\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"users\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">class\u003C\u002Fspan> \u003Cspan class=\"shj-class\">UsersController\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  @\u003Cspan class=\"shj-class\">Get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\":id\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  @\u003Cspan class=\"shj-class\">SerializeOptions\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> userResponseSchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n  \u003Cspan class=\"shj-func\">findOne\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"abc\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Ada\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> passwordHash\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"…\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F stripped down to the schema's shape\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F main.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> app \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-class\">NestFactory\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-class\">AppModule\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\napp\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">useGlobalPipes\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">StandardSchemaValidationPipe\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F required — schema is inert without this\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F users.controller.ts\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">object\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">min\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  age\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>coerce\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">int\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">positive\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n@\u003Cspan class=\"shj-class\">Post\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-func\">create\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>@\u003Cspan class=\"shj-class\">Body\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> schema\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> createUserSchema \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> body\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> z\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>infer\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">typeof\u003C\u002Fspan> createUserSchema\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> created\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> body \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F body.age is a number, coerced from the JSON string\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":613},[]]