[{"data":1,"prerenderedAt":914},["ShallowReactive",2],{"page:\u002Fextensions\u002Fkubb-labs\u002Fplugin-client\u002Fguides\u002Fvalidate-requests-and-responses":3},{"id":4,"frontmatter":5,"title":6,"heading":6,"body":7,"path":913,"source":4},"extensions\u002Fkubb-labs\u002Fplugin-client\u002Fguides\u002Fvalidate-requests-and-responses.md",{},"Validate requests and responses",{"type":8,"value":9,"toc":907},"minimark",[10,22,251,254,342,366,685,701,716,721,728,854,881,888],[11,12,13,17,18,21],"p",{},[14,15,16],"code",{},"@kubb\u002Fplugin-client"," does not validate on its own. With ",[14,19,20],{},"validator"," set, it passes the schemas to your client, which runs them.",[23,24,25],"code-group",{},[26,27,32],"pre",{"language":28,"filename":29,"code":30,"class":31},"typescript","kubb.config.ts","import { defineConfig } from 'kubb\u002Fconfig'\nimport { pluginTs } from '@kubb\u002Fplugin-ts'\nimport { pluginZod } from '@kubb\u002Fplugin-zod'\nimport { pluginClient } from '@kubb\u002Fplugin-client'\n\nexport default defineConfig({\n  input: '.\u002FpetStore.yaml',\n  output: { path: '.\u002Fsrc\u002Fgen' },\n  plugins: [\n    pluginTs(),\n    pluginZod(),\n    pluginClient({ importPath: '..\u002F..\u002F..\u002Fclient', validator: 'zod' }),\n  ],\n})","highlights",[14,33,35,62,63,62,79,62,95,62,111,62,113,62,128,62,145,62,169,62,181,62,194,62,205,62,238,62,247],{"class":34},"language-typescript",[36,37,40,44,48,52,55,58],"span",{"class":38,"style":39},"line","display: inline",[36,41,43],{"style":42},"color:#d73a49;--hls-dark:#f97583","import ",[36,45,47],{"style":46},"color:#005cc5;--hls-dark:#79b8ff","{ ",[36,49,51],{"style":50},"color:#24292e;--hls-dark:#e1e4e8","defineConfig ",[36,53,54],{"style":46},"} ",[36,56,57],{"style":42},"from ",[36,59,61],{"style":60},"color:#032f62;--hls-dark:#9ecbff","'kubb\u002Fconfig'","\n",[36,64,65,67,69,72,74,76],{"class":38,"style":39},[36,66,43],{"style":42},[36,68,47],{"style":46},[36,70,71],{"style":50},"pluginTs ",[36,73,54],{"style":46},[36,75,57],{"style":42},[36,77,78],{"style":60},"'@kubb\u002Fplugin-ts'",[36,80,81,83,85,88,90,92],{"class":38,"style":39},[36,82,43],{"style":42},[36,84,47],{"style":46},[36,86,87],{"style":50},"pluginZod ",[36,89,54],{"style":46},[36,91,57],{"style":42},[36,93,94],{"style":60},"'@kubb\u002Fplugin-zod'",[36,96,97,99,101,104,106,108],{"class":38,"style":39},[36,98,43],{"style":42},[36,100,47],{"style":46},[36,102,103],{"style":50},"pluginClient ",[36,105,54],{"style":46},[36,107,57],{"style":42},[36,109,110],{"style":60},"'@kubb\u002Fplugin-client'",[36,112],{"class":38,"style":39},[36,114,115,118,121,125],{"class":38,"style":39},[36,116,117],{"style":42},"export ",[36,119,120],{"style":42},"default ",[36,122,124],{"style":123},"color:#6f42c1;--hls-dark:#b392f0","defineConfig",[36,126,127],{"style":46},"({",[36,129,130,133,136,139,142],{"class":38,"style":39},[36,131,132],{"style":46},"  ",[36,134,135],{"style":46},"input",[36,137,138],{"style":42},": ",[36,140,141],{"style":60},"'.\u002FpetStore.yaml'",[36,143,144],{"style":42},",",[36,146,147,149,152,154,156,159,161,164,167],{"class":38,"style":39},[36,148,132],{"style":42},[36,150,151],{"style":46},"output",[36,153,138],{"style":42},[36,155,47],{"style":46},[36,157,158],{"style":46},"path",[36,160,138],{"style":42},[36,162,163],{"style":60},"'.\u002Fsrc\u002Fgen' ",[36,165,166],{"style":46},"}",[36,168,144],{"style":42},[36,170,171,173,176,178],{"class":38,"style":39},[36,172,132],{"style":42},[36,174,175],{"style":46},"plugins",[36,177,138],{"style":42},[36,179,180],{"style":46},"[",[36,182,183,186,189,192],{"class":38,"style":39},[36,184,185],{"style":46},"    ",[36,187,188],{"style":123},"pluginTs",[36,190,191],{"style":46},"()",[36,193,144],{"style":42},[36,195,196,198,201,203],{"class":38,"style":39},[36,197,185],{"style":42},[36,199,200],{"style":123},"pluginZod",[36,202,191],{"style":46},[36,204,144],{"style":42},[36,206,207,209,212,215,218,220,223,226,228,230,233,236],{"class":38,"style":39},[36,208,185],{"style":42},[36,210,211],{"style":123},"pluginClient",[36,213,214],{"style":46},"({ ",[36,216,217],{"style":46},"importPath",[36,219,138],{"style":42},[36,221,222],{"style":60},"'..\u002F..\u002F..\u002Fclient'",[36,224,225],{"style":42},", ",[36,227,20],{"style":46},[36,229,138],{"style":42},[36,231,232],{"style":60},"'zod' ",[36,234,235],{"style":46},"})",[36,237,144],{"style":42},[36,239,240,242,245],{"class":38,"style":39},[36,241,132],{"style":42},[36,243,244],{"style":46},"]",[36,246,144],{"style":42},[36,248,249],{"class":38,"style":39},[36,250,235],{"style":46},[11,252,253],{},"Each generated call now includes the schemas:",[26,255,257],{"language":28,"code":256,"class":31},"request({\n  method: 'POST',\n  url: '\u002Fpet',\n  validator: { response: addPetResponseSchema, error: addPetErrorSchema },\n  ...config,\n})",[14,258,259,62,266,62,280,62,294,62,326,62,338],{"class":34},[36,260,261,264],{"class":38,"style":39},[36,262,263],{"style":123},"request",[36,265,127],{"style":46},[36,267,268,270,273,275,278],{"class":38,"style":39},[36,269,132],{"style":46},[36,271,272],{"style":46},"method",[36,274,138],{"style":42},[36,276,277],{"style":60},"'POST'",[36,279,144],{"style":42},[36,281,282,284,287,289,292],{"class":38,"style":39},[36,283,132],{"style":42},[36,285,286],{"style":46},"url",[36,288,138],{"style":42},[36,290,291],{"style":60},"'\u002Fpet'",[36,293,144],{"style":42},[36,295,296,298,300,302,304,307,309,312,314,317,319,322,324],{"class":38,"style":39},[36,297,132],{"style":42},[36,299,20],{"style":46},[36,301,138],{"style":42},[36,303,47],{"style":46},[36,305,306],{"style":46},"response",[36,308,138],{"style":42},[36,310,311],{"style":50},"addPetResponseSchema",[36,313,225],{"style":42},[36,315,316],{"style":46},"error",[36,318,138],{"style":42},[36,320,321],{"style":50},"addPetErrorSchema ",[36,323,166],{"style":46},[36,325,144],{"style":42},[36,327,328,330,333,336],{"class":38,"style":39},[36,329,132],{"style":42},[36,331,332],{"style":42},"...",[36,334,335],{"style":50},"config",[36,337,144],{"style":42},[36,339,340],{"class":38,"style":39},[36,341,235],{"style":46},[11,343,344,345,347,348,351,352,355,356,361,362,365],{},"Add a ",[14,346,20],{}," field to your ",[14,349,350],{},"RequestConfig",", then run the schemas in ",[14,353,354],{},"client",". Zod schemas follow ",[357,358,360],"a",{"href":359},"https:\u002F\u002Fstandardschema.dev","Standard Schema",", so ",[14,363,364],{},"~standard.validate"," works with any compatible library:",[26,367,370],{"language":28,"filename":368,"code":369,"class":31},"src\u002Fclient.ts","type Result = { value: unknown; issues?: undefined } | { issues: ReadonlyArray\u003Cunknown> }\ntype Schema = { '~standard': { validate: (value: unknown) => Result | Promise\u003CResult> } }\n\nexport type RequestConfig = {\n  \u002F\u002F ...\n  validator?: { request?: Schema; response?: Schema; error?: Schema }\n}\n\n\u002F\u002F inside client(), after reading the body\nconst result = await config.validator?.response?.['~standard'].validate(body)\nif (result && 'issues' in result && result.issues) throw new Error('Response validation failed')\nconst validatedBody = result && 'value' in result ? result.value : body",[14,371,372,62,431,62,486,62,488,62,502,62,510,62,548,62,552,62,554,62,559,62,604,62,651],{"class":34},[36,373,374,377,380,383,385,388,390,393,396,399,402,404,407,409,412,414,416,418,421,424,426,429],{"class":38,"style":39},[36,375,376],{"style":42},"type ",[36,378,379],{"style":123},"Result ",[36,381,382],{"style":42},"= ",[36,384,47],{"style":46},[36,386,387],{"style":46},"value",[36,389,138],{"style":50},[36,391,392],{"style":123},"unknown",[36,394,395],{"style":42},"; ",[36,397,398],{"style":46},"issues",[36,400,401],{"style":50},"?",[36,403,138],{"style":42},[36,405,406],{"style":46},"undefined ",[36,408,54],{"style":46},[36,410,411],{"style":42},"| ",[36,413,47],{"style":46},[36,415,398],{"style":46},[36,417,138],{"style":50},[36,419,420],{"style":123},"ReadonlyArray",[36,422,423],{"style":42},"\u003C",[36,425,392],{"style":50},[36,427,428],{"style":42},"> ",[36,430,166],{"style":46},[36,432,433,435,438,440,442,445,447,449,452,454,457,459,461,463,466,469,471,473,476,478,481,483],{"class":38,"style":39},[36,434,376],{"style":42},[36,436,437],{"style":123},"Schema ",[36,439,382],{"style":42},[36,441,47],{"style":46},[36,443,444],{"style":60},"'~standard'",[36,446,138],{"style":42},[36,448,47],{"style":46},[36,450,451],{"style":46},"validate",[36,453,138],{"style":42},[36,455,456],{"style":46},"(",[36,458,387],{"style":50},[36,460,138],{"style":50},[36,462,392],{"style":123},[36,464,465],{"style":46},") ",[36,467,468],{"style":42},"=> ",[36,470,379],{"style":123},[36,472,411],{"style":42},[36,474,475],{"style":123},"Promise",[36,477,423],{"style":42},[36,479,480],{"style":123},"Result",[36,482,428],{"style":42},[36,484,485],{"style":46},"} }",[36,487],{"class":38,"style":39},[36,489,490,492,494,497,499],{"class":38,"style":39},[36,491,117],{"style":42},[36,493,376],{"style":42},[36,495,496],{"style":123},"RequestConfig ",[36,498,382],{"style":42},[36,500,501],{"style":46},"{",[36,503,504,506],{"class":38,"style":39},[36,505,132],{"style":46},[36,507,509],{"style":508},"color:#6a737d;--hls-dark:#6a737d","\u002F\u002F ...",[36,511,512,514,516,518,520,522,524,527,530,532,534,536,538,540,542,544,546],{"class":38,"style":39},[36,513,132],{"style":508},[36,515,20],{"style":46},[36,517,401],{"style":50},[36,519,138],{"style":42},[36,521,47],{"style":46},[36,523,263],{"style":46},[36,525,526],{"style":50},"?: ",[36,528,529],{"style":123},"Schema",[36,531,395],{"style":42},[36,533,306],{"style":46},[36,535,526],{"style":50},[36,537,529],{"style":123},[36,539,395],{"style":42},[36,541,316],{"style":46},[36,543,526],{"style":50},[36,545,437],{"style":123},[36,547,166],{"style":46},[36,549,550],{"class":38,"style":39},[36,551,166],{"style":46},[36,553],{"class":38,"style":39},[36,555,556],{"class":38,"style":39},[36,557,558],{"style":508},"\u002F\u002F inside client(), after reading the body",[36,560,561,564,567,569,572,574,577,579,582,584,586,588,590,592,594,596,598,601],{"class":38,"style":39},[36,562,563],{"style":42},"const ",[36,565,566],{"style":50},"result ",[36,568,382],{"style":42},[36,570,571],{"style":42},"await ",[36,573,335],{"style":50},[36,575,576],{"style":42},".",[36,578,20],{"style":46},[36,580,581],{"style":42},"?.",[36,583,306],{"style":46},[36,585,581],{"style":42},[36,587,180],{"style":46},[36,589,444],{"style":60},[36,591,244],{"style":46},[36,593,576],{"style":42},[36,595,451],{"style":123},[36,597,456],{"style":46},[36,599,600],{"style":50},"body",[36,602,603],{"style":46},")",[36,605,606,609,611,613,616,619,622,624,626,629,631,633,635,638,641,644,646,649],{"class":38,"style":39},[36,607,608],{"style":42},"if ",[36,610,456],{"style":46},[36,612,566],{"style":50},[36,614,615],{"style":42},"&& ",[36,617,618],{"style":60},"'issues' ",[36,620,621],{"style":42},"in ",[36,623,566],{"style":50},[36,625,615],{"style":42},[36,627,628],{"style":50},"result",[36,630,576],{"style":42},[36,632,398],{"style":46},[36,634,465],{"style":46},[36,636,637],{"style":42},"throw ",[36,639,640],{"style":42},"new ",[36,642,643],{"style":123},"Error",[36,645,456],{"style":46},[36,647,648],{"style":60},"'Response validation failed'",[36,650,603],{"style":46},[36,652,653,655,658,660,662,664,667,669,671,674,676,678,681,683],{"class":38,"style":39},[36,654,563],{"style":42},[36,656,657],{"style":50},"validatedBody ",[36,659,382],{"style":42},[36,661,566],{"style":50},[36,663,615],{"style":42},[36,665,666],{"style":60},"'value' ",[36,668,621],{"style":42},[36,670,566],{"style":50},[36,672,673],{"style":42},"? ",[36,675,628],{"style":50},[36,677,576],{"style":42},[36,679,680],{"style":46},"value ",[36,682,138],{"style":42},[36,684,600],{"style":50},[11,686,687,688,691,692,695,696,698,699,576],{},"Return ",[14,689,690],{},"validatedBody"," as ",[14,693,694],{},"data",", so a schema that transforms the value takes effect. Without a response schema, ",[14,697,690],{}," is the original ",[14,700,600],{},[11,702,703,704,707,708,711,712,715],{},"Set ",[14,705,706],{},"validator: { request: 'zod' }"," or ",[14,709,710],{},"{ response: 'zod' }"," to pass one direction only. Without ",[14,713,714],{},"pluginZod()"," in the plugins list, generation stops with an error.",[717,718,720],"h2",{"id":719},"validate-params","Validate params",[11,722,723,724,727],{},"With ",[14,725,726],{},"validator: { params: 'zod' }",", each call also passes the schemas for its path, query, and header params:",[26,729,731],{"language":28,"code":730,"class":31},"request({\n  method: 'PUT',\n  url: '\u002Fevents\u002F{day}',\n  validator: {\n    path: putEventPathSchema,\n    query: putEventQuerySchema,\n    headers: toHeadersStandardSchema(putEventHeadersSchema, putEventHeadersSchema.shape),\n  },\n  ...config,\n})",[14,732,733,62,739,62,752,62,765,62,775,62,788,62,802,62,832,62,840,62,850],{"class":34},[36,734,735,737],{"class":38,"style":39},[36,736,263],{"style":123},[36,738,127],{"style":46},[36,740,741,743,745,747,750],{"class":38,"style":39},[36,742,132],{"style":46},[36,744,272],{"style":46},[36,746,138],{"style":42},[36,748,749],{"style":60},"'PUT'",[36,751,144],{"style":42},[36,753,754,756,758,760,763],{"class":38,"style":39},[36,755,132],{"style":42},[36,757,286],{"style":46},[36,759,138],{"style":42},[36,761,762],{"style":60},"'\u002Fevents\u002F{day}'",[36,764,144],{"style":42},[36,766,767,769,771,773],{"class":38,"style":39},[36,768,132],{"style":42},[36,770,20],{"style":46},[36,772,138],{"style":42},[36,774,501],{"style":46},[36,776,777,779,781,783,786],{"class":38,"style":39},[36,778,185],{"style":46},[36,780,158],{"style":46},[36,782,138],{"style":42},[36,784,785],{"style":50},"putEventPathSchema",[36,787,144],{"style":42},[36,789,790,792,795,797,800],{"class":38,"style":39},[36,791,185],{"style":42},[36,793,794],{"style":46},"query",[36,796,138],{"style":42},[36,798,799],{"style":50},"putEventQuerySchema",[36,801,144],{"style":42},[36,803,804,806,809,811,814,816,819,821,823,825,828,830],{"class":38,"style":39},[36,805,185],{"style":42},[36,807,808],{"style":46},"headers",[36,810,138],{"style":42},[36,812,813],{"style":123},"toHeadersStandardSchema",[36,815,456],{"style":46},[36,817,818],{"style":50},"putEventHeadersSchema",[36,820,225],{"style":42},[36,822,818],{"style":50},[36,824,576],{"style":42},[36,826,827],{"style":46},"shape",[36,829,603],{"style":46},[36,831,144],{"style":42},[36,833,834,836,838],{"class":38,"style":39},[36,835,132],{"style":42},[36,837,166],{"style":46},[36,839,144],{"style":42},[36,841,842,844,846,848],{"class":38,"style":39},[36,843,132],{"style":42},[36,845,332],{"style":42},[36,847,335],{"style":50},[36,849,144],{"style":42},[36,851,852],{"class":38,"style":39},[36,853,235],{"style":46},[11,855,856,857,225,859,861,862,864,865,867,868,870,871,225,874,861,877,880],{},"Add ",[14,858,158],{},[14,860,794],{},", and ",[14,863,808],{}," to the ",[14,866,20],{}," field of your ",[14,869,350],{},". Run them on ",[14,872,873],{},"config.path",[14,875,876],{},"config.query",[14,878,879],{},"config.headers"," before you build the URL and headers, and send the values they return.",[11,882,883,884,887],{},"The path and query schemas strip keys the spec does not declare, so only declared params are sent. Headers your client adds itself, such as ",[14,885,886],{},"Authorization",", are not declared, so the headers schema passes undeclared keys through.",[11,889,890,891,893,894,896,897,900,901,904,905,576],{},"The generated call imports ",[14,892,813],{}," from your ",[14,895,217],{}," module, so that module must export it, or the generated code does not compile. It takes a Standard Schema and the object's declared keys, and returns a Standard Schema that matches the declared headers in any casing and passes undeclared headers through. Copy it from the ",[14,898,899],{},".kubb\u002FstandardSchema.ts"," file that ",[14,902,903],{},"@kubb\u002Fplugin-fetch"," generates. Returning the schema unchanged is not enough: the plain object schema strips undeclared headers such as ",[14,906,886],{},{"title":908,"searchDepth":909,"depth":909,"links":910},"",5,[911],{"id":719,"depth":912,"text":720},2,"\u002Fextensions\u002Fkubb-labs\u002Fplugin-client\u002Fguides\u002Fvalidate-requests-and-responses",1793747914545]