[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"dev-laravel-caching-from-memory-leaks-to-fast-apis":3,"dev-all":340},{"id":4,"title":5,"body":6,"category":325,"date":326,"description":327,"extension":328,"meta":329,"navigation":53,"path":330,"readTime":331,"seo":332,"stem":333,"tags":334,"__hash__":339},"dev\u002Fdev\u002Flaravel-caching-from-memory-leaks-to-fast-apis.md","Laravel Caching: From Memory Leaks to fast APIs",{"type":7,"value":8,"toc":318},"minimark",[9,13,18,79,90,94,132,142,146,201,205,239,243,309,314],[10,11,12],"p",{},"Laravel gets called \"slow\" when people skip caching patterns that work. These strategies cut API response times from seconds to milliseconds.",[14,15,17],"h2",{"id":16},"cacheremember-cacheput","Cache::remember() > Cache::put()",[19,20,25],"pre",{"className":21,"code":22,"language":23,"meta":24,"style":24},"language-php shiki shiki-themes github-light github-dark","\u002F\u002F ❌ Wrong: Manual expiration management\nCache::put('user-posts', $posts, now()->addHour());\n$posts = Cache::get('user-posts');\n\n\u002F\u002F ✅ Right: Automatic refresh\n$posts = Cache::remember(\"user.{$user->id}.posts\", now()->addHour(), function () use ($user) {\n    return $user->posts()->with('comments')->get();\n});\n","php","",[26,27,28,36,42,48,55,61,67,73],"code",{"__ignoreMap":24},[29,30,33],"span",{"class":31,"line":32},"line",1,[29,34,35],{},"\u002F\u002F ❌ Wrong: Manual expiration management\n",[29,37,39],{"class":31,"line":38},2,[29,40,41],{},"Cache::put('user-posts', $posts, now()->addHour());\n",[29,43,45],{"class":31,"line":44},3,[29,46,47],{},"$posts = Cache::get('user-posts');\n",[29,49,51],{"class":31,"line":50},4,[29,52,54],{"emptyLinePlaceholder":53},true,"\n",[29,56,58],{"class":31,"line":57},5,[29,59,60],{},"\u002F\u002F ✅ Right: Automatic refresh\n",[29,62,64],{"class":31,"line":63},6,[29,65,66],{},"$posts = Cache::remember(\"user.{$user->id}.posts\", now()->addHour(), function () use ($user) {\n",[29,68,70],{"class":31,"line":69},7,[29,71,72],{},"    return $user->posts()->with('comments')->get();\n",[29,74,76],{"class":31,"line":75},8,[29,77,78],{},"});\n",[10,80,81,85,86,89],{},[82,83,84],"strong",{},"Why",": ",[26,87,88],{},"remember()"," guarantees fresh data on cache miss. No stale data bugs.",[14,91,93],{"id":92},"cache-tags-for-related-data","Cache Tags for Related Data",[19,95,97],{"className":21,"code":96,"language":23,"meta":24,"style":24},"\u002F\u002F Tag entire user data tree\nCache::tags(['user', \"user:{$user->id}\"])->rememberForever(\"user:{$user->id}:profile\", function () use ($user) {\n    return $user->load(['posts', 'posts.comments']);\n});\n\n\u002F\u002F Invalidate everything when user updates\nCache::tags(['user', \"user:{$user->id}\"])->flush();\n",[26,98,99,104,109,114,118,122,127],{"__ignoreMap":24},[29,100,101],{"class":31,"line":32},[29,102,103],{},"\u002F\u002F Tag entire user data tree\n",[29,105,106],{"class":31,"line":38},[29,107,108],{},"Cache::tags(['user', \"user:{$user->id}\"])->rememberForever(\"user:{$user->id}:profile\", function () use ($user) {\n",[29,110,111],{"class":31,"line":44},[29,112,113],{},"    return $user->load(['posts', 'posts.comments']);\n",[29,115,116],{"class":31,"line":50},[29,117,78],{},[29,119,120],{"class":31,"line":57},[29,121,54],{"emptyLinePlaceholder":53},[29,123,124],{"class":31,"line":63},[29,125,126],{},"\u002F\u002F Invalidate everything when user updates\n",[29,128,129],{"class":31,"line":69},[29,130,131],{},"Cache::tags(['user', \"user:{$user->id}\"])->flush();\n",[10,133,134,137,138,141],{},[82,135,136],{},"Result",": One ",[26,139,140],{},"flush()"," clears profile + posts + comments. No hunting cache keys.",[14,143,145],{"id":144},"routemodel-caching-that-survives-production","Route\u002FModel Caching That Survives Production",[19,147,149],{"className":21,"code":148,"language":23,"meta":24,"style":24},"\u002F\u002F Route caching (CLI only)\nRoute::middleware('cache.headers:public;max_age=3600')->get('\u002Fapi\u002Fposts', function () {\n    return Cache::remember('public-posts', 3600, fn() => Post::published()->get();\n});\n\n\u002F\u002F Model query caching\nPost::published()\n    ->remember(3600)\n    ->orderBy('published_at')\n    ->get();\n",[26,150,151,156,161,166,170,174,179,184,189,195],{"__ignoreMap":24},[29,152,153],{"class":31,"line":32},[29,154,155],{},"\u002F\u002F Route caching (CLI only)\n",[29,157,158],{"class":31,"line":38},[29,159,160],{},"Route::middleware('cache.headers:public;max_age=3600')->get('\u002Fapi\u002Fposts', function () {\n",[29,162,163],{"class":31,"line":44},[29,164,165],{},"    return Cache::remember('public-posts', 3600, fn() => Post::published()->get();\n",[29,167,168],{"class":31,"line":50},[29,169,78],{},[29,171,172],{"class":31,"line":57},[29,173,54],{"emptyLinePlaceholder":53},[29,175,176],{"class":31,"line":63},[29,177,178],{},"\u002F\u002F Model query caching\n",[29,180,181],{"class":31,"line":69},[29,182,183],{},"Post::published()\n",[29,185,186],{"class":31,"line":75},[29,187,188],{},"    ->remember(3600)\n",[29,190,192],{"class":31,"line":191},9,[29,193,194],{},"    ->orderBy('published_at')\n",[29,196,198],{"class":31,"line":197},10,[29,199,200],{},"    ->get();\n",[14,202,204],{"id":203},"redis-json-for-complex-objects","Redis JSON for Complex Objects",[19,206,208],{"className":21,"code":207,"language":23,"meta":24,"style":24},"\u002F\u002F Store JSON directly (faster than serialization)\nCache::store('redis')->put('dashboard-stats', json_encode($stats), 1800);\n\n\u002F\u002F Driver-specific: Redis JSON commands\nRedis::connection()->json()->set(\"user:{$user->id}\", '$', $user->toArray());\nRedis::connection()->json()->get(\"user:{$user->id}\");\n",[26,209,210,215,220,224,229,234],{"__ignoreMap":24},[29,211,212],{"class":31,"line":32},[29,213,214],{},"\u002F\u002F Store JSON directly (faster than serialization)\n",[29,216,217],{"class":31,"line":38},[29,218,219],{},"Cache::store('redis')->put('dashboard-stats', json_encode($stats), 1800);\n",[29,221,222],{"class":31,"line":44},[29,223,54],{"emptyLinePlaceholder":53},[29,225,226],{"class":31,"line":50},[29,227,228],{},"\u002F\u002F Driver-specific: Redis JSON commands\n",[29,230,231],{"class":31,"line":57},[29,232,233],{},"Redis::connection()->json()->set(\"user:{$user->id}\", '$', $user->toArray());\n",[29,235,236],{"class":31,"line":63},[29,237,238],{},"Redis::connection()->json()->get(\"user:{$user->id}\");\n",[14,240,242],{"id":241},"the-production-checklist","The Production Checklist",[19,244,246],{"className":21,"code":245,"language":23,"meta":24,"style":24},"\u002F\u002F .env\nCACHE_STORE=redis\nREDIS_CLIENT=phpredis\nSESSION_DRIVER=redis\n\n\u002F\u002F config\u002Fcache.php\n'tags' => [\n    'redis' => [\n        'driver' => 'redis',\n        'connection' => 'cache',\n    ],\n],\n",[26,247,248,253,258,263,268,272,277,282,287,292,297,303],{"__ignoreMap":24},[29,249,250],{"class":31,"line":32},[29,251,252],{},"\u002F\u002F .env\n",[29,254,255],{"class":31,"line":38},[29,256,257],{},"CACHE_STORE=redis\n",[29,259,260],{"class":31,"line":44},[29,261,262],{},"REDIS_CLIENT=phpredis\n",[29,264,265],{"class":31,"line":50},[29,266,267],{},"SESSION_DRIVER=redis\n",[29,269,270],{"class":31,"line":57},[29,271,54],{"emptyLinePlaceholder":53},[29,273,274],{"class":31,"line":63},[29,275,276],{},"\u002F\u002F config\u002Fcache.php\n",[29,278,279],{"class":31,"line":69},[29,280,281],{},"'tags' => [\n",[29,283,284],{"class":31,"line":75},[29,285,286],{},"    'redis' => [\n",[29,288,289],{"class":31,"line":191},[29,290,291],{},"        'driver' => 'redis',\n",[29,293,294],{"class":31,"line":197},[29,295,296],{},"        'connection' => 'cache',\n",[29,298,300],{"class":31,"line":299},11,[29,301,302],{},"    ],\n",[29,304,306],{"class":31,"line":305},12,[29,307,308],{},"],\n",[10,310,311],{},[82,312,313],{},"Cache smart. Ship fast.",[315,316,317],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":24,"searchDepth":38,"depth":38,"links":319},[320,321,322,323,324],{"id":16,"depth":38,"text":17},{"id":92,"depth":38,"text":93},{"id":144,"depth":38,"text":145},{"id":203,"depth":38,"text":204},{"id":241,"depth":38,"text":242},"Dev","2026-04-19","Practical caching patterns that ship faster than your uncached endpoints.","md",{},"\u002Fdev\u002Flaravel-caching-from-memory-leaks-to-fast-apis","10 min",{"title":5,"description":327},"dev\u002Flaravel-caching-from-memory-leaks-to-fast-apis",[335,336,337,338],"Laravel","Caching","Performance","Redis","X5GW_GstX-4dBwpdsLAPvI3zkuuZ0d_7hL7FL8G2Jpg",[341,576],{"id":4,"title":5,"body":342,"category":325,"date":326,"description":327,"extension":328,"meta":573,"navigation":53,"path":330,"readTime":331,"seo":574,"stem":333,"tags":575,"__hash__":339},{"type":7,"value":343,"toc":566},[344,346,348,384,390,392,424,430,432,476,478,506,508,560,564],[10,345,12],{},[14,347,17],{"id":16},[19,349,350],{"className":21,"code":22,"language":23,"meta":24,"style":24},[26,351,352,356,360,364,368,372,376,380],{"__ignoreMap":24},[29,353,354],{"class":31,"line":32},[29,355,35],{},[29,357,358],{"class":31,"line":38},[29,359,41],{},[29,361,362],{"class":31,"line":44},[29,363,47],{},[29,365,366],{"class":31,"line":50},[29,367,54],{"emptyLinePlaceholder":53},[29,369,370],{"class":31,"line":57},[29,371,60],{},[29,373,374],{"class":31,"line":63},[29,375,66],{},[29,377,378],{"class":31,"line":69},[29,379,72],{},[29,381,382],{"class":31,"line":75},[29,383,78],{},[10,385,386,85,388,89],{},[82,387,84],{},[26,389,88],{},[14,391,93],{"id":92},[19,393,394],{"className":21,"code":96,"language":23,"meta":24,"style":24},[26,395,396,400,404,408,412,416,420],{"__ignoreMap":24},[29,397,398],{"class":31,"line":32},[29,399,103],{},[29,401,402],{"class":31,"line":38},[29,403,108],{},[29,405,406],{"class":31,"line":44},[29,407,113],{},[29,409,410],{"class":31,"line":50},[29,411,78],{},[29,413,414],{"class":31,"line":57},[29,415,54],{"emptyLinePlaceholder":53},[29,417,418],{"class":31,"line":63},[29,419,126],{},[29,421,422],{"class":31,"line":69},[29,423,131],{},[10,425,426,137,428,141],{},[82,427,136],{},[26,429,140],{},[14,431,145],{"id":144},[19,433,434],{"className":21,"code":148,"language":23,"meta":24,"style":24},[26,435,436,440,444,448,452,456,460,464,468,472],{"__ignoreMap":24},[29,437,438],{"class":31,"line":32},[29,439,155],{},[29,441,442],{"class":31,"line":38},[29,443,160],{},[29,445,446],{"class":31,"line":44},[29,447,165],{},[29,449,450],{"class":31,"line":50},[29,451,78],{},[29,453,454],{"class":31,"line":57},[29,455,54],{"emptyLinePlaceholder":53},[29,457,458],{"class":31,"line":63},[29,459,178],{},[29,461,462],{"class":31,"line":69},[29,463,183],{},[29,465,466],{"class":31,"line":75},[29,467,188],{},[29,469,470],{"class":31,"line":191},[29,471,194],{},[29,473,474],{"class":31,"line":197},[29,475,200],{},[14,477,204],{"id":203},[19,479,480],{"className":21,"code":207,"language":23,"meta":24,"style":24},[26,481,482,486,490,494,498,502],{"__ignoreMap":24},[29,483,484],{"class":31,"line":32},[29,485,214],{},[29,487,488],{"class":31,"line":38},[29,489,219],{},[29,491,492],{"class":31,"line":44},[29,493,54],{"emptyLinePlaceholder":53},[29,495,496],{"class":31,"line":50},[29,497,228],{},[29,499,500],{"class":31,"line":57},[29,501,233],{},[29,503,504],{"class":31,"line":63},[29,505,238],{},[14,507,242],{"id":241},[19,509,510],{"className":21,"code":245,"language":23,"meta":24,"style":24},[26,511,512,516,520,524,528,532,536,540,544,548,552,556],{"__ignoreMap":24},[29,513,514],{"class":31,"line":32},[29,515,252],{},[29,517,518],{"class":31,"line":38},[29,519,257],{},[29,521,522],{"class":31,"line":44},[29,523,262],{},[29,525,526],{"class":31,"line":50},[29,527,267],{},[29,529,530],{"class":31,"line":57},[29,531,54],{"emptyLinePlaceholder":53},[29,533,534],{"class":31,"line":63},[29,535,276],{},[29,537,538],{"class":31,"line":69},[29,539,281],{},[29,541,542],{"class":31,"line":75},[29,543,286],{},[29,545,546],{"class":31,"line":191},[29,547,291],{},[29,549,550],{"class":31,"line":197},[29,551,296],{},[29,553,554],{"class":31,"line":299},[29,555,302],{},[29,557,558],{"class":31,"line":305},[29,559,308],{},[10,561,562],{},[82,563,313],{},[315,565,317],{},{"title":24,"searchDepth":38,"depth":38,"links":567},[568,569,570,571,572],{"id":16,"depth":38,"text":17},{"id":92,"depth":38,"text":93},{"id":144,"depth":38,"text":145},{"id":203,"depth":38,"text":204},{"id":241,"depth":38,"text":242},{},{"title":5,"description":327},[335,336,337,338],{"id":577,"title":578,"body":579,"category":1208,"date":326,"description":1209,"extension":328,"meta":1210,"navigation":53,"path":1211,"readTime":1212,"seo":1213,"stem":1214,"tags":1215,"__hash__":1218},"dev\u002Fdev\u002Ftypescript-advanced-patterns.md","TypeScript Patterns That Actually Ship",{"type":7,"value":580,"toc":1203},[581,589,593,773,780,784,1047,1054,1058,1190,1195,1200],[10,582,583,584,588],{},"TypeScript gets called verbose when people fight the type system instead of working ",[585,586,587],"em",{},"with"," it. These three patterns flipped that script for me.",[14,590,592],{"id":591},"discriminated-unions-for-api-responses","Discriminated Unions for API Responses",[19,594,598],{"className":595,"code":596,"language":597,"meta":24,"style":24},"language-typescript shiki shiki-themes github-light github-dark","type ApiResponse\u003CT> = \n  | { success: true; data: T }\n  | { success: false; error: string }\n\nconst response: ApiResponse\u003CUser> = await fetchUser()\n\nif (response.success) {\n  \u002F\u002F TypeScript knows response.data is User\n  response.data.email\n} else {\n  \u002F\u002F TypeScript knows response.error exists\n  console.error(response.error)\n}\n","typescript",[26,599,600,626,659,684,688,718,722,730,736,741,752,757,767],{"__ignoreMap":24},[29,601,602,606,610,614,617,620,623],{"class":31,"line":32},[29,603,605],{"class":604},"szBVR","type",[29,607,609],{"class":608},"sScJk"," ApiResponse",[29,611,613],{"class":612},"sVt8B","\u003C",[29,615,616],{"class":608},"T",[29,618,619],{"class":612},"> ",[29,621,622],{"class":604},"=",[29,624,625],{"class":612}," \n",[29,627,628,631,634,638,641,645,648,651,653,656],{"class":31,"line":38},[29,629,630],{"class":604},"  |",[29,632,633],{"class":612}," { ",[29,635,637],{"class":636},"s4XuR","success",[29,639,640],{"class":604},":",[29,642,644],{"class":643},"sj4cs"," true",[29,646,647],{"class":612},"; ",[29,649,650],{"class":636},"data",[29,652,640],{"class":604},[29,654,655],{"class":608}," T",[29,657,658],{"class":612}," }\n",[29,660,661,663,665,667,669,672,674,677,679,682],{"class":31,"line":44},[29,662,630],{"class":604},[29,664,633],{"class":612},[29,666,637],{"class":636},[29,668,640],{"class":604},[29,670,671],{"class":643}," false",[29,673,647],{"class":612},[29,675,676],{"class":636},"error",[29,678,640],{"class":604},[29,680,681],{"class":643}," string",[29,683,658],{"class":612},[29,685,686],{"class":31,"line":50},[29,687,54],{"emptyLinePlaceholder":53},[29,689,690,693,696,698,700,702,705,707,709,712,715],{"class":31,"line":57},[29,691,692],{"class":604},"const",[29,694,695],{"class":643}," response",[29,697,640],{"class":604},[29,699,609],{"class":608},[29,701,613],{"class":612},[29,703,704],{"class":608},"User",[29,706,619],{"class":612},[29,708,622],{"class":604},[29,710,711],{"class":604}," await",[29,713,714],{"class":608}," fetchUser",[29,716,717],{"class":612},"()\n",[29,719,720],{"class":31,"line":63},[29,721,54],{"emptyLinePlaceholder":53},[29,723,724,727],{"class":31,"line":69},[29,725,726],{"class":604},"if",[29,728,729],{"class":612}," (response.success) {\n",[29,731,732],{"class":31,"line":75},[29,733,735],{"class":734},"sJ8bj","  \u002F\u002F TypeScript knows response.data is User\n",[29,737,738],{"class":31,"line":191},[29,739,740],{"class":612},"  response.data.email\n",[29,742,743,746,749],{"class":31,"line":197},[29,744,745],{"class":612},"} ",[29,747,748],{"class":604},"else",[29,750,751],{"class":612}," {\n",[29,753,754],{"class":31,"line":299},[29,755,756],{"class":734},"  \u002F\u002F TypeScript knows response.error exists\n",[29,758,759,762,764],{"class":31,"line":305},[29,760,761],{"class":612},"  console.",[29,763,676],{"class":608},[29,765,766],{"class":612},"(response.error)\n",[29,768,770],{"class":31,"line":769},13,[29,771,772],{"class":612},"}\n",[10,774,775,776,779],{},"No more ",[26,777,778],{},"if (response.error)"," guesswork. The compiler guards every branch.",[14,781,783],{"id":782},"generic-composables-in-vue","Generic Composables in Vue",[19,785,787],{"className":595,"code":786,"language":597,"meta":24,"style":24},"\u002F\u002F composables\u002FuseFetch.ts\nexport const useFetch = \u003CT>() => {\n  const data = ref\u003CT | null>(null)\n  const error = ref\u003Cstring | null>(null)\n  \n  const fetch = async (url: string) => {\n    const res = await fetch(url)\n    if (!res.ok) {\n      error.value = 'Failed'\n      return\n    }\n    data.value = await res.json() as T\n  }\n  \n  return { data, error, fetch }\n}\n\n\u002F\u002F Usage knows the exact return type\nconst { data } = useFetch\u003CUserProfile>()\n",[26,788,789,794,821,853,879,884,913,930,943,954,959,964,988,993,998,1007,1012,1017,1023],{"__ignoreMap":24},[29,790,791],{"class":31,"line":32},[29,792,793],{"class":734},"\u002F\u002F composables\u002FuseFetch.ts\n",[29,795,796,799,802,805,808,811,813,816,819],{"class":31,"line":38},[29,797,798],{"class":604},"export",[29,800,801],{"class":604}," const",[29,803,804],{"class":608}," useFetch",[29,806,807],{"class":604}," =",[29,809,810],{"class":612}," \u003C",[29,812,616],{"class":608},[29,814,815],{"class":612},">() ",[29,817,818],{"class":604},"=>",[29,820,751],{"class":612},[29,822,823,826,829,831,834,836,838,841,844,847,850],{"class":31,"line":44},[29,824,825],{"class":604},"  const",[29,827,828],{"class":643}," data",[29,830,807],{"class":604},[29,832,833],{"class":608}," ref",[29,835,613],{"class":612},[29,837,616],{"class":608},[29,839,840],{"class":604}," |",[29,842,843],{"class":643}," null",[29,845,846],{"class":612},">(",[29,848,849],{"class":643},"null",[29,851,852],{"class":612},")\n",[29,854,855,857,860,862,864,866,869,871,873,875,877],{"class":31,"line":50},[29,856,825],{"class":604},[29,858,859],{"class":643}," error",[29,861,807],{"class":604},[29,863,833],{"class":608},[29,865,613],{"class":612},[29,867,868],{"class":643},"string",[29,870,840],{"class":604},[29,872,843],{"class":643},[29,874,846],{"class":612},[29,876,849],{"class":643},[29,878,852],{"class":612},[29,880,881],{"class":31,"line":57},[29,882,883],{"class":612},"  \n",[29,885,886,888,891,893,896,899,902,904,906,909,911],{"class":31,"line":63},[29,887,825],{"class":604},[29,889,890],{"class":608}," fetch",[29,892,807],{"class":604},[29,894,895],{"class":604}," async",[29,897,898],{"class":612}," (",[29,900,901],{"class":636},"url",[29,903,640],{"class":604},[29,905,681],{"class":643},[29,907,908],{"class":612},") ",[29,910,818],{"class":604},[29,912,751],{"class":612},[29,914,915,918,921,923,925,927],{"class":31,"line":69},[29,916,917],{"class":604},"    const",[29,919,920],{"class":643}," res",[29,922,807],{"class":604},[29,924,711],{"class":604},[29,926,890],{"class":608},[29,928,929],{"class":612},"(url)\n",[29,931,932,935,937,940],{"class":31,"line":75},[29,933,934],{"class":604},"    if",[29,936,898],{"class":612},[29,938,939],{"class":604},"!",[29,941,942],{"class":612},"res.ok) {\n",[29,944,945,948,950],{"class":31,"line":191},[29,946,947],{"class":612},"      error.value ",[29,949,622],{"class":604},[29,951,953],{"class":952},"sZZnC"," 'Failed'\n",[29,955,956],{"class":31,"line":197},[29,957,958],{"class":604},"      return\n",[29,960,961],{"class":31,"line":299},[29,962,963],{"class":612},"    }\n",[29,965,966,969,971,973,976,979,982,985],{"class":31,"line":305},[29,967,968],{"class":612},"    data.value ",[29,970,622],{"class":604},[29,972,711],{"class":604},[29,974,975],{"class":612}," res.",[29,977,978],{"class":608},"json",[29,980,981],{"class":612},"() ",[29,983,984],{"class":604},"as",[29,986,987],{"class":608}," T\n",[29,989,990],{"class":31,"line":769},[29,991,992],{"class":612},"  }\n",[29,994,996],{"class":31,"line":995},14,[29,997,883],{"class":612},[29,999,1001,1004],{"class":31,"line":1000},15,[29,1002,1003],{"class":604},"  return",[29,1005,1006],{"class":612}," { data, error, fetch }\n",[29,1008,1010],{"class":31,"line":1009},16,[29,1011,772],{"class":612},[29,1013,1015],{"class":31,"line":1014},17,[29,1016,54],{"emptyLinePlaceholder":53},[29,1018,1020],{"class":31,"line":1019},18,[29,1021,1022],{"class":734},"\u002F\u002F Usage knows the exact return type\n",[29,1024,1026,1028,1030,1032,1035,1037,1039,1041,1044],{"class":31,"line":1025},19,[29,1027,692],{"class":604},[29,1029,633],{"class":612},[29,1031,650],{"class":643},[29,1033,1034],{"class":612}," } ",[29,1036,622],{"class":604},[29,1038,804],{"class":608},[29,1040,613],{"class":612},[29,1042,1043],{"class":608},"UserProfile",[29,1045,1046],{"class":612},">()\n",[10,1048,1049,1050,1053],{},"20 lines upfront, zero ",[26,1051,1052],{},"any"," debugging later.",[14,1055,1057],{"id":1056},"zod-at-the-boundary","Zod at the Boundary",[19,1059,1061],{"className":595,"code":1060,"language":597,"meta":24,"style":24},"const userSchema = z.object({\n  id: z.number(),\n  email: z.string().email(),\n  name: z.string()\n})\n\nconst response = await fetch('\u002Fapi\u002Fuser')\nconst user = userSchema.parse(await response.json())\n\n\u002F\u002F Everything past this point is 100% typed\nuser.email \u002F\u002F string, guaranteed\n",[26,1062,1063,1081,1092,1107,1116,1121,1125,1145,1173,1177,1182],{"__ignoreMap":24},[29,1064,1065,1067,1070,1072,1075,1078],{"class":31,"line":32},[29,1066,692],{"class":604},[29,1068,1069],{"class":643}," userSchema",[29,1071,807],{"class":604},[29,1073,1074],{"class":612}," z.",[29,1076,1077],{"class":608},"object",[29,1079,1080],{"class":612},"({\n",[29,1082,1083,1086,1089],{"class":31,"line":38},[29,1084,1085],{"class":612},"  id: z.",[29,1087,1088],{"class":608},"number",[29,1090,1091],{"class":612},"(),\n",[29,1093,1094,1097,1099,1102,1105],{"class":31,"line":44},[29,1095,1096],{"class":612},"  email: z.",[29,1098,868],{"class":608},[29,1100,1101],{"class":612},"().",[29,1103,1104],{"class":608},"email",[29,1106,1091],{"class":612},[29,1108,1109,1112,1114],{"class":31,"line":50},[29,1110,1111],{"class":612},"  name: z.",[29,1113,868],{"class":608},[29,1115,717],{"class":612},[29,1117,1118],{"class":31,"line":57},[29,1119,1120],{"class":612},"})\n",[29,1122,1123],{"class":31,"line":63},[29,1124,54],{"emptyLinePlaceholder":53},[29,1126,1127,1129,1131,1133,1135,1137,1140,1143],{"class":31,"line":69},[29,1128,692],{"class":604},[29,1130,695],{"class":643},[29,1132,807],{"class":604},[29,1134,711],{"class":604},[29,1136,890],{"class":608},[29,1138,1139],{"class":612},"(",[29,1141,1142],{"class":952},"'\u002Fapi\u002Fuser'",[29,1144,852],{"class":612},[29,1146,1147,1149,1152,1154,1157,1160,1162,1165,1168,1170],{"class":31,"line":75},[29,1148,692],{"class":604},[29,1150,1151],{"class":643}," user",[29,1153,807],{"class":604},[29,1155,1156],{"class":612}," userSchema.",[29,1158,1159],{"class":608},"parse",[29,1161,1139],{"class":612},[29,1163,1164],{"class":604},"await",[29,1166,1167],{"class":612}," response.",[29,1169,978],{"class":608},[29,1171,1172],{"class":612},"())\n",[29,1174,1175],{"class":31,"line":191},[29,1176,54],{"emptyLinePlaceholder":53},[29,1178,1179],{"class":31,"line":197},[29,1180,1181],{"class":734},"\u002F\u002F Everything past this point is 100% typed\n",[29,1183,1184,1187],{"class":31,"line":299},[29,1185,1186],{"class":612},"user.email ",[29,1188,1189],{"class":734},"\u002F\u002F string, guaranteed\n",[10,1191,1192,1194],{},[82,1193,136],{},": Last Laravel + Vue project refactor eliminated 80% of runtime type errors. The compiler became my QA team.",[10,1196,1197],{},[82,1198,1199],{},"Ship faster. Type smarter.",[315,1201,1202],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":24,"searchDepth":38,"depth":38,"links":1204},[1205,1206,1207],{"id":591,"depth":38,"text":592},{"id":782,"depth":38,"text":783},{"id":1056,"depth":38,"text":1057},"TypeScript","Three techniques that cut Vue + Laravel API bugs by 80%—no verbosity, just results.",{},"\u002Fdev\u002Ftypescript-advanced-patterns","10 min read",{"title":578,"description":1209},"dev\u002Ftypescript-advanced-patterns",[1208,1216,335,1217],"Vue","API","WQ5r26dYx0WOWNZq1U-ZFhbuE74Mfz7o_qGgpE44oKM"]