[{"data":1,"prerenderedAt":5108},["ShallowReactive",2],{"page-\u002Fdart\u002F17-server-side-dart":3},{"id":4,"title":5,"body":6,"description":5101,"extension":5102,"meta":5103,"navigation":80,"path":5104,"seo":5105,"stem":5106,"__hash__":5107},"content\u002Fdart\u002F17-server-side-dart.md","Dart — Server Architecture, Middleware & Database Patterns",{"type":7,"value":8,"toc":5086},"minimark",[9,13,22,1377,1384,1994,1998,2778,2782,4605,4609,4682,4686,4858,4862,4866,4922,4925,5082],[10,11,5],"h1",{"id":12},"dart-server-architecture-middleware-database-patterns",[14,15,17,21],"h2",{"id":16},"shelf-middleware-pipeline-architecture",[18,19,20],"code",{},"shelf"," — Middleware Pipeline Architecture",[23,24,26],"code-wrapper",{"language":25},"dart",[27,28,32],"pre",{"className":29,"code":30,"language":25,"meta":31,"style":31},"language-dart shiki shiki-themes github-light github-dark","import 'package:shelf\u002Fshelf.dart';\nimport 'package:shelf\u002Fshelf_io.dart' as io;\nimport 'package:shelf_router\u002Fshelf_router.dart';\n\n\u002F\u002F shelf is the standard Dart server framework — composable middleware pipeline.\n\u002F\u002F Each middleware wraps the handler: Request → Handler → Response.\n\u002F\u002F Middleware can modify the request (before), response (after), or short-circuit.\n\n\u002F\u002F ── Middleware: a function (Handler) → Handler ──\nMiddleware corsMiddleware() {\n  return (Handler innerHandler) {\n    return (Request request) async {\n      \u002F\u002F Pre-processing: add CORS headers to the response.\n      final response = await innerHandler(request);\n      return response.change(\n        headers: {\n          'Access-Control-Allow-Origin': '*',\n          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',\n          'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n        },\n      );\n    };\n  };\n}\n\n\u002F\u002F ── Auth middleware: short-circuit on invalid token ──\nMiddleware authMiddleware(String secret) {\n  return (Handler innerHandler) {\n    return (Request request) async {\n      final token = request.headers['Authorization'];\n      if (token == null || !token.startsWith('Bearer ')) {\n        return Response(401, body: 'Missing or invalid token');\n      }\n      final jwt = token.substring(7);\n      \u002F\u002F Verify JWT (omitted) — throw or return 401 on failure.\n      \u002F\u002F Pass the request to the next handler (auth passed).\n      return innerHandler(request.change(\n        context: {'userId': 'extracted-user-id'},  \u002F\u002F add context for handlers\n      ));\n    };\n  };\n}\n\n\u002F\u002F ── Rate limiting middleware ──\nMiddleware rateLimiter({int maxPerMinute = 60}) {\n  final counts = \u003CString, List\u003CDateTime>>{};\n  return (Handler innerHandler) {\n    return (Request request) async {\n      final ip = request.headers['X-Forwarded-For'] ?? request.requestedUri.host;\n      final now = DateTime.now();\n      counts[ip]?.removeWhere((t) => now.difference(t) > Duration(minutes: 1));\n      counts.putIfAbsent(ip, () => []).add(now);\n      if ((counts[ip]?.length ?? 0) > maxPerMinute) {\n        return Response(429, body: 'Rate limit exceeded');\n      }\n      return innerHandler(request);\n    };\n  };\n}\n\n\u002F\u002F ── Composing the pipeline ──\nvoid main() async {\n  final router = Router()\n    ..get('\u002F', _rootHandler)\n    ..get('\u002Fusers\u002F\u003Cid>', _userHandler)\n    ..post('\u002Fusers', _createUserHandler);\n\n  final handler = const Pipeline()\n      .addMiddleware(logRequests())           \u002F\u002F built-in request logging\n      .addMiddleware(corsMiddleware())         \u002F\u002F CORS headers\n      .addMiddleware(rateLimiter(maxPerMinute: 100))\n      .addMiddleware(authMiddleware('secret'))  \u002F\u002F auth\n      .addHandler(router);                     \u002F\u002F route handler\n\n  final server = await io.serve(handler, InternetAddress.loopbackIPv4, 8080);\n  print('Serving at http:\u002F\u002F${server.address.address}:${server.port}');\n}\n\nResponse _rootHandler(Request request) => Response.ok('Hello\\n');\n\nResponse _userHandler(Request request, String id) {\n  final userId = request.context['userId'];  \u002F\u002F from auth middleware\n  return Response.ok('User $id (requested by $userId)');\n}\n\nFuture\u003CResponse> _createUserHandler(Request request) async {\n  final body = await request.readAsString();\n  final data = jsonDecode(body) as Map\u003CString, dynamic>;\n  \u002F\u002F Validate, save to DB...\n  return Response.ok(jsonEncode({'created': data['name']}), headers: {'Content-Type': 'application\u002Fjson'});\n}\n","",[18,33,34,51,65,75,82,89,95,101,106,112,126,141,161,167,188,203,214,228,241,254,260,266,272,278,284,289,295,312,323,338,357,392,417,423,446,452,458,472,497,503,508,513,518,523,529,554,590,601,616,640,662,707,730,756,777,782,791,796,801,806,811,817,833,849,866,881,897,902,920,940,958,981,1004,1018,1023,1055,1102,1107,1112,1148,1153,1173,1194,1229,1234,1239,1265,1285,1319,1325,1372],{"__ignoreMap":31},[35,36,39,43,47],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"svdQ7","import",[35,44,46],{"class":45},"sJ6F3"," 'package:shelf\u002Fshelf.dart'",[35,48,50],{"class":49},"ssxIu",";\n",[35,52,54,56,59,62],{"class":37,"line":53},2,[35,55,42],{"class":41},[35,57,58],{"class":45}," 'package:shelf\u002Fshelf_io.dart'",[35,60,61],{"class":41}," as",[35,63,64],{"class":49}," io;\n",[35,66,68,70,73],{"class":37,"line":67},3,[35,69,42],{"class":41},[35,71,72],{"class":45}," 'package:shelf_router\u002Fshelf_router.dart'",[35,74,50],{"class":49},[35,76,78],{"class":37,"line":77},4,[35,79,81],{"emptyLinePlaceholder":80},true,"\n",[35,83,85],{"class":37,"line":84},5,[35,86,88],{"class":87},"sdCPZ","\u002F\u002F shelf is the standard Dart server framework — composable middleware pipeline.\n",[35,90,92],{"class":37,"line":91},6,[35,93,94],{"class":87},"\u002F\u002F Each middleware wraps the handler: Request → Handler → Response.\n",[35,96,98],{"class":37,"line":97},7,[35,99,100],{"class":87},"\u002F\u002F Middleware can modify the request (before), response (after), or short-circuit.\n",[35,102,104],{"class":37,"line":103},8,[35,105,81],{"emptyLinePlaceholder":80},[35,107,109],{"class":37,"line":108},9,[35,110,111],{"class":87},"\u002F\u002F ── Middleware: a function (Handler) → Handler ──\n",[35,113,115,119,123],{"class":37,"line":114},10,[35,116,118],{"class":117},"snvgF","Middleware",[35,120,122],{"class":121},"sIsaT"," corsMiddleware",[35,124,125],{"class":49},"() {\n",[35,127,129,132,135,138],{"class":37,"line":128},11,[35,130,131],{"class":41},"  return",[35,133,134],{"class":49}," (",[35,136,137],{"class":117},"Handler",[35,139,140],{"class":49}," innerHandler) {\n",[35,142,144,147,149,152,155,158],{"class":37,"line":143},12,[35,145,146],{"class":41},"    return",[35,148,134],{"class":49},[35,150,151],{"class":117},"Request",[35,153,154],{"class":49}," request) ",[35,156,157],{"class":41},"async",[35,159,160],{"class":49}," {\n",[35,162,164],{"class":37,"line":163},13,[35,165,166],{"class":87},"      \u002F\u002F Pre-processing: add CORS headers to the response.\n",[35,168,170,173,176,179,182,185],{"class":37,"line":169},14,[35,171,172],{"class":41},"      final",[35,174,175],{"class":49}," response ",[35,177,178],{"class":41},"=",[35,180,181],{"class":41}," await",[35,183,184],{"class":121}," innerHandler",[35,186,187],{"class":49},"(request);\n",[35,189,191,194,197,200],{"class":37,"line":190},15,[35,192,193],{"class":41},"      return",[35,195,196],{"class":49}," response.",[35,198,199],{"class":121},"change",[35,201,202],{"class":49},"(\n",[35,204,206,209,212],{"class":37,"line":205},16,[35,207,208],{"class":49},"        headers",[35,210,211],{"class":41},":",[35,213,160],{"class":49},[35,215,217,220,222,225],{"class":37,"line":216},17,[35,218,219],{"class":45},"          'Access-Control-Allow-Origin'",[35,221,211],{"class":41},[35,223,224],{"class":45}," '*'",[35,226,227],{"class":49},",\n",[35,229,231,234,236,239],{"class":37,"line":230},18,[35,232,233],{"class":45},"          'Access-Control-Allow-Methods'",[35,235,211],{"class":41},[35,237,238],{"class":45}," 'GET, POST, PUT, DELETE'",[35,240,227],{"class":49},[35,242,244,247,249,252],{"class":37,"line":243},19,[35,245,246],{"class":45},"          'Access-Control-Allow-Headers'",[35,248,211],{"class":41},[35,250,251],{"class":45}," 'Content-Type, Authorization'",[35,253,227],{"class":49},[35,255,257],{"class":37,"line":256},20,[35,258,259],{"class":49},"        },\n",[35,261,263],{"class":37,"line":262},21,[35,264,265],{"class":49},"      );\n",[35,267,269],{"class":37,"line":268},22,[35,270,271],{"class":49},"    };\n",[35,273,275],{"class":37,"line":274},23,[35,276,277],{"class":49},"  };\n",[35,279,281],{"class":37,"line":280},24,[35,282,283],{"class":49},"}\n",[35,285,287],{"class":37,"line":286},25,[35,288,81],{"emptyLinePlaceholder":80},[35,290,292],{"class":37,"line":291},26,[35,293,294],{"class":87},"\u002F\u002F ── Auth middleware: short-circuit on invalid token ──\n",[35,296,298,300,303,306,309],{"class":37,"line":297},27,[35,299,118],{"class":117},[35,301,302],{"class":121}," authMiddleware",[35,304,305],{"class":49},"(",[35,307,308],{"class":117},"String",[35,310,311],{"class":49}," secret) {\n",[35,313,315,317,319,321],{"class":37,"line":314},28,[35,316,131],{"class":41},[35,318,134],{"class":49},[35,320,137],{"class":117},[35,322,140],{"class":49},[35,324,326,328,330,332,334,336],{"class":37,"line":325},29,[35,327,146],{"class":41},[35,329,134],{"class":49},[35,331,151],{"class":117},[35,333,154],{"class":49},[35,335,157],{"class":41},[35,337,160],{"class":49},[35,339,341,343,346,348,351,354],{"class":37,"line":340},30,[35,342,172],{"class":41},[35,344,345],{"class":49}," token ",[35,347,178],{"class":41},[35,349,350],{"class":49}," request.headers[",[35,352,353],{"class":45},"'Authorization'",[35,355,356],{"class":49},"];\n",[35,358,360,363,366,369,372,375,378,381,384,386,389],{"class":37,"line":359},31,[35,361,362],{"class":41},"      if",[35,364,365],{"class":49}," (token ",[35,367,368],{"class":41},"==",[35,370,371],{"class":117}," null",[35,373,374],{"class":41}," ||",[35,376,377],{"class":41}," !",[35,379,380],{"class":49},"token.",[35,382,383],{"class":121},"startsWith",[35,385,305],{"class":49},[35,387,388],{"class":45},"'Bearer '",[35,390,391],{"class":49},")) {\n",[35,393,395,398,401,403,406,409,411,414],{"class":37,"line":394},32,[35,396,397],{"class":41},"        return",[35,399,400],{"class":117}," Response",[35,402,305],{"class":49},[35,404,405],{"class":117},"401",[35,407,408],{"class":49},", body",[35,410,211],{"class":41},[35,412,413],{"class":45}," 'Missing or invalid token'",[35,415,416],{"class":49},");\n",[35,418,420],{"class":37,"line":419},33,[35,421,422],{"class":49},"      }\n",[35,424,426,428,431,433,436,439,441,444],{"class":37,"line":425},34,[35,427,172],{"class":41},[35,429,430],{"class":49}," jwt ",[35,432,178],{"class":41},[35,434,435],{"class":49}," token.",[35,437,438],{"class":121},"substring",[35,440,305],{"class":49},[35,442,443],{"class":117},"7",[35,445,416],{"class":49},[35,447,449],{"class":37,"line":448},35,[35,450,451],{"class":87},"      \u002F\u002F Verify JWT (omitted) — throw or return 401 on failure.\n",[35,453,455],{"class":37,"line":454},36,[35,456,457],{"class":87},"      \u002F\u002F Pass the request to the next handler (auth passed).\n",[35,459,461,463,465,468,470],{"class":37,"line":460},37,[35,462,193],{"class":41},[35,464,184],{"class":121},[35,466,467],{"class":49},"(request.",[35,469,199],{"class":121},[35,471,202],{"class":49},[35,473,475,478,480,483,486,488,491,494],{"class":37,"line":474},38,[35,476,477],{"class":49},"        context",[35,479,211],{"class":41},[35,481,482],{"class":49}," {",[35,484,485],{"class":45},"'userId'",[35,487,211],{"class":41},[35,489,490],{"class":45}," 'extracted-user-id'",[35,492,493],{"class":49},"},  ",[35,495,496],{"class":87},"\u002F\u002F add context for handlers\n",[35,498,500],{"class":37,"line":499},39,[35,501,502],{"class":49},"      ));\n",[35,504,506],{"class":37,"line":505},40,[35,507,271],{"class":49},[35,509,511],{"class":37,"line":510},41,[35,512,277],{"class":49},[35,514,516],{"class":37,"line":515},42,[35,517,283],{"class":49},[35,519,521],{"class":37,"line":520},43,[35,522,81],{"emptyLinePlaceholder":80},[35,524,526],{"class":37,"line":525},44,[35,527,528],{"class":87},"\u002F\u002F ── Rate limiting middleware ──\n",[35,530,532,534,537,540,543,546,548,551],{"class":37,"line":531},45,[35,533,118],{"class":117},[35,535,536],{"class":121}," rateLimiter",[35,538,539],{"class":49},"({",[35,541,542],{"class":117},"int",[35,544,545],{"class":49}," maxPerMinute ",[35,547,178],{"class":41},[35,549,550],{"class":117}," 60",[35,552,553],{"class":49},"}) {\n",[35,555,557,560,563,565,568,570,573,576,579,582,585,587],{"class":37,"line":556},46,[35,558,559],{"class":41},"  final",[35,561,562],{"class":49}," counts ",[35,564,178],{"class":41},[35,566,567],{"class":41}," \u003C",[35,569,308],{"class":117},[35,571,572],{"class":49},", ",[35,574,575],{"class":117},"List",[35,577,578],{"class":49},"\u003C",[35,580,581],{"class":117},"DateTime",[35,583,584],{"class":49},">",[35,586,584],{"class":41},[35,588,589],{"class":49},"{};\n",[35,591,593,595,597,599],{"class":37,"line":592},47,[35,594,131],{"class":41},[35,596,134],{"class":49},[35,598,137],{"class":117},[35,600,140],{"class":49},[35,602,604,606,608,610,612,614],{"class":37,"line":603},48,[35,605,146],{"class":41},[35,607,134],{"class":49},[35,609,151],{"class":117},[35,611,154],{"class":49},[35,613,157],{"class":41},[35,615,160],{"class":49},[35,617,619,621,624,626,628,631,634,637],{"class":37,"line":618},49,[35,620,172],{"class":41},[35,622,623],{"class":49}," ip ",[35,625,178],{"class":41},[35,627,350],{"class":49},[35,629,630],{"class":45},"'X-Forwarded-For'",[35,632,633],{"class":49},"] ",[35,635,636],{"class":41},"??",[35,638,639],{"class":49}," request.requestedUri.host;\n",[35,641,643,645,648,650,653,656,659],{"class":37,"line":642},50,[35,644,172],{"class":41},[35,646,647],{"class":49}," now ",[35,649,178],{"class":41},[35,651,652],{"class":117}," DateTime",[35,654,655],{"class":49},".",[35,657,658],{"class":121},"now",[35,660,661],{"class":49},"();\n",[35,663,665,668,671,673,676,679,682,685,688,691,693,696,699,701,704],{"class":37,"line":664},51,[35,666,667],{"class":49},"      counts[ip]",[35,669,670],{"class":41},"?",[35,672,655],{"class":49},[35,674,675],{"class":121},"removeWhere",[35,677,678],{"class":49},"((t) ",[35,680,681],{"class":41},"=>",[35,683,684],{"class":49}," now.",[35,686,687],{"class":121},"difference",[35,689,690],{"class":49},"(t) ",[35,692,584],{"class":41},[35,694,695],{"class":117}," Duration",[35,697,698],{"class":49},"(minutes",[35,700,211],{"class":41},[35,702,703],{"class":117}," 1",[35,705,706],{"class":49},"));\n",[35,708,710,713,716,719,721,724,727],{"class":37,"line":709},52,[35,711,712],{"class":49},"      counts.",[35,714,715],{"class":121},"putIfAbsent",[35,717,718],{"class":49},"(ip, () ",[35,720,681],{"class":41},[35,722,723],{"class":49}," []).",[35,725,726],{"class":121},"add",[35,728,729],{"class":49},"(now);\n",[35,731,733,735,738,740,743,745,748,751,753],{"class":37,"line":732},53,[35,734,362],{"class":41},[35,736,737],{"class":49}," ((counts[ip]",[35,739,670],{"class":41},[35,741,742],{"class":49},".length ",[35,744,636],{"class":41},[35,746,747],{"class":117}," 0",[35,749,750],{"class":49},") ",[35,752,584],{"class":41},[35,754,755],{"class":49}," maxPerMinute) {\n",[35,757,759,761,763,765,768,770,772,775],{"class":37,"line":758},54,[35,760,397],{"class":41},[35,762,400],{"class":117},[35,764,305],{"class":49},[35,766,767],{"class":117},"429",[35,769,408],{"class":49},[35,771,211],{"class":41},[35,773,774],{"class":45}," 'Rate limit exceeded'",[35,776,416],{"class":49},[35,778,780],{"class":37,"line":779},55,[35,781,422],{"class":49},[35,783,785,787,789],{"class":37,"line":784},56,[35,786,193],{"class":41},[35,788,184],{"class":121},[35,790,187],{"class":49},[35,792,794],{"class":37,"line":793},57,[35,795,271],{"class":49},[35,797,799],{"class":37,"line":798},58,[35,800,277],{"class":49},[35,802,804],{"class":37,"line":803},59,[35,805,283],{"class":49},[35,807,809],{"class":37,"line":808},60,[35,810,81],{"emptyLinePlaceholder":80},[35,812,814],{"class":37,"line":813},61,[35,815,816],{"class":87},"\u002F\u002F ── Composing the pipeline ──\n",[35,818,820,823,826,829,831],{"class":37,"line":819},62,[35,821,822],{"class":41},"void",[35,824,825],{"class":121}," main",[35,827,828],{"class":49},"() ",[35,830,157],{"class":41},[35,832,160],{"class":49},[35,834,836,838,841,843,846],{"class":37,"line":835},63,[35,837,559],{"class":41},[35,839,840],{"class":49}," router ",[35,842,178],{"class":41},[35,844,845],{"class":117}," Router",[35,847,848],{"class":49},"()\n",[35,850,852,855,858,860,863],{"class":37,"line":851},64,[35,853,854],{"class":49},"    ..",[35,856,857],{"class":121},"get",[35,859,305],{"class":49},[35,861,862],{"class":45},"'\u002F'",[35,864,865],{"class":49},", _rootHandler)\n",[35,867,869,871,873,875,878],{"class":37,"line":868},65,[35,870,854],{"class":49},[35,872,857],{"class":121},[35,874,305],{"class":49},[35,876,877],{"class":45},"'\u002Fusers\u002F\u003Cid>'",[35,879,880],{"class":49},", _userHandler)\n",[35,882,884,886,889,891,894],{"class":37,"line":883},66,[35,885,854],{"class":49},[35,887,888],{"class":121},"post",[35,890,305],{"class":49},[35,892,893],{"class":45},"'\u002Fusers'",[35,895,896],{"class":49},", _createUserHandler);\n",[35,898,900],{"class":37,"line":899},67,[35,901,81],{"emptyLinePlaceholder":80},[35,903,905,907,910,912,915,918],{"class":37,"line":904},68,[35,906,559],{"class":41},[35,908,909],{"class":49}," handler ",[35,911,178],{"class":41},[35,913,914],{"class":41}," const",[35,916,917],{"class":117}," Pipeline",[35,919,848],{"class":49},[35,921,923,926,929,931,934,937],{"class":37,"line":922},69,[35,924,925],{"class":49},"      .",[35,927,928],{"class":121},"addMiddleware",[35,930,305],{"class":49},[35,932,933],{"class":121},"logRequests",[35,935,936],{"class":49},"())           ",[35,938,939],{"class":87},"\u002F\u002F built-in request logging\n",[35,941,943,945,947,949,952,955],{"class":37,"line":942},70,[35,944,925],{"class":49},[35,946,928],{"class":121},[35,948,305],{"class":49},[35,950,951],{"class":121},"corsMiddleware",[35,953,954],{"class":49},"())         ",[35,956,957],{"class":87},"\u002F\u002F CORS headers\n",[35,959,961,963,965,967,970,973,975,978],{"class":37,"line":960},71,[35,962,925],{"class":49},[35,964,928],{"class":121},[35,966,305],{"class":49},[35,968,969],{"class":121},"rateLimiter",[35,971,972],{"class":49},"(maxPerMinute",[35,974,211],{"class":41},[35,976,977],{"class":117}," 100",[35,979,980],{"class":49},"))\n",[35,982,984,986,988,990,993,995,998,1001],{"class":37,"line":983},72,[35,985,925],{"class":49},[35,987,928],{"class":121},[35,989,305],{"class":49},[35,991,992],{"class":121},"authMiddleware",[35,994,305],{"class":49},[35,996,997],{"class":45},"'secret'",[35,999,1000],{"class":49},"))  ",[35,1002,1003],{"class":87},"\u002F\u002F auth\n",[35,1005,1007,1009,1012,1015],{"class":37,"line":1006},73,[35,1008,925],{"class":49},[35,1010,1011],{"class":121},"addHandler",[35,1013,1014],{"class":49},"(router);                     ",[35,1016,1017],{"class":87},"\u002F\u002F route handler\n",[35,1019,1021],{"class":37,"line":1020},74,[35,1022,81],{"emptyLinePlaceholder":80},[35,1024,1026,1028,1031,1033,1035,1038,1041,1044,1047,1050,1053],{"class":37,"line":1025},75,[35,1027,559],{"class":41},[35,1029,1030],{"class":49}," server ",[35,1032,178],{"class":41},[35,1034,181],{"class":41},[35,1036,1037],{"class":49}," io.",[35,1039,1040],{"class":121},"serve",[35,1042,1043],{"class":49},"(handler, ",[35,1045,1046],{"class":117},"InternetAddress",[35,1048,1049],{"class":49},".loopbackIPv4, ",[35,1051,1052],{"class":117},"8080",[35,1054,416],{"class":49},[35,1056,1058,1061,1063,1066,1069,1072,1074,1077,1079,1081,1084,1086,1088,1090,1092,1095,1097,1100],{"class":37,"line":1057},76,[35,1059,1060],{"class":121},"  print",[35,1062,305],{"class":49},[35,1064,1065],{"class":45},"'Serving at http:\u002F\u002F",[35,1067,1068],{"class":45},"${",[35,1070,1071],{"class":117},"server",[35,1073,655],{"class":45},[35,1075,1076],{"class":117},"address",[35,1078,655],{"class":45},[35,1080,1076],{"class":117},[35,1082,1083],{"class":45},"}",[35,1085,211],{"class":45},[35,1087,1068],{"class":45},[35,1089,1071],{"class":117},[35,1091,655],{"class":45},[35,1093,1094],{"class":117},"port",[35,1096,1083],{"class":45},[35,1098,1099],{"class":45},"'",[35,1101,416],{"class":49},[35,1103,1105],{"class":37,"line":1104},77,[35,1106,283],{"class":49},[35,1108,1110],{"class":37,"line":1109},78,[35,1111,81],{"emptyLinePlaceholder":80},[35,1113,1115,1118,1121,1123,1125,1127,1129,1131,1133,1136,1138,1141,1144,1146],{"class":37,"line":1114},79,[35,1116,1117],{"class":117},"Response",[35,1119,1120],{"class":121}," _rootHandler",[35,1122,305],{"class":49},[35,1124,151],{"class":117},[35,1126,154],{"class":49},[35,1128,681],{"class":41},[35,1130,400],{"class":117},[35,1132,655],{"class":49},[35,1134,1135],{"class":121},"ok",[35,1137,305],{"class":49},[35,1139,1140],{"class":45},"'Hello",[35,1142,1143],{"class":117},"\\n",[35,1145,1099],{"class":45},[35,1147,416],{"class":49},[35,1149,1151],{"class":37,"line":1150},80,[35,1152,81],{"emptyLinePlaceholder":80},[35,1154,1156,1158,1161,1163,1165,1168,1170],{"class":37,"line":1155},81,[35,1157,1117],{"class":117},[35,1159,1160],{"class":121}," _userHandler",[35,1162,305],{"class":49},[35,1164,151],{"class":117},[35,1166,1167],{"class":49}," request, ",[35,1169,308],{"class":117},[35,1171,1172],{"class":49}," id) {\n",[35,1174,1176,1178,1181,1183,1186,1188,1191],{"class":37,"line":1175},82,[35,1177,559],{"class":41},[35,1179,1180],{"class":49}," userId ",[35,1182,178],{"class":41},[35,1184,1185],{"class":49}," request.context[",[35,1187,485],{"class":45},[35,1189,1190],{"class":49},"];  ",[35,1192,1193],{"class":87},"\u002F\u002F from auth middleware\n",[35,1195,1197,1199,1201,1203,1205,1207,1210,1213,1216,1219,1221,1224,1227],{"class":37,"line":1196},83,[35,1198,131],{"class":41},[35,1200,400],{"class":117},[35,1202,655],{"class":49},[35,1204,1135],{"class":121},[35,1206,305],{"class":49},[35,1208,1209],{"class":45},"'User ",[35,1211,1212],{"class":45},"$",[35,1214,1215],{"class":117},"id",[35,1217,1218],{"class":45}," (requested by ",[35,1220,1212],{"class":45},[35,1222,1223],{"class":117},"userId",[35,1225,1226],{"class":45},")'",[35,1228,416],{"class":49},[35,1230,1232],{"class":37,"line":1231},84,[35,1233,283],{"class":49},[35,1235,1237],{"class":37,"line":1236},85,[35,1238,81],{"emptyLinePlaceholder":80},[35,1240,1242,1245,1247,1249,1252,1255,1257,1259,1261,1263],{"class":37,"line":1241},86,[35,1243,1244],{"class":117},"Future",[35,1246,578],{"class":49},[35,1248,1117],{"class":117},[35,1250,1251],{"class":49},"> ",[35,1253,1254],{"class":121},"_createUserHandler",[35,1256,305],{"class":49},[35,1258,151],{"class":117},[35,1260,154],{"class":49},[35,1262,157],{"class":41},[35,1264,160],{"class":49},[35,1266,1268,1270,1273,1275,1277,1280,1283],{"class":37,"line":1267},87,[35,1269,559],{"class":41},[35,1271,1272],{"class":49}," body ",[35,1274,178],{"class":41},[35,1276,181],{"class":41},[35,1278,1279],{"class":49}," request.",[35,1281,1282],{"class":121},"readAsString",[35,1284,661],{"class":49},[35,1286,1288,1290,1293,1295,1298,1301,1304,1307,1309,1311,1313,1316],{"class":37,"line":1287},88,[35,1289,559],{"class":41},[35,1291,1292],{"class":49}," data ",[35,1294,178],{"class":41},[35,1296,1297],{"class":121}," jsonDecode",[35,1299,1300],{"class":49},"(body) ",[35,1302,1303],{"class":41},"as",[35,1305,1306],{"class":117}," Map",[35,1308,578],{"class":49},[35,1310,308],{"class":117},[35,1312,572],{"class":49},[35,1314,1315],{"class":117},"dynamic",[35,1317,1318],{"class":49},">;\n",[35,1320,1322],{"class":37,"line":1321},89,[35,1323,1324],{"class":87},"  \u002F\u002F Validate, save to DB...\n",[35,1326,1328,1330,1332,1334,1336,1338,1341,1343,1346,1348,1351,1354,1357,1359,1361,1364,1366,1369],{"class":37,"line":1327},90,[35,1329,131],{"class":41},[35,1331,400],{"class":117},[35,1333,655],{"class":49},[35,1335,1135],{"class":121},[35,1337,305],{"class":49},[35,1339,1340],{"class":121},"jsonEncode",[35,1342,539],{"class":49},[35,1344,1345],{"class":45},"'created'",[35,1347,211],{"class":41},[35,1349,1350],{"class":49}," data[",[35,1352,1353],{"class":45},"'name'",[35,1355,1356],{"class":49},"]}), headers",[35,1358,211],{"class":41},[35,1360,482],{"class":49},[35,1362,1363],{"class":45},"'Content-Type'",[35,1365,211],{"class":41},[35,1367,1368],{"class":45}," 'application\u002Fjson'",[35,1370,1371],{"class":49},"});\n",[35,1373,1375],{"class":37,"line":1374},91,[35,1376,283],{"class":49},[14,1378,1380,1383],{"id":1379},"dart_frog-file-based-routing",[18,1381,1382],{},"dart_frog"," — File-Based Routing",[23,1385,1386],{"language":25},[27,1387,1389],{"className":29,"code":1388,"language":25,"meta":31,"style":31},"import 'package:dart_frog\u002Fdart_frog.dart';\n\n\u002F\u002F dart_frog: file-based routing (like Next.js). Each file in routes\u002F maps to a URL.\n\u002F\u002F routes\u002Findex.dart        → GET \u002F\n\u002F\u002F routes\u002Fusers\u002Findex.dart   → GET \u002Fusers\n\u002F\u002F routes\u002Fusers\u002F[id].dart    → GET \u002Fusers\u002F:id\n\u002F\u002F routes\u002Fusers\u002F[id]\u002Fposts.dart → GET \u002Fusers\u002F:id\u002Fposts\n\n\u002F\u002F ── routes\u002Findex.dart ──\nResponse onRequest(Request request) {\n  return Response(body: 'Hello, Dart Frog!');\n}\n\n\u002F\u002F ── routes\u002Fusers\u002F[id].dart ──\n\u002F\u002F The `id` parameter is injected by the framework.\nResponse onRequest(RequestContext context, String id) {\n  \u002F\u002F context provides middleware-provided values, request, etc.\n  final userId = context.read\u003CString>();  \u002F\u002F from middleware provider\n  return Response.json(body: {'id': id, 'requestedBy': userId});\n}\n\n\u002F\u002F ── routes\u002Fusers\u002Findex.dart (with method routing) ──\nFuture\u003CResponse> onRequest(RequestContext context) async {\n  return switch (context.request.method) {\n    HttpMethod.get => Response.json(body: await _allUsers()),\n    HttpMethod.post => _createUser(context),\n    _ => Response(statusCode: HttpStatus.methodNotAllowed),\n  };\n}\n\nFuture\u003CResponse> _createUser(RequestContext context) async {\n  final body = await context.request.json() as Map\u003CString, dynamic>;\n  final name = body['name'] as String?;\n  if (name == null || name.isEmpty) {\n    return Response(statusCode: 400, body: 'Name is required');\n  }\n  final user = await db.insertUser(name);\n  return Response.json(statusCode: 201, body: user.toJson());\n}\n\n\u002F\u002F ── Middleware via _middleware.dart ──\n\u002F\u002F routes\u002F_middleware.dart — runs for all routes in this directory and below.\nHandler middleware(Handler handler) {\n  return (context) async {\n    \u002F\u002F Pre-processing: auth check\n    final token = context.request.headers['Authorization'];\n    if (token == null) return Response(statusCode: 401);\n    \u002F\u002F Pass userId to downstream handlers via provider\n    return handler(context.provide\u003CString>(() => 'user-from-token'));\n  };\n}\n",[18,1390,1391,1400,1404,1409,1414,1419,1424,1429,1433,1438,1452,1468,1472,1476,1481,1486,1504,1509,1533,1566,1570,1574,1579,1603,1613,1643,1658,1678,1682,1686,1690,1713,1744,1769,1786,1808,1813,1833,1863,1867,1871,1876,1881,1895,1906,1911,1927,1954,1959,1986,1990],{"__ignoreMap":31},[35,1392,1393,1395,1398],{"class":37,"line":38},[35,1394,42],{"class":41},[35,1396,1397],{"class":45}," 'package:dart_frog\u002Fdart_frog.dart'",[35,1399,50],{"class":49},[35,1401,1402],{"class":37,"line":53},[35,1403,81],{"emptyLinePlaceholder":80},[35,1405,1406],{"class":37,"line":67},[35,1407,1408],{"class":87},"\u002F\u002F dart_frog: file-based routing (like Next.js). Each file in routes\u002F maps to a URL.\n",[35,1410,1411],{"class":37,"line":77},[35,1412,1413],{"class":87},"\u002F\u002F routes\u002Findex.dart        → GET \u002F\n",[35,1415,1416],{"class":37,"line":84},[35,1417,1418],{"class":87},"\u002F\u002F routes\u002Fusers\u002Findex.dart   → GET \u002Fusers\n",[35,1420,1421],{"class":37,"line":91},[35,1422,1423],{"class":87},"\u002F\u002F routes\u002Fusers\u002F[id].dart    → GET \u002Fusers\u002F:id\n",[35,1425,1426],{"class":37,"line":97},[35,1427,1428],{"class":87},"\u002F\u002F routes\u002Fusers\u002F[id]\u002Fposts.dart → GET \u002Fusers\u002F:id\u002Fposts\n",[35,1430,1431],{"class":37,"line":103},[35,1432,81],{"emptyLinePlaceholder":80},[35,1434,1435],{"class":37,"line":108},[35,1436,1437],{"class":87},"\u002F\u002F ── routes\u002Findex.dart ──\n",[35,1439,1440,1442,1445,1447,1449],{"class":37,"line":114},[35,1441,1117],{"class":117},[35,1443,1444],{"class":121}," onRequest",[35,1446,305],{"class":49},[35,1448,151],{"class":117},[35,1450,1451],{"class":49}," request) {\n",[35,1453,1454,1456,1458,1461,1463,1466],{"class":37,"line":128},[35,1455,131],{"class":41},[35,1457,400],{"class":117},[35,1459,1460],{"class":49},"(body",[35,1462,211],{"class":41},[35,1464,1465],{"class":45}," 'Hello, Dart Frog!'",[35,1467,416],{"class":49},[35,1469,1470],{"class":37,"line":143},[35,1471,283],{"class":49},[35,1473,1474],{"class":37,"line":163},[35,1475,81],{"emptyLinePlaceholder":80},[35,1477,1478],{"class":37,"line":169},[35,1479,1480],{"class":87},"\u002F\u002F ── routes\u002Fusers\u002F[id].dart ──\n",[35,1482,1483],{"class":37,"line":190},[35,1484,1485],{"class":87},"\u002F\u002F The `id` parameter is injected by the framework.\n",[35,1487,1488,1490,1492,1494,1497,1500,1502],{"class":37,"line":205},[35,1489,1117],{"class":117},[35,1491,1444],{"class":121},[35,1493,305],{"class":49},[35,1495,1496],{"class":117},"RequestContext",[35,1498,1499],{"class":49}," context, ",[35,1501,308],{"class":117},[35,1503,1172],{"class":49},[35,1505,1506],{"class":37,"line":216},[35,1507,1508],{"class":87},"  \u002F\u002F context provides middleware-provided values, request, etc.\n",[35,1510,1511,1513,1515,1517,1520,1523,1525,1527,1530],{"class":37,"line":230},[35,1512,559],{"class":41},[35,1514,1180],{"class":49},[35,1516,178],{"class":41},[35,1518,1519],{"class":49}," context.",[35,1521,1522],{"class":121},"read",[35,1524,578],{"class":49},[35,1526,308],{"class":117},[35,1528,1529],{"class":49},">();  ",[35,1531,1532],{"class":87},"\u002F\u002F from middleware provider\n",[35,1534,1535,1537,1539,1541,1544,1546,1548,1550,1553,1555,1558,1561,1563],{"class":37,"line":243},[35,1536,131],{"class":41},[35,1538,400],{"class":117},[35,1540,655],{"class":49},[35,1542,1543],{"class":121},"json",[35,1545,1460],{"class":49},[35,1547,211],{"class":41},[35,1549,482],{"class":49},[35,1551,1552],{"class":45},"'id'",[35,1554,211],{"class":41},[35,1556,1557],{"class":49}," id, ",[35,1559,1560],{"class":45},"'requestedBy'",[35,1562,211],{"class":41},[35,1564,1565],{"class":49}," userId});\n",[35,1567,1568],{"class":37,"line":256},[35,1569,283],{"class":49},[35,1571,1572],{"class":37,"line":262},[35,1573,81],{"emptyLinePlaceholder":80},[35,1575,1576],{"class":37,"line":268},[35,1577,1578],{"class":87},"\u002F\u002F ── routes\u002Fusers\u002Findex.dart (with method routing) ──\n",[35,1580,1581,1583,1585,1587,1589,1592,1594,1596,1599,1601],{"class":37,"line":274},[35,1582,1244],{"class":117},[35,1584,578],{"class":49},[35,1586,1117],{"class":117},[35,1588,1251],{"class":49},[35,1590,1591],{"class":121},"onRequest",[35,1593,305],{"class":49},[35,1595,1496],{"class":117},[35,1597,1598],{"class":49}," context) ",[35,1600,157],{"class":41},[35,1602,160],{"class":49},[35,1604,1605,1607,1610],{"class":37,"line":280},[35,1606,131],{"class":41},[35,1608,1609],{"class":41}," switch",[35,1611,1612],{"class":49}," (context.request.method) {\n",[35,1614,1615,1618,1620,1622,1625,1627,1629,1631,1633,1635,1637,1640],{"class":37,"line":286},[35,1616,1617],{"class":117},"    HttpMethod",[35,1619,655],{"class":49},[35,1621,857],{"class":41},[35,1623,1624],{"class":41}," =>",[35,1626,400],{"class":117},[35,1628,655],{"class":49},[35,1630,1543],{"class":121},[35,1632,1460],{"class":49},[35,1634,211],{"class":41},[35,1636,181],{"class":41},[35,1638,1639],{"class":121}," _allUsers",[35,1641,1642],{"class":49},"()),\n",[35,1644,1645,1647,1650,1652,1655],{"class":37,"line":291},[35,1646,1617],{"class":117},[35,1648,1649],{"class":49},".post ",[35,1651,681],{"class":41},[35,1653,1654],{"class":121}," _createUser",[35,1656,1657],{"class":49},"(context),\n",[35,1659,1660,1663,1665,1667,1670,1672,1675],{"class":37,"line":297},[35,1661,1662],{"class":49},"    _ ",[35,1664,681],{"class":41},[35,1666,400],{"class":117},[35,1668,1669],{"class":49},"(statusCode",[35,1671,211],{"class":41},[35,1673,1674],{"class":117}," HttpStatus",[35,1676,1677],{"class":49},".methodNotAllowed),\n",[35,1679,1680],{"class":37,"line":314},[35,1681,277],{"class":49},[35,1683,1684],{"class":37,"line":325},[35,1685,283],{"class":49},[35,1687,1688],{"class":37,"line":340},[35,1689,81],{"emptyLinePlaceholder":80},[35,1691,1692,1694,1696,1698,1700,1703,1705,1707,1709,1711],{"class":37,"line":359},[35,1693,1244],{"class":117},[35,1695,578],{"class":49},[35,1697,1117],{"class":117},[35,1699,1251],{"class":49},[35,1701,1702],{"class":121},"_createUser",[35,1704,305],{"class":49},[35,1706,1496],{"class":117},[35,1708,1598],{"class":49},[35,1710,157],{"class":41},[35,1712,160],{"class":49},[35,1714,1715,1717,1719,1721,1723,1726,1728,1730,1732,1734,1736,1738,1740,1742],{"class":37,"line":394},[35,1716,559],{"class":41},[35,1718,1272],{"class":49},[35,1720,178],{"class":41},[35,1722,181],{"class":41},[35,1724,1725],{"class":49}," context.request.",[35,1727,1543],{"class":121},[35,1729,828],{"class":49},[35,1731,1303],{"class":41},[35,1733,1306],{"class":117},[35,1735,578],{"class":49},[35,1737,308],{"class":117},[35,1739,572],{"class":49},[35,1741,1315],{"class":117},[35,1743,1318],{"class":49},[35,1745,1746,1748,1751,1753,1756,1758,1760,1762,1765,1767],{"class":37,"line":419},[35,1747,559],{"class":41},[35,1749,1750],{"class":49}," name ",[35,1752,178],{"class":41},[35,1754,1755],{"class":49}," body[",[35,1757,1353],{"class":45},[35,1759,633],{"class":49},[35,1761,1303],{"class":41},[35,1763,1764],{"class":117}," String",[35,1766,670],{"class":41},[35,1768,50],{"class":49},[35,1770,1771,1774,1777,1779,1781,1783],{"class":37,"line":425},[35,1772,1773],{"class":41},"  if",[35,1775,1776],{"class":49}," (name ",[35,1778,368],{"class":41},[35,1780,371],{"class":117},[35,1782,374],{"class":41},[35,1784,1785],{"class":49}," name.isEmpty) {\n",[35,1787,1788,1790,1792,1794,1796,1799,1801,1803,1806],{"class":37,"line":448},[35,1789,146],{"class":41},[35,1791,400],{"class":117},[35,1793,1669],{"class":49},[35,1795,211],{"class":41},[35,1797,1798],{"class":117}," 400",[35,1800,408],{"class":49},[35,1802,211],{"class":41},[35,1804,1805],{"class":45}," 'Name is required'",[35,1807,416],{"class":49},[35,1809,1810],{"class":37,"line":454},[35,1811,1812],{"class":49},"  }\n",[35,1814,1815,1817,1820,1822,1824,1827,1830],{"class":37,"line":460},[35,1816,559],{"class":41},[35,1818,1819],{"class":49}," user ",[35,1821,178],{"class":41},[35,1823,181],{"class":41},[35,1825,1826],{"class":49}," db.",[35,1828,1829],{"class":121},"insertUser",[35,1831,1832],{"class":49},"(name);\n",[35,1834,1835,1837,1839,1841,1843,1845,1847,1850,1852,1854,1857,1860],{"class":37,"line":474},[35,1836,131],{"class":41},[35,1838,400],{"class":117},[35,1840,655],{"class":49},[35,1842,1543],{"class":121},[35,1844,1669],{"class":49},[35,1846,211],{"class":41},[35,1848,1849],{"class":117}," 201",[35,1851,408],{"class":49},[35,1853,211],{"class":41},[35,1855,1856],{"class":49}," user.",[35,1858,1859],{"class":121},"toJson",[35,1861,1862],{"class":49},"());\n",[35,1864,1865],{"class":37,"line":499},[35,1866,283],{"class":49},[35,1868,1869],{"class":37,"line":505},[35,1870,81],{"emptyLinePlaceholder":80},[35,1872,1873],{"class":37,"line":510},[35,1874,1875],{"class":87},"\u002F\u002F ── Middleware via _middleware.dart ──\n",[35,1877,1878],{"class":37,"line":515},[35,1879,1880],{"class":87},"\u002F\u002F routes\u002F_middleware.dart — runs for all routes in this directory and below.\n",[35,1882,1883,1885,1888,1890,1892],{"class":37,"line":520},[35,1884,137],{"class":117},[35,1886,1887],{"class":121}," middleware",[35,1889,305],{"class":49},[35,1891,137],{"class":117},[35,1893,1894],{"class":49}," handler) {\n",[35,1896,1897,1899,1902,1904],{"class":37,"line":525},[35,1898,131],{"class":41},[35,1900,1901],{"class":49}," (context) ",[35,1903,157],{"class":41},[35,1905,160],{"class":49},[35,1907,1908],{"class":37,"line":531},[35,1909,1910],{"class":87},"    \u002F\u002F Pre-processing: auth check\n",[35,1912,1913,1916,1918,1920,1923,1925],{"class":37,"line":556},[35,1914,1915],{"class":41},"    final",[35,1917,345],{"class":49},[35,1919,178],{"class":41},[35,1921,1922],{"class":49}," context.request.headers[",[35,1924,353],{"class":45},[35,1926,356],{"class":49},[35,1928,1929,1932,1934,1936,1938,1940,1943,1945,1947,1949,1952],{"class":37,"line":592},[35,1930,1931],{"class":41},"    if",[35,1933,365],{"class":49},[35,1935,368],{"class":41},[35,1937,371],{"class":117},[35,1939,750],{"class":49},[35,1941,1942],{"class":41},"return",[35,1944,400],{"class":117},[35,1946,1669],{"class":49},[35,1948,211],{"class":41},[35,1950,1951],{"class":117}," 401",[35,1953,416],{"class":49},[35,1955,1956],{"class":37,"line":603},[35,1957,1958],{"class":87},"    \u002F\u002F Pass userId to downstream handlers via provider\n",[35,1960,1961,1963,1966,1969,1972,1974,1976,1979,1981,1984],{"class":37,"line":618},[35,1962,146],{"class":41},[35,1964,1965],{"class":121}," handler",[35,1967,1968],{"class":49},"(context.",[35,1970,1971],{"class":121},"provide",[35,1973,578],{"class":49},[35,1975,308],{"class":117},[35,1977,1978],{"class":49},">(() ",[35,1980,681],{"class":41},[35,1982,1983],{"class":45}," 'user-from-token'",[35,1985,706],{"class":49},[35,1987,1988],{"class":37,"line":642},[35,1989,277],{"class":49},[35,1991,1992],{"class":37,"line":664},[35,1993,283],{"class":49},[14,1995,1997],{"id":1996},"file-io-streaming-async","File I\u002FO — Streaming & Async",[23,1999,2000],{"language":25},[27,2001,2003],{"className":29,"code":2002,"language":25,"meta":31,"style":31},"import 'dart:io';\nimport 'dart:convert';\n\n\u002F\u002F ── Read (async — yields to event loop, doesn't block) ──\nFuture\u003Cvoid> readExamples() async {\n  final content = await File('data.txt').readAsString();  \u002F\u002F UTF-8, all at once\n  final lines = await File('data.txt').readAsLines();  \u002F\u002F List\u003CString> by line\n  final bytes = await File('image.png').readAsBytes();  \u002F\u002F Uint8List\n\n  \u002F\u002F ── Streaming: process large files without loading into memory ──\n  await for (final chunk in File('large.log').openRead().transform(utf8.decoder)) {\n    processChunk(chunk);  \u002F\u002F process each chunk as it's read — constant memory\n  }\n\n  \u002F\u002F Line-by-line streaming:\n  final stream = File('large.log').openRead().transform(utf8.decoder).transform(LineSplitter());\n  await for (final line in stream) {\n    if (line.startsWith('ERROR')) print(line);  \u002F\u002F filter lines, constant memory\n  }\n}\n\n\u002F\u002F ── Write ──\nFuture\u003Cvoid> writeExamples() async {\n  await File('output.txt').writeAsString('Hello');  \u002F\u002F overwrites\n  await File('output.txt').writeAsString('\\nMore', mode: FileMode.append);  \u002F\u002F append\n  await File('data.bin').writeAsBytes([0, 1, 2]);  \u002F\u002F binary\n\n  \u002F\u002F Streaming write (for large outputs):\n  final sink = File('big.txt').openWrite();  \u002F\u002F IOSink — stream-like\n  for (var i = 0; i \u003C 1000000; i++) {\n    sink.writeln('Line $i');  \u002F\u002F buffered, flushed periodically\n  }\n  await sink.close();  \u002F\u002F ← MUST close to flush remaining buffer\n}\n\n\u002F\u002F ── Directories ──\nFuture\u003Cvoid> dirExamples() async {\n  final dir = Directory('output');\n  await dir.create(recursive: true);  \u002F\u002F mkdir -p (creates parent dirs)\n\n  \u002F\u002F List lazily (Stream — process without loading all entries):\n  await for (final entry in dir.list(recursive: true)) {\n    if (entry is File) print('File: ${entry.path}');\n    else if (entry is Directory) print('Dir: ${entry.path}');\n  }\n\n  await dir.delete(recursive: true);  \u002F\u002F rm -rf\n}\n\n\u002F\u002F ❌ Anti-pattern: sync file operations in a server (blocks the event loop).\n\u002F\u002F File('data.txt').readAsStringSync();  \u002F\u002F blocks all other requests!\n\u002F\u002F Always use the async variants (readAsString, writeAsString) in servers.\n",[18,2004,2005,2014,2023,2027,2032,2051,2081,2108,2136,2140,2145,2185,2196,2200,2204,2209,2244,2262,2288,2292,2296,2300,2305,2324,2351,2388,2426,2430,2435,2461,2495,2520,2524,2539,2543,2547,2552,2571,2590,2613,2617,2622,2650,2687,2724,2728,2732,2752,2756,2760,2765,2773],{"__ignoreMap":31},[35,2006,2007,2009,2012],{"class":37,"line":38},[35,2008,42],{"class":41},[35,2010,2011],{"class":45}," 'dart:io'",[35,2013,50],{"class":49},[35,2015,2016,2018,2021],{"class":37,"line":53},[35,2017,42],{"class":41},[35,2019,2020],{"class":45}," 'dart:convert'",[35,2022,50],{"class":49},[35,2024,2025],{"class":37,"line":67},[35,2026,81],{"emptyLinePlaceholder":80},[35,2028,2029],{"class":37,"line":77},[35,2030,2031],{"class":87},"\u002F\u002F ── Read (async — yields to event loop, doesn't block) ──\n",[35,2033,2034,2036,2038,2040,2042,2045,2047,2049],{"class":37,"line":84},[35,2035,1244],{"class":117},[35,2037,578],{"class":49},[35,2039,822],{"class":41},[35,2041,1251],{"class":49},[35,2043,2044],{"class":121},"readExamples",[35,2046,828],{"class":49},[35,2048,157],{"class":41},[35,2050,160],{"class":49},[35,2052,2053,2055,2058,2060,2062,2065,2067,2070,2073,2075,2078],{"class":37,"line":91},[35,2054,559],{"class":41},[35,2056,2057],{"class":49}," content ",[35,2059,178],{"class":41},[35,2061,181],{"class":41},[35,2063,2064],{"class":117}," File",[35,2066,305],{"class":49},[35,2068,2069],{"class":45},"'data.txt'",[35,2071,2072],{"class":49},").",[35,2074,1282],{"class":121},[35,2076,2077],{"class":49},"();  ",[35,2079,2080],{"class":87},"\u002F\u002F UTF-8, all at once\n",[35,2082,2083,2085,2088,2090,2092,2094,2096,2098,2100,2103,2105],{"class":37,"line":97},[35,2084,559],{"class":41},[35,2086,2087],{"class":49}," lines ",[35,2089,178],{"class":41},[35,2091,181],{"class":41},[35,2093,2064],{"class":117},[35,2095,305],{"class":49},[35,2097,2069],{"class":45},[35,2099,2072],{"class":49},[35,2101,2102],{"class":121},"readAsLines",[35,2104,2077],{"class":49},[35,2106,2107],{"class":87},"\u002F\u002F List\u003CString> by line\n",[35,2109,2110,2112,2115,2117,2119,2121,2123,2126,2128,2131,2133],{"class":37,"line":103},[35,2111,559],{"class":41},[35,2113,2114],{"class":49}," bytes ",[35,2116,178],{"class":41},[35,2118,181],{"class":41},[35,2120,2064],{"class":117},[35,2122,305],{"class":49},[35,2124,2125],{"class":45},"'image.png'",[35,2127,2072],{"class":49},[35,2129,2130],{"class":121},"readAsBytes",[35,2132,2077],{"class":49},[35,2134,2135],{"class":87},"\u002F\u002F Uint8List\n",[35,2137,2138],{"class":37,"line":108},[35,2139,81],{"emptyLinePlaceholder":80},[35,2141,2142],{"class":37,"line":114},[35,2143,2144],{"class":87},"  \u002F\u002F ── Streaming: process large files without loading into memory ──\n",[35,2146,2147,2150,2153,2155,2158,2161,2164,2166,2168,2171,2173,2176,2179,2182],{"class":37,"line":128},[35,2148,2149],{"class":41},"  await",[35,2151,2152],{"class":41}," for",[35,2154,134],{"class":49},[35,2156,2157],{"class":41},"final",[35,2159,2160],{"class":49}," chunk ",[35,2162,2163],{"class":41},"in",[35,2165,2064],{"class":117},[35,2167,305],{"class":49},[35,2169,2170],{"class":45},"'large.log'",[35,2172,2072],{"class":49},[35,2174,2175],{"class":121},"openRead",[35,2177,2178],{"class":49},"().",[35,2180,2181],{"class":121},"transform",[35,2183,2184],{"class":49},"(utf8.decoder)) {\n",[35,2186,2187,2190,2193],{"class":37,"line":143},[35,2188,2189],{"class":121},"    processChunk",[35,2191,2192],{"class":49},"(chunk);  ",[35,2194,2195],{"class":87},"\u002F\u002F process each chunk as it's read — constant memory\n",[35,2197,2198],{"class":37,"line":163},[35,2199,1812],{"class":49},[35,2201,2202],{"class":37,"line":169},[35,2203,81],{"emptyLinePlaceholder":80},[35,2205,2206],{"class":37,"line":190},[35,2207,2208],{"class":87},"  \u002F\u002F Line-by-line streaming:\n",[35,2210,2211,2213,2216,2218,2220,2222,2224,2226,2228,2230,2232,2235,2237,2239,2242],{"class":37,"line":205},[35,2212,559],{"class":41},[35,2214,2215],{"class":49}," stream ",[35,2217,178],{"class":41},[35,2219,2064],{"class":117},[35,2221,305],{"class":49},[35,2223,2170],{"class":45},[35,2225,2072],{"class":49},[35,2227,2175],{"class":121},[35,2229,2178],{"class":49},[35,2231,2181],{"class":121},[35,2233,2234],{"class":49},"(utf8.decoder).",[35,2236,2181],{"class":121},[35,2238,305],{"class":49},[35,2240,2241],{"class":117},"LineSplitter",[35,2243,1862],{"class":49},[35,2245,2246,2248,2250,2252,2254,2257,2259],{"class":37,"line":216},[35,2247,2149],{"class":41},[35,2249,2152],{"class":41},[35,2251,134],{"class":49},[35,2253,2157],{"class":41},[35,2255,2256],{"class":49}," line ",[35,2258,2163],{"class":41},[35,2260,2261],{"class":49}," stream) {\n",[35,2263,2264,2266,2269,2271,2273,2276,2279,2282,2285],{"class":37,"line":230},[35,2265,1931],{"class":41},[35,2267,2268],{"class":49}," (line.",[35,2270,383],{"class":121},[35,2272,305],{"class":49},[35,2274,2275],{"class":45},"'ERROR'",[35,2277,2278],{"class":49},")) ",[35,2280,2281],{"class":121},"print",[35,2283,2284],{"class":49},"(line);  ",[35,2286,2287],{"class":87},"\u002F\u002F filter lines, constant memory\n",[35,2289,2290],{"class":37,"line":243},[35,2291,1812],{"class":49},[35,2293,2294],{"class":37,"line":256},[35,2295,283],{"class":49},[35,2297,2298],{"class":37,"line":262},[35,2299,81],{"emptyLinePlaceholder":80},[35,2301,2302],{"class":37,"line":268},[35,2303,2304],{"class":87},"\u002F\u002F ── Write ──\n",[35,2306,2307,2309,2311,2313,2315,2318,2320,2322],{"class":37,"line":274},[35,2308,1244],{"class":117},[35,2310,578],{"class":49},[35,2312,822],{"class":41},[35,2314,1251],{"class":49},[35,2316,2317],{"class":121},"writeExamples",[35,2319,828],{"class":49},[35,2321,157],{"class":41},[35,2323,160],{"class":49},[35,2325,2326,2328,2330,2332,2335,2337,2340,2342,2345,2348],{"class":37,"line":280},[35,2327,2149],{"class":41},[35,2329,2064],{"class":117},[35,2331,305],{"class":49},[35,2333,2334],{"class":45},"'output.txt'",[35,2336,2072],{"class":49},[35,2338,2339],{"class":121},"writeAsString",[35,2341,305],{"class":49},[35,2343,2344],{"class":45},"'Hello'",[35,2346,2347],{"class":49},");  ",[35,2349,2350],{"class":87},"\u002F\u002F overwrites\n",[35,2352,2353,2355,2357,2359,2361,2363,2365,2367,2369,2371,2374,2377,2379,2382,2385],{"class":37,"line":286},[35,2354,2149],{"class":41},[35,2356,2064],{"class":117},[35,2358,305],{"class":49},[35,2360,2334],{"class":45},[35,2362,2072],{"class":49},[35,2364,2339],{"class":121},[35,2366,305],{"class":49},[35,2368,1099],{"class":45},[35,2370,1143],{"class":117},[35,2372,2373],{"class":45},"More'",[35,2375,2376],{"class":49},", mode",[35,2378,211],{"class":41},[35,2380,2381],{"class":117}," FileMode",[35,2383,2384],{"class":49},".append);  ",[35,2386,2387],{"class":87},"\u002F\u002F append\n",[35,2389,2390,2392,2394,2396,2399,2401,2404,2407,2410,2412,2415,2417,2420,2423],{"class":37,"line":291},[35,2391,2149],{"class":41},[35,2393,2064],{"class":117},[35,2395,305],{"class":49},[35,2397,2398],{"class":45},"'data.bin'",[35,2400,2072],{"class":49},[35,2402,2403],{"class":121},"writeAsBytes",[35,2405,2406],{"class":49},"([",[35,2408,2409],{"class":117},"0",[35,2411,572],{"class":49},[35,2413,2414],{"class":117},"1",[35,2416,572],{"class":49},[35,2418,2419],{"class":117},"2",[35,2421,2422],{"class":49},"]);  ",[35,2424,2425],{"class":87},"\u002F\u002F binary\n",[35,2427,2428],{"class":37,"line":297},[35,2429,81],{"emptyLinePlaceholder":80},[35,2431,2432],{"class":37,"line":314},[35,2433,2434],{"class":87},"  \u002F\u002F Streaming write (for large outputs):\n",[35,2436,2437,2439,2442,2444,2446,2448,2451,2453,2456,2458],{"class":37,"line":325},[35,2438,559],{"class":41},[35,2440,2441],{"class":49}," sink ",[35,2443,178],{"class":41},[35,2445,2064],{"class":117},[35,2447,305],{"class":49},[35,2449,2450],{"class":45},"'big.txt'",[35,2452,2072],{"class":49},[35,2454,2455],{"class":121},"openWrite",[35,2457,2077],{"class":49},[35,2459,2460],{"class":87},"\u002F\u002F IOSink — stream-like\n",[35,2462,2463,2466,2468,2471,2474,2476,2478,2481,2483,2486,2489,2492],{"class":37,"line":340},[35,2464,2465],{"class":41},"  for",[35,2467,134],{"class":49},[35,2469,2470],{"class":41},"var",[35,2472,2473],{"class":49}," i ",[35,2475,178],{"class":41},[35,2477,747],{"class":117},[35,2479,2480],{"class":49},"; i ",[35,2482,578],{"class":41},[35,2484,2485],{"class":117}," 1000000",[35,2487,2488],{"class":49},"; i",[35,2490,2491],{"class":41},"++",[35,2493,2494],{"class":49},") {\n",[35,2496,2497,2500,2503,2505,2508,2510,2513,2515,2517],{"class":37,"line":359},[35,2498,2499],{"class":49},"    sink.",[35,2501,2502],{"class":121},"writeln",[35,2504,305],{"class":49},[35,2506,2507],{"class":45},"'Line ",[35,2509,1212],{"class":45},[35,2511,2512],{"class":117},"i",[35,2514,1099],{"class":45},[35,2516,2347],{"class":49},[35,2518,2519],{"class":87},"\u002F\u002F buffered, flushed periodically\n",[35,2521,2522],{"class":37,"line":394},[35,2523,1812],{"class":49},[35,2525,2526,2528,2531,2534,2536],{"class":37,"line":419},[35,2527,2149],{"class":41},[35,2529,2530],{"class":49}," sink.",[35,2532,2533],{"class":121},"close",[35,2535,2077],{"class":49},[35,2537,2538],{"class":87},"\u002F\u002F ← MUST close to flush remaining buffer\n",[35,2540,2541],{"class":37,"line":425},[35,2542,283],{"class":49},[35,2544,2545],{"class":37,"line":448},[35,2546,81],{"emptyLinePlaceholder":80},[35,2548,2549],{"class":37,"line":454},[35,2550,2551],{"class":87},"\u002F\u002F ── Directories ──\n",[35,2553,2554,2556,2558,2560,2562,2565,2567,2569],{"class":37,"line":460},[35,2555,1244],{"class":117},[35,2557,578],{"class":49},[35,2559,822],{"class":41},[35,2561,1251],{"class":49},[35,2563,2564],{"class":121},"dirExamples",[35,2566,828],{"class":49},[35,2568,157],{"class":41},[35,2570,160],{"class":49},[35,2572,2573,2575,2578,2580,2583,2585,2588],{"class":37,"line":474},[35,2574,559],{"class":41},[35,2576,2577],{"class":49}," dir ",[35,2579,178],{"class":41},[35,2581,2582],{"class":117}," Directory",[35,2584,305],{"class":49},[35,2586,2587],{"class":45},"'output'",[35,2589,416],{"class":49},[35,2591,2592,2594,2597,2600,2603,2605,2608,2610],{"class":37,"line":499},[35,2593,2149],{"class":41},[35,2595,2596],{"class":49}," dir.",[35,2598,2599],{"class":121},"create",[35,2601,2602],{"class":49},"(recursive",[35,2604,211],{"class":41},[35,2606,2607],{"class":117}," true",[35,2609,2347],{"class":49},[35,2611,2612],{"class":87},"\u002F\u002F mkdir -p (creates parent dirs)\n",[35,2614,2615],{"class":37,"line":505},[35,2616,81],{"emptyLinePlaceholder":80},[35,2618,2619],{"class":37,"line":510},[35,2620,2621],{"class":87},"  \u002F\u002F List lazily (Stream — process without loading all entries):\n",[35,2623,2624,2626,2628,2630,2632,2635,2637,2639,2642,2644,2646,2648],{"class":37,"line":515},[35,2625,2149],{"class":41},[35,2627,2152],{"class":41},[35,2629,134],{"class":49},[35,2631,2157],{"class":41},[35,2633,2634],{"class":49}," entry ",[35,2636,2163],{"class":41},[35,2638,2596],{"class":49},[35,2640,2641],{"class":121},"list",[35,2643,2602],{"class":49},[35,2645,211],{"class":41},[35,2647,2607],{"class":117},[35,2649,391],{"class":49},[35,2651,2652,2654,2657,2660,2662,2664,2666,2668,2671,2673,2676,2678,2681,2683,2685],{"class":37,"line":520},[35,2653,1931],{"class":41},[35,2655,2656],{"class":49}," (entry ",[35,2658,2659],{"class":41},"is",[35,2661,2064],{"class":117},[35,2663,750],{"class":49},[35,2665,2281],{"class":121},[35,2667,305],{"class":49},[35,2669,2670],{"class":45},"'File: ",[35,2672,1068],{"class":45},[35,2674,2675],{"class":117},"entry",[35,2677,655],{"class":45},[35,2679,2680],{"class":117},"path",[35,2682,1083],{"class":45},[35,2684,1099],{"class":45},[35,2686,416],{"class":49},[35,2688,2689,2692,2695,2697,2699,2701,2703,2705,2707,2710,2712,2714,2716,2718,2720,2722],{"class":37,"line":525},[35,2690,2691],{"class":41},"    else",[35,2693,2694],{"class":41}," if",[35,2696,2656],{"class":49},[35,2698,2659],{"class":41},[35,2700,2582],{"class":117},[35,2702,750],{"class":49},[35,2704,2281],{"class":121},[35,2706,305],{"class":49},[35,2708,2709],{"class":45},"'Dir: ",[35,2711,1068],{"class":45},[35,2713,2675],{"class":117},[35,2715,655],{"class":45},[35,2717,2680],{"class":117},[35,2719,1083],{"class":45},[35,2721,1099],{"class":45},[35,2723,416],{"class":49},[35,2725,2726],{"class":37,"line":531},[35,2727,1812],{"class":49},[35,2729,2730],{"class":37,"line":556},[35,2731,81],{"emptyLinePlaceholder":80},[35,2733,2734,2736,2738,2741,2743,2745,2747,2749],{"class":37,"line":592},[35,2735,2149],{"class":41},[35,2737,2596],{"class":49},[35,2739,2740],{"class":121},"delete",[35,2742,2602],{"class":49},[35,2744,211],{"class":41},[35,2746,2607],{"class":117},[35,2748,2347],{"class":49},[35,2750,2751],{"class":87},"\u002F\u002F rm -rf\n",[35,2753,2754],{"class":37,"line":603},[35,2755,283],{"class":49},[35,2757,2758],{"class":37,"line":618},[35,2759,81],{"emptyLinePlaceholder":80},[35,2761,2762],{"class":37,"line":642},[35,2763,2764],{"class":87},"\u002F\u002F ❌ Anti-pattern: sync file operations in a server (blocks the event loop).\n",[35,2766,2767,2770],{"class":37,"line":664},[35,2768,2769],{"class":87},"\u002F\u002F File('data.txt').readAsStringSync();",[35,2771,2772],{"class":87},"  \u002F\u002F blocks all other requests!\n",[35,2774,2775],{"class":37,"line":709},[35,2776,2777],{"class":87},"\u002F\u002F Always use the async variants (readAsString, writeAsString) in servers.\n",[14,2779,2781],{"id":2780},"database-parameterized-queries-sql-injection-prevention","Database — Parameterized Queries (SQL Injection Prevention)",[23,2783,2784,3246,3253,3695,3699,4203,4207],{"language":25},[27,2785,2787],{"className":29,"code":2786,"language":25,"meta":31,"style":31},"import 'package:postgres\u002Fpostgres.dart';\n\n\u002F\u002F ❌ Anti-pattern: string interpolation in SQL — SQL INJECTION.\nFuture\u003CList\u003CMap>> badQuery(PostgreSQLConnection conn, String name) async {\n  return conn.mappedResultsQuery(\n    \"SELECT * FROM users WHERE name = '$name'\",  \u002F\u002F ← INJECTION VULNERABILITY\n  );\n  \u002F\u002F If name = \"'; DROP TABLE users; --\", the query becomes:\n  \u002F\u002F SELECT * FROM users WHERE name = ''; DROP TABLE users; --'\n  \u002F\u002F → drops the users table!\n}\n\n\u002F\u002F ✓ Correct: parameterized queries — the driver escapes the value.\nFuture\u003CList\u003CMap>> goodQuery(PostgreSQLConnection conn, String name) async {\n  return conn.mappedResultsQuery(\n    'SELECT * FROM users WHERE name = @name',\n    substitutionValues: {'name': name},  \u002F\u002F ← safe: name is never parsed as SQL\n  );\n}\n\n\u002F\u002F ── Transactions ──\nFuture\u003Cvoid> transferFunds(PostgreSQLConnection conn, int from, int to, int amount) async {\n  await conn.transaction((conn) async {\n    \u002F\u002F All queries in this block run in a single transaction.\n    \u002F\u002F If any throws, the transaction is rolled back automatically.\n    await conn.execute(\n      'UPDATE accounts SET balance = balance - @amount WHERE id = @from',\n      substitutionValues: {'amount': amount, 'from': from},\n    );\n    await conn.execute(\n      'UPDATE accounts SET balance = balance + @amount WHERE id = @to',\n      substitutionValues: {'amount': amount, 'to': to},\n    );\n    \u002F\u002F If we throw here, both updates are rolled back.\n    final balance = await conn.query(\n      'SELECT balance FROM accounts WHERE id = @id',\n      substitutionValues: {'id': from},\n    );\n    if (balance.first[0] as int \u003C 0) {\n      throw Exception('Insufficient funds — rollback');\n    }\n  });\n}\n",[18,2788,2789,2798,2802,2807,2843,2855,2874,2879,2884,2889,2894,2898,2902,2907,2938,2948,2955,2974,2978,2982,2986,2991,3029,3045,3050,3055,3067,3074,3099,3104,3114,3121,3143,3147,3152,3170,3177,3191,3195,3217,3232,3237,3242],{"__ignoreMap":31},[35,2790,2791,2793,2796],{"class":37,"line":38},[35,2792,42],{"class":41},[35,2794,2795],{"class":45}," 'package:postgres\u002Fpostgres.dart'",[35,2797,50],{"class":49},[35,2799,2800],{"class":37,"line":53},[35,2801,81],{"emptyLinePlaceholder":80},[35,2803,2804],{"class":37,"line":67},[35,2805,2806],{"class":87},"\u002F\u002F ❌ Anti-pattern: string interpolation in SQL — SQL INJECTION.\n",[35,2808,2809,2811,2813,2815,2817,2820,2823,2826,2828,2831,2834,2836,2839,2841],{"class":37,"line":77},[35,2810,1244],{"class":117},[35,2812,578],{"class":49},[35,2814,575],{"class":117},[35,2816,578],{"class":49},[35,2818,2819],{"class":117},"Map",[35,2821,2822],{"class":49},">> ",[35,2824,2825],{"class":121},"badQuery",[35,2827,305],{"class":49},[35,2829,2830],{"class":117},"PostgreSQLConnection",[35,2832,2833],{"class":49}," conn, ",[35,2835,308],{"class":117},[35,2837,2838],{"class":49}," name) ",[35,2840,157],{"class":41},[35,2842,160],{"class":49},[35,2844,2845,2847,2850,2853],{"class":37,"line":84},[35,2846,131],{"class":41},[35,2848,2849],{"class":49}," conn.",[35,2851,2852],{"class":121},"mappedResultsQuery",[35,2854,202],{"class":49},[35,2856,2857,2860,2862,2865,2868,2871],{"class":37,"line":91},[35,2858,2859],{"class":45},"    \"SELECT * FROM users WHERE name = '",[35,2861,1212],{"class":45},[35,2863,2864],{"class":117},"name",[35,2866,2867],{"class":45},"'\"",[35,2869,2870],{"class":49},",  ",[35,2872,2873],{"class":87},"\u002F\u002F ← INJECTION VULNERABILITY\n",[35,2875,2876],{"class":37,"line":97},[35,2877,2878],{"class":49},"  );\n",[35,2880,2881],{"class":37,"line":103},[35,2882,2883],{"class":87},"  \u002F\u002F If name = \"'; DROP TABLE users; --\", the query becomes:\n",[35,2885,2886],{"class":37,"line":108},[35,2887,2888],{"class":87},"  \u002F\u002F SELECT * FROM users WHERE name = ''; DROP TABLE users; --'\n",[35,2890,2891],{"class":37,"line":114},[35,2892,2893],{"class":87},"  \u002F\u002F → drops the users table!\n",[35,2895,2896],{"class":37,"line":128},[35,2897,283],{"class":49},[35,2899,2900],{"class":37,"line":143},[35,2901,81],{"emptyLinePlaceholder":80},[35,2903,2904],{"class":37,"line":163},[35,2905,2906],{"class":87},"\u002F\u002F ✓ Correct: parameterized queries — the driver escapes the value.\n",[35,2908,2909,2911,2913,2915,2917,2919,2921,2924,2926,2928,2930,2932,2934,2936],{"class":37,"line":169},[35,2910,1244],{"class":117},[35,2912,578],{"class":49},[35,2914,575],{"class":117},[35,2916,578],{"class":49},[35,2918,2819],{"class":117},[35,2920,2822],{"class":49},[35,2922,2923],{"class":121},"goodQuery",[35,2925,305],{"class":49},[35,2927,2830],{"class":117},[35,2929,2833],{"class":49},[35,2931,308],{"class":117},[35,2933,2838],{"class":49},[35,2935,157],{"class":41},[35,2937,160],{"class":49},[35,2939,2940,2942,2944,2946],{"class":37,"line":190},[35,2941,131],{"class":41},[35,2943,2849],{"class":49},[35,2945,2852],{"class":121},[35,2947,202],{"class":49},[35,2949,2950,2953],{"class":37,"line":205},[35,2951,2952],{"class":45},"    'SELECT * FROM users WHERE name = @name'",[35,2954,227],{"class":49},[35,2956,2957,2960,2962,2964,2966,2968,2971],{"class":37,"line":216},[35,2958,2959],{"class":49},"    substitutionValues",[35,2961,211],{"class":41},[35,2963,482],{"class":49},[35,2965,1353],{"class":45},[35,2967,211],{"class":41},[35,2969,2970],{"class":49}," name},  ",[35,2972,2973],{"class":87},"\u002F\u002F ← safe: name is never parsed as SQL\n",[35,2975,2976],{"class":37,"line":230},[35,2977,2878],{"class":49},[35,2979,2980],{"class":37,"line":243},[35,2981,283],{"class":49},[35,2983,2984],{"class":37,"line":256},[35,2985,81],{"emptyLinePlaceholder":80},[35,2987,2988],{"class":37,"line":262},[35,2989,2990],{"class":87},"\u002F\u002F ── Transactions ──\n",[35,2992,2993,2995,2997,2999,3001,3004,3006,3008,3010,3012,3015,3017,3020,3022,3025,3027],{"class":37,"line":268},[35,2994,1244],{"class":117},[35,2996,578],{"class":49},[35,2998,822],{"class":41},[35,3000,1251],{"class":49},[35,3002,3003],{"class":121},"transferFunds",[35,3005,305],{"class":49},[35,3007,2830],{"class":117},[35,3009,2833],{"class":49},[35,3011,542],{"class":117},[35,3013,3014],{"class":49}," from, ",[35,3016,542],{"class":117},[35,3018,3019],{"class":49}," to, ",[35,3021,542],{"class":117},[35,3023,3024],{"class":49}," amount) ",[35,3026,157],{"class":41},[35,3028,160],{"class":49},[35,3030,3031,3033,3035,3038,3041,3043],{"class":37,"line":274},[35,3032,2149],{"class":41},[35,3034,2849],{"class":49},[35,3036,3037],{"class":121},"transaction",[35,3039,3040],{"class":49},"((conn) ",[35,3042,157],{"class":41},[35,3044,160],{"class":49},[35,3046,3047],{"class":37,"line":280},[35,3048,3049],{"class":87},"    \u002F\u002F All queries in this block run in a single transaction.\n",[35,3051,3052],{"class":37,"line":286},[35,3053,3054],{"class":87},"    \u002F\u002F If any throws, the transaction is rolled back automatically.\n",[35,3056,3057,3060,3062,3065],{"class":37,"line":291},[35,3058,3059],{"class":41},"    await",[35,3061,2849],{"class":49},[35,3063,3064],{"class":121},"execute",[35,3066,202],{"class":49},[35,3068,3069,3072],{"class":37,"line":297},[35,3070,3071],{"class":45},"      'UPDATE accounts SET balance = balance - @amount WHERE id = @from'",[35,3073,227],{"class":49},[35,3075,3076,3079,3081,3083,3086,3088,3091,3094,3096],{"class":37,"line":314},[35,3077,3078],{"class":49},"      substitutionValues",[35,3080,211],{"class":41},[35,3082,482],{"class":49},[35,3084,3085],{"class":45},"'amount'",[35,3087,211],{"class":41},[35,3089,3090],{"class":49}," amount, ",[35,3092,3093],{"class":45},"'from'",[35,3095,211],{"class":41},[35,3097,3098],{"class":49}," from},\n",[35,3100,3101],{"class":37,"line":325},[35,3102,3103],{"class":49},"    );\n",[35,3105,3106,3108,3110,3112],{"class":37,"line":340},[35,3107,3059],{"class":41},[35,3109,2849],{"class":49},[35,3111,3064],{"class":121},[35,3113,202],{"class":49},[35,3115,3116,3119],{"class":37,"line":359},[35,3117,3118],{"class":45},"      'UPDATE accounts SET balance = balance + @amount WHERE id = @to'",[35,3120,227],{"class":49},[35,3122,3123,3125,3127,3129,3131,3133,3135,3138,3140],{"class":37,"line":394},[35,3124,3078],{"class":49},[35,3126,211],{"class":41},[35,3128,482],{"class":49},[35,3130,3085],{"class":45},[35,3132,211],{"class":41},[35,3134,3090],{"class":49},[35,3136,3137],{"class":45},"'to'",[35,3139,211],{"class":41},[35,3141,3142],{"class":49}," to},\n",[35,3144,3145],{"class":37,"line":419},[35,3146,3103],{"class":49},[35,3148,3149],{"class":37,"line":425},[35,3150,3151],{"class":87},"    \u002F\u002F If we throw here, both updates are rolled back.\n",[35,3153,3154,3156,3159,3161,3163,3165,3168],{"class":37,"line":448},[35,3155,1915],{"class":41},[35,3157,3158],{"class":49}," balance ",[35,3160,178],{"class":41},[35,3162,181],{"class":41},[35,3164,2849],{"class":49},[35,3166,3167],{"class":121},"query",[35,3169,202],{"class":49},[35,3171,3172,3175],{"class":37,"line":454},[35,3173,3174],{"class":45},"      'SELECT balance FROM accounts WHERE id = @id'",[35,3176,227],{"class":49},[35,3178,3179,3181,3183,3185,3187,3189],{"class":37,"line":460},[35,3180,3078],{"class":49},[35,3182,211],{"class":41},[35,3184,482],{"class":49},[35,3186,1552],{"class":45},[35,3188,211],{"class":41},[35,3190,3098],{"class":49},[35,3192,3193],{"class":37,"line":474},[35,3194,3103],{"class":49},[35,3196,3197,3199,3202,3204,3206,3208,3211,3213,3215],{"class":37,"line":499},[35,3198,1931],{"class":41},[35,3200,3201],{"class":49}," (balance.first[",[35,3203,2409],{"class":117},[35,3205,633],{"class":49},[35,3207,1303],{"class":41},[35,3209,3210],{"class":117}," int",[35,3212,567],{"class":41},[35,3214,747],{"class":117},[35,3216,2494],{"class":49},[35,3218,3219,3222,3225,3227,3230],{"class":37,"line":505},[35,3220,3221],{"class":41},"      throw",[35,3223,3224],{"class":117}," Exception",[35,3226,305],{"class":49},[35,3228,3229],{"class":45},"'Insufficient funds — rollback'",[35,3231,416],{"class":49},[35,3233,3234],{"class":37,"line":510},[35,3235,3236],{"class":49},"    }\n",[35,3238,3239],{"class":37,"line":515},[35,3240,3241],{"class":49},"  });\n",[35,3243,3244],{"class":37,"line":520},[35,3245,283],{"class":49},[14,3247,3249,3250],{"id":3248},"sqlite-with-sqlite3","SQLite with ",[18,3251,3252],{},"sqlite3",[23,3254,3255],{"language":25},[27,3256,3258],{"className":29,"code":3257,"language":25,"meta":31,"style":31},"import 'package:sqlite3\u002Fsqlite3.dart';\n\nvoid main() {\n  final db = sqlite3.open('app.db');\n  db.execute('''\n    CREATE TABLE IF NOT EXISTS users (\n      id INTEGER PRIMARY KEY AUTOINCREMENT,\n      name TEXT NOT NULL,\n      email TEXT UNIQUE,\n      created_at TEXT DEFAULT CURRENT_TIMESTAMP\n    )\n  ''');\n\n  \u002F\u002F ── Prepared statements (parameterized — safe from injection) ──\n  final insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');\n  insert.execute(['Alice', 'alice@example.com']);\n  insert.execute(['Bob', 'bob@example.com']);\n  insert.dispose();  \u002F\u002F ← MUST dispose to release resources\n\n  \u002F\u002F ── Query with parameters ──\n  final query = db.prepare('SELECT * FROM users WHERE name LIKE ?');\n  final results = query.select(['%li%']);  \u002F\u002F matches 'Alice'\n  for (final row in results) {\n    print('${row['id']}: ${row['name']} \u003C${row['email']}>');\n  }\n  query.dispose();\n\n  \u002F\u002F ── Transaction ──\n  db.execute('BEGIN');\n  try {\n    db.execute(\"INSERT INTO users (name) VALUES ('Temp')\");\n    \u002F\u002F If we throw here, the transaction is open — rollback:\n    db.execute('ROLLBACK');\n  } catch (e) {\n    db.execute('ROLLBACK');\n    rethrow;\n  }\n  db.execute('COMMIT');\n\n  db.dispose();  \u002F\u002F close the database\n}\n",[18,3259,3260,3269,3273,3281,3303,3315,3320,3325,3330,3335,3340,3345,3352,3356,3361,3382,3402,3420,3432,3436,3441,3461,3486,3502,3555,3559,3568,3572,3577,3590,3597,3611,3616,3629,3640,3652,3659,3663,3676,3680,3691],{"__ignoreMap":31},[35,3261,3262,3264,3267],{"class":37,"line":38},[35,3263,42],{"class":41},[35,3265,3266],{"class":45}," 'package:sqlite3\u002Fsqlite3.dart'",[35,3268,50],{"class":49},[35,3270,3271],{"class":37,"line":53},[35,3272,81],{"emptyLinePlaceholder":80},[35,3274,3275,3277,3279],{"class":37,"line":67},[35,3276,822],{"class":41},[35,3278,825],{"class":121},[35,3280,125],{"class":49},[35,3282,3283,3285,3288,3290,3293,3296,3298,3301],{"class":37,"line":77},[35,3284,559],{"class":41},[35,3286,3287],{"class":49}," db ",[35,3289,178],{"class":41},[35,3291,3292],{"class":49}," sqlite3.",[35,3294,3295],{"class":121},"open",[35,3297,305],{"class":49},[35,3299,3300],{"class":45},"'app.db'",[35,3302,416],{"class":49},[35,3304,3305,3308,3310,3312],{"class":37,"line":84},[35,3306,3307],{"class":49},"  db.",[35,3309,3064],{"class":121},[35,3311,305],{"class":49},[35,3313,3314],{"class":45},"'''\n",[35,3316,3317],{"class":37,"line":91},[35,3318,3319],{"class":45},"    CREATE TABLE IF NOT EXISTS users (\n",[35,3321,3322],{"class":37,"line":97},[35,3323,3324],{"class":45},"      id INTEGER PRIMARY KEY AUTOINCREMENT,\n",[35,3326,3327],{"class":37,"line":103},[35,3328,3329],{"class":45},"      name TEXT NOT NULL,\n",[35,3331,3332],{"class":37,"line":108},[35,3333,3334],{"class":45},"      email TEXT UNIQUE,\n",[35,3336,3337],{"class":37,"line":114},[35,3338,3339],{"class":45},"      created_at TEXT DEFAULT CURRENT_TIMESTAMP\n",[35,3341,3342],{"class":37,"line":128},[35,3343,3344],{"class":45},"    )\n",[35,3346,3347,3350],{"class":37,"line":143},[35,3348,3349],{"class":45},"  '''",[35,3351,416],{"class":49},[35,3353,3354],{"class":37,"line":163},[35,3355,81],{"emptyLinePlaceholder":80},[35,3357,3358],{"class":37,"line":169},[35,3359,3360],{"class":87},"  \u002F\u002F ── Prepared statements (parameterized — safe from injection) ──\n",[35,3362,3363,3365,3368,3370,3372,3375,3377,3380],{"class":37,"line":190},[35,3364,559],{"class":41},[35,3366,3367],{"class":49}," insert ",[35,3369,178],{"class":41},[35,3371,1826],{"class":49},[35,3373,3374],{"class":121},"prepare",[35,3376,305],{"class":49},[35,3378,3379],{"class":45},"'INSERT INTO users (name, email) VALUES (?, ?)'",[35,3381,416],{"class":49},[35,3383,3384,3387,3389,3391,3394,3396,3399],{"class":37,"line":205},[35,3385,3386],{"class":49},"  insert.",[35,3388,3064],{"class":121},[35,3390,2406],{"class":49},[35,3392,3393],{"class":45},"'Alice'",[35,3395,572],{"class":49},[35,3397,3398],{"class":45},"'alice@example.com'",[35,3400,3401],{"class":49},"]);\n",[35,3403,3404,3406,3408,3410,3413,3415,3418],{"class":37,"line":216},[35,3405,3386],{"class":49},[35,3407,3064],{"class":121},[35,3409,2406],{"class":49},[35,3411,3412],{"class":45},"'Bob'",[35,3414,572],{"class":49},[35,3416,3417],{"class":45},"'bob@example.com'",[35,3419,3401],{"class":49},[35,3421,3422,3424,3427,3429],{"class":37,"line":230},[35,3423,3386],{"class":49},[35,3425,3426],{"class":121},"dispose",[35,3428,2077],{"class":49},[35,3430,3431],{"class":87},"\u002F\u002F ← MUST dispose to release resources\n",[35,3433,3434],{"class":37,"line":243},[35,3435,81],{"emptyLinePlaceholder":80},[35,3437,3438],{"class":37,"line":256},[35,3439,3440],{"class":87},"  \u002F\u002F ── Query with parameters ──\n",[35,3442,3443,3445,3448,3450,3452,3454,3456,3459],{"class":37,"line":262},[35,3444,559],{"class":41},[35,3446,3447],{"class":49}," query ",[35,3449,178],{"class":41},[35,3451,1826],{"class":49},[35,3453,3374],{"class":121},[35,3455,305],{"class":49},[35,3457,3458],{"class":45},"'SELECT * FROM users WHERE name LIKE ?'",[35,3460,416],{"class":49},[35,3462,3463,3465,3468,3470,3473,3476,3478,3481,3483],{"class":37,"line":268},[35,3464,559],{"class":41},[35,3466,3467],{"class":49}," results ",[35,3469,178],{"class":41},[35,3471,3472],{"class":49}," query.",[35,3474,3475],{"class":121},"select",[35,3477,2406],{"class":49},[35,3479,3480],{"class":45},"'%li%'",[35,3482,2422],{"class":49},[35,3484,3485],{"class":87},"\u002F\u002F matches 'Alice'\n",[35,3487,3488,3490,3492,3494,3497,3499],{"class":37,"line":274},[35,3489,2465],{"class":41},[35,3491,134],{"class":49},[35,3493,2157],{"class":41},[35,3495,3496],{"class":49}," row ",[35,3498,2163],{"class":41},[35,3500,3501],{"class":49}," results) {\n",[35,3503,3504,3507,3509,3511,3513,3516,3519,3521,3524,3527,3529,3531,3533,3535,3537,3539,3541,3543,3545,3548,3550,3553],{"class":37,"line":280},[35,3505,3506],{"class":121},"    print",[35,3508,305],{"class":49},[35,3510,1099],{"class":45},[35,3512,1068],{"class":45},[35,3514,3515],{"class":117},"row",[35,3517,3518],{"class":45},"[",[35,3520,1552],{"class":45},[35,3522,3523],{"class":45},"]}",[35,3525,3526],{"class":45},": ",[35,3528,1068],{"class":45},[35,3530,3515],{"class":117},[35,3532,3518],{"class":45},[35,3534,1353],{"class":45},[35,3536,3523],{"class":45},[35,3538,567],{"class":45},[35,3540,1068],{"class":45},[35,3542,3515],{"class":117},[35,3544,3518],{"class":45},[35,3546,3547],{"class":45},"'email'",[35,3549,3523],{"class":45},[35,3551,3552],{"class":45},">'",[35,3554,416],{"class":49},[35,3556,3557],{"class":37,"line":286},[35,3558,1812],{"class":49},[35,3560,3561,3564,3566],{"class":37,"line":291},[35,3562,3563],{"class":49},"  query.",[35,3565,3426],{"class":121},[35,3567,661],{"class":49},[35,3569,3570],{"class":37,"line":297},[35,3571,81],{"emptyLinePlaceholder":80},[35,3573,3574],{"class":37,"line":314},[35,3575,3576],{"class":87},"  \u002F\u002F ── Transaction ──\n",[35,3578,3579,3581,3583,3585,3588],{"class":37,"line":325},[35,3580,3307],{"class":49},[35,3582,3064],{"class":121},[35,3584,305],{"class":49},[35,3586,3587],{"class":45},"'BEGIN'",[35,3589,416],{"class":49},[35,3591,3592,3595],{"class":37,"line":340},[35,3593,3594],{"class":41},"  try",[35,3596,160],{"class":49},[35,3598,3599,3602,3604,3606,3609],{"class":37,"line":359},[35,3600,3601],{"class":49},"    db.",[35,3603,3064],{"class":121},[35,3605,305],{"class":49},[35,3607,3608],{"class":45},"\"INSERT INTO users (name) VALUES ('Temp')\"",[35,3610,416],{"class":49},[35,3612,3613],{"class":37,"line":394},[35,3614,3615],{"class":87},"    \u002F\u002F If we throw here, the transaction is open — rollback:\n",[35,3617,3618,3620,3622,3624,3627],{"class":37,"line":419},[35,3619,3601],{"class":49},[35,3621,3064],{"class":121},[35,3623,305],{"class":49},[35,3625,3626],{"class":45},"'ROLLBACK'",[35,3628,416],{"class":49},[35,3630,3631,3634,3637],{"class":37,"line":425},[35,3632,3633],{"class":49},"  } ",[35,3635,3636],{"class":41},"catch",[35,3638,3639],{"class":49}," (e) {\n",[35,3641,3642,3644,3646,3648,3650],{"class":37,"line":448},[35,3643,3601],{"class":49},[35,3645,3064],{"class":121},[35,3647,305],{"class":49},[35,3649,3626],{"class":45},[35,3651,416],{"class":49},[35,3653,3654,3657],{"class":37,"line":454},[35,3655,3656],{"class":41},"    rethrow",[35,3658,50],{"class":49},[35,3660,3661],{"class":37,"line":460},[35,3662,1812],{"class":49},[35,3664,3665,3667,3669,3671,3674],{"class":37,"line":474},[35,3666,3307],{"class":49},[35,3668,3064],{"class":121},[35,3670,305],{"class":49},[35,3672,3673],{"class":45},"'COMMIT'",[35,3675,416],{"class":49},[35,3677,3678],{"class":37,"line":499},[35,3679,81],{"emptyLinePlaceholder":80},[35,3681,3682,3684,3686,3688],{"class":37,"line":505},[35,3683,3307],{"class":49},[35,3685,3426],{"class":121},[35,3687,2077],{"class":49},[35,3689,3690],{"class":87},"\u002F\u002F close the database\n",[35,3692,3693],{"class":37,"line":510},[35,3694,283],{"class":49},[14,3696,3698],{"id":3697},"websocket-server","WebSocket Server",[23,3700,3701],{"language":25},[27,3702,3704],{"className":29,"code":3703,"language":25,"meta":31,"style":31},"import 'dart:io';\n\n\u002F\u002F ── Raw HttpServer WebSocket upgrade ──\nFuture\u003Cvoid> startWebSocketServer() async {\n  final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);\n  print('WebSocket server on ws:\u002F\u002Flocalhost:8080');\n\n  await for (final request in server) {\n    if (WebSocketTransformer.isUpgradeRequest(request)) {\n      final socket = await WebSocketTransformer.upgrade(request);\n\n      \u002F\u002F Each connection is a bidirectional stream.\n      socket.listen(\n        (message) {\n          print('Received: $message');\n          socket.add('Echo: $message');  \u002F\u002F send back\n        },\n        onError: (e) => print('Socket error: $e'),\n        onDone: () => print('Socket closed'),\n        cancelOnError: true,\n      );\n    } else {\n      request.response.statusCode = HttpStatus.forbidden;\n      await request.response.close();\n    }\n  }\n}\n\n\u002F\u002F ── Broadcast pattern (chat room) ──\nclass ChatRoom {\n  final _clients = \u003CWebSocket>{};\n\n  void addClient(WebSocket socket) {\n    _clients.add(socket);\n    socket.listen(\n      (message) {\n        \u002F\u002F Broadcast to all other clients:\n        for (final client in _clients) {\n          if (client != socket) client.add(message);\n        }\n      },\n      onDone: () => _clients.remove(socket),\n      onError: (e) => _clients.remove(socket),\n    );\n  }\n}\n",[18,3705,3706,3714,3718,3723,3742,3770,3781,3785,3803,3820,3841,3845,3850,3860,3865,3884,3907,3911,3941,3962,3973,3977,3987,3999,4011,4015,4019,4023,4027,4032,4042,4060,4064,4079,4089,4098,4103,4108,4125,4144,4149,4154,4174,4191,4195,4199],{"__ignoreMap":31},[35,3707,3708,3710,3712],{"class":37,"line":38},[35,3709,42],{"class":41},[35,3711,2011],{"class":45},[35,3713,50],{"class":49},[35,3715,3716],{"class":37,"line":53},[35,3717,81],{"emptyLinePlaceholder":80},[35,3719,3720],{"class":37,"line":67},[35,3721,3722],{"class":87},"\u002F\u002F ── Raw HttpServer WebSocket upgrade ──\n",[35,3724,3725,3727,3729,3731,3733,3736,3738,3740],{"class":37,"line":77},[35,3726,1244],{"class":117},[35,3728,578],{"class":49},[35,3730,822],{"class":41},[35,3732,1251],{"class":49},[35,3734,3735],{"class":121},"startWebSocketServer",[35,3737,828],{"class":49},[35,3739,157],{"class":41},[35,3741,160],{"class":49},[35,3743,3744,3746,3748,3750,3752,3755,3757,3760,3762,3764,3766,3768],{"class":37,"line":84},[35,3745,559],{"class":41},[35,3747,1030],{"class":49},[35,3749,178],{"class":41},[35,3751,181],{"class":41},[35,3753,3754],{"class":117}," HttpServer",[35,3756,655],{"class":49},[35,3758,3759],{"class":121},"bind",[35,3761,305],{"class":49},[35,3763,1046],{"class":117},[35,3765,1049],{"class":49},[35,3767,1052],{"class":117},[35,3769,416],{"class":49},[35,3771,3772,3774,3776,3779],{"class":37,"line":91},[35,3773,1060],{"class":121},[35,3775,305],{"class":49},[35,3777,3778],{"class":45},"'WebSocket server on ws:\u002F\u002Flocalhost:8080'",[35,3780,416],{"class":49},[35,3782,3783],{"class":37,"line":97},[35,3784,81],{"emptyLinePlaceholder":80},[35,3786,3787,3789,3791,3793,3795,3798,3800],{"class":37,"line":103},[35,3788,2149],{"class":41},[35,3790,2152],{"class":41},[35,3792,134],{"class":49},[35,3794,2157],{"class":41},[35,3796,3797],{"class":49}," request ",[35,3799,2163],{"class":41},[35,3801,3802],{"class":49}," server) {\n",[35,3804,3805,3807,3809,3812,3814,3817],{"class":37,"line":108},[35,3806,1931],{"class":41},[35,3808,134],{"class":49},[35,3810,3811],{"class":117},"WebSocketTransformer",[35,3813,655],{"class":49},[35,3815,3816],{"class":121},"isUpgradeRequest",[35,3818,3819],{"class":49},"(request)) {\n",[35,3821,3822,3824,3827,3829,3831,3834,3836,3839],{"class":37,"line":114},[35,3823,172],{"class":41},[35,3825,3826],{"class":49}," socket ",[35,3828,178],{"class":41},[35,3830,181],{"class":41},[35,3832,3833],{"class":117}," WebSocketTransformer",[35,3835,655],{"class":49},[35,3837,3838],{"class":121},"upgrade",[35,3840,187],{"class":49},[35,3842,3843],{"class":37,"line":128},[35,3844,81],{"emptyLinePlaceholder":80},[35,3846,3847],{"class":37,"line":143},[35,3848,3849],{"class":87},"      \u002F\u002F Each connection is a bidirectional stream.\n",[35,3851,3852,3855,3858],{"class":37,"line":163},[35,3853,3854],{"class":49},"      socket.",[35,3856,3857],{"class":121},"listen",[35,3859,202],{"class":49},[35,3861,3862],{"class":37,"line":169},[35,3863,3864],{"class":49},"        (message) {\n",[35,3866,3867,3870,3872,3875,3877,3880,3882],{"class":37,"line":190},[35,3868,3869],{"class":121},"          print",[35,3871,305],{"class":49},[35,3873,3874],{"class":45},"'Received: ",[35,3876,1212],{"class":45},[35,3878,3879],{"class":117},"message",[35,3881,1099],{"class":45},[35,3883,416],{"class":49},[35,3885,3886,3889,3891,3893,3896,3898,3900,3902,3904],{"class":37,"line":205},[35,3887,3888],{"class":49},"          socket.",[35,3890,726],{"class":121},[35,3892,305],{"class":49},[35,3894,3895],{"class":45},"'Echo: ",[35,3897,1212],{"class":45},[35,3899,3879],{"class":117},[35,3901,1099],{"class":45},[35,3903,2347],{"class":49},[35,3905,3906],{"class":87},"\u002F\u002F send back\n",[35,3908,3909],{"class":37,"line":216},[35,3910,259],{"class":49},[35,3912,3913,3916,3918,3921,3923,3926,3928,3931,3933,3936,3938],{"class":37,"line":230},[35,3914,3915],{"class":49},"        onError",[35,3917,211],{"class":41},[35,3919,3920],{"class":49}," (e) ",[35,3922,681],{"class":41},[35,3924,3925],{"class":121}," print",[35,3927,305],{"class":49},[35,3929,3930],{"class":45},"'Socket error: ",[35,3932,1212],{"class":45},[35,3934,3935],{"class":117},"e",[35,3937,1099],{"class":45},[35,3939,3940],{"class":49},"),\n",[35,3942,3943,3946,3948,3951,3953,3955,3957,3960],{"class":37,"line":243},[35,3944,3945],{"class":49},"        onDone",[35,3947,211],{"class":41},[35,3949,3950],{"class":49}," () ",[35,3952,681],{"class":41},[35,3954,3925],{"class":121},[35,3956,305],{"class":49},[35,3958,3959],{"class":45},"'Socket closed'",[35,3961,3940],{"class":49},[35,3963,3964,3967,3969,3971],{"class":37,"line":256},[35,3965,3966],{"class":49},"        cancelOnError",[35,3968,211],{"class":41},[35,3970,2607],{"class":117},[35,3972,227],{"class":49},[35,3974,3975],{"class":37,"line":262},[35,3976,265],{"class":49},[35,3978,3979,3982,3985],{"class":37,"line":268},[35,3980,3981],{"class":49},"    } ",[35,3983,3984],{"class":41},"else",[35,3986,160],{"class":49},[35,3988,3989,3992,3994,3996],{"class":37,"line":274},[35,3990,3991],{"class":49},"      request.response.statusCode ",[35,3993,178],{"class":41},[35,3995,1674],{"class":117},[35,3997,3998],{"class":49},".forbidden;\n",[35,4000,4001,4004,4007,4009],{"class":37,"line":280},[35,4002,4003],{"class":41},"      await",[35,4005,4006],{"class":49}," request.response.",[35,4008,2533],{"class":121},[35,4010,661],{"class":49},[35,4012,4013],{"class":37,"line":286},[35,4014,3236],{"class":49},[35,4016,4017],{"class":37,"line":291},[35,4018,1812],{"class":49},[35,4020,4021],{"class":37,"line":297},[35,4022,283],{"class":49},[35,4024,4025],{"class":37,"line":314},[35,4026,81],{"emptyLinePlaceholder":80},[35,4028,4029],{"class":37,"line":325},[35,4030,4031],{"class":87},"\u002F\u002F ── Broadcast pattern (chat room) ──\n",[35,4033,4034,4037,4040],{"class":37,"line":340},[35,4035,4036],{"class":41},"class",[35,4038,4039],{"class":117}," ChatRoom",[35,4041,160],{"class":49},[35,4043,4044,4046,4049,4051,4053,4056,4058],{"class":37,"line":359},[35,4045,559],{"class":41},[35,4047,4048],{"class":49}," _clients ",[35,4050,178],{"class":41},[35,4052,567],{"class":41},[35,4054,4055],{"class":117},"WebSocket",[35,4057,584],{"class":41},[35,4059,589],{"class":49},[35,4061,4062],{"class":37,"line":394},[35,4063,81],{"emptyLinePlaceholder":80},[35,4065,4066,4069,4072,4074,4076],{"class":37,"line":419},[35,4067,4068],{"class":41},"  void",[35,4070,4071],{"class":121}," addClient",[35,4073,305],{"class":49},[35,4075,4055],{"class":117},[35,4077,4078],{"class":49}," socket) {\n",[35,4080,4081,4084,4086],{"class":37,"line":425},[35,4082,4083],{"class":49},"    _clients.",[35,4085,726],{"class":121},[35,4087,4088],{"class":49},"(socket);\n",[35,4090,4091,4094,4096],{"class":37,"line":448},[35,4092,4093],{"class":49},"    socket.",[35,4095,3857],{"class":121},[35,4097,202],{"class":49},[35,4099,4100],{"class":37,"line":454},[35,4101,4102],{"class":49},"      (message) {\n",[35,4104,4105],{"class":37,"line":460},[35,4106,4107],{"class":87},"        \u002F\u002F Broadcast to all other clients:\n",[35,4109,4110,4113,4115,4117,4120,4122],{"class":37,"line":474},[35,4111,4112],{"class":41},"        for",[35,4114,134],{"class":49},[35,4116,2157],{"class":41},[35,4118,4119],{"class":49}," client ",[35,4121,2163],{"class":41},[35,4123,4124],{"class":49}," _clients) {\n",[35,4126,4127,4130,4133,4136,4139,4141],{"class":37,"line":499},[35,4128,4129],{"class":41},"          if",[35,4131,4132],{"class":49}," (client ",[35,4134,4135],{"class":41},"!=",[35,4137,4138],{"class":49}," socket) client.",[35,4140,726],{"class":121},[35,4142,4143],{"class":49},"(message);\n",[35,4145,4146],{"class":37,"line":505},[35,4147,4148],{"class":49},"        }\n",[35,4150,4151],{"class":37,"line":510},[35,4152,4153],{"class":49},"      },\n",[35,4155,4156,4159,4161,4163,4165,4168,4171],{"class":37,"line":515},[35,4157,4158],{"class":49},"      onDone",[35,4160,211],{"class":41},[35,4162,3950],{"class":49},[35,4164,681],{"class":41},[35,4166,4167],{"class":49}," _clients.",[35,4169,4170],{"class":121},"remove",[35,4172,4173],{"class":49},"(socket),\n",[35,4175,4176,4179,4181,4183,4185,4187,4189],{"class":37,"line":520},[35,4177,4178],{"class":49},"      onError",[35,4180,211],{"class":41},[35,4182,3920],{"class":49},[35,4184,681],{"class":41},[35,4186,4167],{"class":49},[35,4188,4170],{"class":121},[35,4190,4173],{"class":49},[35,4192,4193],{"class":37,"line":525},[35,4194,3103],{"class":49},[35,4196,4197],{"class":37,"line":531},[35,4198,1812],{"class":49},[35,4200,4201],{"class":37,"line":556},[35,4202,283],{"class":49},[14,4204,4206],{"id":4205},"native-aot-deployment","Native AOT Deployment",[23,4208,4210,4328],{"language":4209},"bash",[27,4211,4214],{"className":4212,"code":4213,"language":4209,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","# ── Compile to a standalone native binary (no Dart SDK at runtime) ──\n# Tree-shaken, AOT-compiled, fast cold start. Perfect for Docker.\ndart compile exe bin\u002Fserver.dart -o bin\u002Fserver\n\n# ── Dockerfile for a Dart server ──\n# FROM dart:stable AS build\n# WORKDIR \u002Fapp\n# COPY pubspec.* .\u002F\n# RUN dart pub get\n# COPY . .\n# RUN dart compile exe bin\u002Fserver.dart -o bin\u002Fserver\n#\n# FROM scratch  # or debian:stable-slim for glibc\n# COPY --from=build \u002Fapp\u002Fbin\u002Fserver \u002Fserver\n# EXPOSE 8080\n# CMD [\"\u002Fserver\"]\n\n# ── Environment-based configuration ──\n# PORT env var (common for Cloud Run, Fly.io, Heroku):\n# final port = int.parse(Platform.environment['PORT'] ?? '8080');\n",[18,4215,4216,4221,4226,4245,4249,4254,4259,4264,4269,4274,4279,4284,4289,4294,4299,4304,4309,4313,4318,4323],{"__ignoreMap":31},[35,4217,4218],{"class":37,"line":38},[35,4219,4220],{"class":87},"# ── Compile to a standalone native binary (no Dart SDK at runtime) ──\n",[35,4222,4223],{"class":37,"line":53},[35,4224,4225],{"class":87},"# Tree-shaken, AOT-compiled, fast cold start. Perfect for Docker.\n",[35,4227,4228,4230,4233,4236,4239,4242],{"class":37,"line":67},[35,4229,25],{"class":121},[35,4231,4232],{"class":45}," compile",[35,4234,4235],{"class":45}," exe",[35,4237,4238],{"class":45}," bin\u002Fserver.dart",[35,4240,4241],{"class":117}," -o",[35,4243,4244],{"class":45}," bin\u002Fserver\n",[35,4246,4247],{"class":37,"line":77},[35,4248,81],{"emptyLinePlaceholder":80},[35,4250,4251],{"class":37,"line":84},[35,4252,4253],{"class":87},"# ── Dockerfile for a Dart server ──\n",[35,4255,4256],{"class":37,"line":91},[35,4257,4258],{"class":87},"# FROM dart:stable AS build\n",[35,4260,4261],{"class":37,"line":97},[35,4262,4263],{"class":87},"# WORKDIR \u002Fapp\n",[35,4265,4266],{"class":37,"line":103},[35,4267,4268],{"class":87},"# COPY pubspec.* .\u002F\n",[35,4270,4271],{"class":37,"line":108},[35,4272,4273],{"class":87},"# RUN dart pub get\n",[35,4275,4276],{"class":37,"line":114},[35,4277,4278],{"class":87},"# COPY . .\n",[35,4280,4281],{"class":37,"line":128},[35,4282,4283],{"class":87},"# RUN dart compile exe bin\u002Fserver.dart -o bin\u002Fserver\n",[35,4285,4286],{"class":37,"line":143},[35,4287,4288],{"class":87},"#\n",[35,4290,4291],{"class":37,"line":163},[35,4292,4293],{"class":87},"# FROM scratch  # or debian:stable-slim for glibc\n",[35,4295,4296],{"class":37,"line":169},[35,4297,4298],{"class":87},"# COPY --from=build \u002Fapp\u002Fbin\u002Fserver \u002Fserver\n",[35,4300,4301],{"class":37,"line":190},[35,4302,4303],{"class":87},"# EXPOSE 8080\n",[35,4305,4306],{"class":37,"line":205},[35,4307,4308],{"class":87},"# CMD [\"\u002Fserver\"]\n",[35,4310,4311],{"class":37,"line":216},[35,4312,81],{"emptyLinePlaceholder":80},[35,4314,4315],{"class":37,"line":230},[35,4316,4317],{"class":87},"# ── Environment-based configuration ──\n",[35,4319,4320],{"class":37,"line":243},[35,4321,4322],{"class":87},"# PORT env var (common for Cloud Run, Fly.io, Heroku):\n",[35,4324,4325],{"class":37,"line":256},[35,4326,4327],{"class":87},"# final port = int.parse(Platform.environment['PORT'] ?? '8080');\n",[23,4329,4330],{"language":25},[27,4331,4333],{"className":29,"code":4332,"language":25,"meta":31,"style":31},"import 'dart:io';\n\nvoid main() async {\n  \u002F\u002F Production config from environment — don't hardcode ports in production.\n  final port = int.parse(Platform.environment['PORT'] ?? '8080');\n  final dbUrl = Platform.environment['DATABASE_URL'];\n  if (dbUrl == null) {\n    stderr.writeln('DATABASE_URL is required');\n    exit(1);\n  }\n\n  final server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);\n  print('Server running on port $port');\n\n  \u002F\u002F Graceful shutdown (SIGTERM\u002FSIGINT):\n  ProcessSignal.sigterm.watch().listen((_) async {\n    print('Shutting down...');\n    await server.close();\n    exit(0);\n  });\n\n  await for (final request in server) {\n    \u002F\u002F ... handle requests\n  }\n}\n",[18,4334,4335,4343,4347,4359,4364,4400,4419,4432,4446,4457,4461,4465,4488,4505,4509,4514,4536,4547,4558,4568,4572,4576,4592,4597,4601],{"__ignoreMap":31},[35,4336,4337,4339,4341],{"class":37,"line":38},[35,4338,42],{"class":41},[35,4340,2011],{"class":45},[35,4342,50],{"class":49},[35,4344,4345],{"class":37,"line":53},[35,4346,81],{"emptyLinePlaceholder":80},[35,4348,4349,4351,4353,4355,4357],{"class":37,"line":67},[35,4350,822],{"class":41},[35,4352,825],{"class":121},[35,4354,828],{"class":49},[35,4356,157],{"class":41},[35,4358,160],{"class":49},[35,4360,4361],{"class":37,"line":77},[35,4362,4363],{"class":87},"  \u002F\u002F Production config from environment — don't hardcode ports in production.\n",[35,4365,4366,4368,4371,4373,4375,4377,4380,4382,4385,4388,4391,4393,4395,4398],{"class":37,"line":84},[35,4367,559],{"class":41},[35,4369,4370],{"class":49}," port ",[35,4372,178],{"class":41},[35,4374,3210],{"class":117},[35,4376,655],{"class":49},[35,4378,4379],{"class":121},"parse",[35,4381,305],{"class":49},[35,4383,4384],{"class":117},"Platform",[35,4386,4387],{"class":49},".environment[",[35,4389,4390],{"class":45},"'PORT'",[35,4392,633],{"class":49},[35,4394,636],{"class":41},[35,4396,4397],{"class":45}," '8080'",[35,4399,416],{"class":49},[35,4401,4402,4404,4407,4409,4412,4414,4417],{"class":37,"line":91},[35,4403,559],{"class":41},[35,4405,4406],{"class":49}," dbUrl ",[35,4408,178],{"class":41},[35,4410,4411],{"class":117}," Platform",[35,4413,4387],{"class":49},[35,4415,4416],{"class":45},"'DATABASE_URL'",[35,4418,356],{"class":49},[35,4420,4421,4423,4426,4428,4430],{"class":37,"line":97},[35,4422,1773],{"class":41},[35,4424,4425],{"class":49}," (dbUrl ",[35,4427,368],{"class":41},[35,4429,371],{"class":117},[35,4431,2494],{"class":49},[35,4433,4434,4437,4439,4441,4444],{"class":37,"line":103},[35,4435,4436],{"class":49},"    stderr.",[35,4438,2502],{"class":121},[35,4440,305],{"class":49},[35,4442,4443],{"class":45},"'DATABASE_URL is required'",[35,4445,416],{"class":49},[35,4447,4448,4451,4453,4455],{"class":37,"line":108},[35,4449,4450],{"class":121},"    exit",[35,4452,305],{"class":49},[35,4454,2414],{"class":117},[35,4456,416],{"class":49},[35,4458,4459],{"class":37,"line":114},[35,4460,1812],{"class":49},[35,4462,4463],{"class":37,"line":128},[35,4464,81],{"emptyLinePlaceholder":80},[35,4466,4467,4469,4471,4473,4475,4477,4479,4481,4483,4485],{"class":37,"line":143},[35,4468,559],{"class":41},[35,4470,1030],{"class":49},[35,4472,178],{"class":41},[35,4474,181],{"class":41},[35,4476,3754],{"class":117},[35,4478,655],{"class":49},[35,4480,3759],{"class":121},[35,4482,305],{"class":49},[35,4484,1046],{"class":117},[35,4486,4487],{"class":49},".loopbackIPv4, port);\n",[35,4489,4490,4492,4494,4497,4499,4501,4503],{"class":37,"line":163},[35,4491,1060],{"class":121},[35,4493,305],{"class":49},[35,4495,4496],{"class":45},"'Server running on port ",[35,4498,1212],{"class":45},[35,4500,1094],{"class":117},[35,4502,1099],{"class":45},[35,4504,416],{"class":49},[35,4506,4507],{"class":37,"line":169},[35,4508,81],{"emptyLinePlaceholder":80},[35,4510,4511],{"class":37,"line":190},[35,4512,4513],{"class":87},"  \u002F\u002F Graceful shutdown (SIGTERM\u002FSIGINT):\n",[35,4515,4516,4519,4522,4525,4527,4529,4532,4534],{"class":37,"line":205},[35,4517,4518],{"class":117},"  ProcessSignal",[35,4520,4521],{"class":49},".sigterm.",[35,4523,4524],{"class":121},"watch",[35,4526,2178],{"class":49},[35,4528,3857],{"class":121},[35,4530,4531],{"class":49},"((_) ",[35,4533,157],{"class":41},[35,4535,160],{"class":49},[35,4537,4538,4540,4542,4545],{"class":37,"line":216},[35,4539,3506],{"class":121},[35,4541,305],{"class":49},[35,4543,4544],{"class":45},"'Shutting down...'",[35,4546,416],{"class":49},[35,4548,4549,4551,4554,4556],{"class":37,"line":230},[35,4550,3059],{"class":41},[35,4552,4553],{"class":49}," server.",[35,4555,2533],{"class":121},[35,4557,661],{"class":49},[35,4559,4560,4562,4564,4566],{"class":37,"line":243},[35,4561,4450],{"class":121},[35,4563,305],{"class":49},[35,4565,2409],{"class":117},[35,4567,416],{"class":49},[35,4569,4570],{"class":37,"line":256},[35,4571,3241],{"class":49},[35,4573,4574],{"class":37,"line":262},[35,4575,81],{"emptyLinePlaceholder":80},[35,4577,4578,4580,4582,4584,4586,4588,4590],{"class":37,"line":268},[35,4579,2149],{"class":41},[35,4581,2152],{"class":41},[35,4583,134],{"class":49},[35,4585,2157],{"class":41},[35,4587,3797],{"class":49},[35,4589,2163],{"class":41},[35,4591,3802],{"class":49},[35,4593,4594],{"class":37,"line":274},[35,4595,4596],{"class":87},"    \u002F\u002F ... handle requests\n",[35,4598,4599],{"class":37,"line":280},[35,4600,1812],{"class":49},[35,4602,4603],{"class":37,"line":286},[35,4604,283],{"class":49},[14,4606,4608],{"id":4607},"tips-tricks","💡 Tips & Tricks",[4610,4611,4612,4638,4646,4655,4667],"ul",{},[4613,4614,4615,4619,4620,4622,4623,4626,4627,4630,4631,4634,4635,4637],"li",{},[4616,4617,4618],"strong",{},"Idiom",": use ",[18,4621,20],{}," (not raw ",[18,4624,4625],{},"dart:io",") for server apps — middleware pipeline, routing (",[18,4628,4629],{},"shelf_router","), composable handlers. Cleaner than raw ",[18,4632,4633],{},"HttpServer",". ",[18,4636,1382],{}," for file-based routing (Next.js-like).",[4613,4639,4640,4619,4642,4645],{},[4616,4641,4618],{},[18,4643,4644],{},"dart compile exe"," for native server executables — AOT-compiled, fast startup, no runtime needed. Great for Docker containers (small image, fast cold start).",[4613,4647,4648,4650,4651,4654],{},[4616,4649,4618],{},": stream large files (don't load into memory) — ",[18,4652,4653],{},"await for (final chunk in File('large.txt').openRead()) { ... }"," processes chunks as they're read, avoiding loading the whole file into memory.",[4613,4656,4657,4659,4660,4663,4664,655],{},[4616,4658,4618],{},": use parameterized queries (never string interpolation) for SQL — ",[18,4661,4662],{},"conn.query('SELECT ... WHERE id = @id', substitutionValues: {'id': 1})",". Prevents SQL injection. Never ",[18,4665,4666],{},"'... WHERE id = $id'",[4613,4668,4669,4619,4671,4673,4674,4677,4678,4681],{},[4616,4670,4618],{},[18,4672,1382],{}," for file-based APIs — ",[18,4675,4676],{},"routes\u002Fusers\u002F[id].dart"," handles ",[18,4679,4680],{},"\u002Fusers\u002F:id",", like Next.js. File-based routing + middleware. Great for REST APIs and full-stack Dart.",[14,4683,4685],{"id":4684},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[4610,4687,4688,4704,4720,4741,4763,4776,4784,4802,4821,4838],{},[4613,4689,4690,3526,4695,572,4697,572,4700,4703],{},[4616,4691,4692,4694],{},[18,4693,4625],{}," is native-only",[18,4696,4633],{},[18,4698,4699],{},"File",[18,4701,4702],{},"Platform.exit"," don't work on the web. Server code runs natively (or in a container).",[4613,4705,4706,4619,4711,4713,4714,4716,4717,4719],{},[4616,4707,4708,4710],{},[18,4709,4633],{}," is low-level",[18,4712,20],{},"\u002F",[18,4715,1382],{}," for routing, middleware, JSON, CORS. Raw ",[18,4718,4633],{}," is verbose for real apps.",[4613,4721,4722,3526,4725,4728,4729,134,4732,4735,4736,4738,4739,2072],{},[4616,4723,4724],{},"Parameterized queries are required for SQL",[18,4726,4727],{},"'SELECT ... WHERE id = $id'"," is a SQL injection risk. Use ",[18,4730,4731],{},"substitutionValues",[18,4733,4734],{},"postgres",") or ",[18,4737,670],{}," placeholders (",[18,4740,3252],{},[4613,4742,4743,3526,4748,4751,4752,4754,4755,4758,4759,4762],{},[4616,4744,4745,4747],{},[18,4746,4699],{}," operations are async",[18,4749,4750],{},"readAsString()"," returns a ",[18,4753,1244],{},". Use ",[18,4756,4757],{},"await",". Sync variants (",[18,4760,4761],{},"readAsStringSync",") exist but block — avoid in servers.",[4613,4764,4765,4771,4772,4775],{},[4616,4766,4767,4770],{},[18,4768,4769],{},"Platform.environment"," is empty in tests",": env vars aren't set in ",[18,4773,4774],{},"dart test",". Mock or set them in the test setup.",[4613,4777,4778,4783],{},[4616,4779,4780,4782],{},[18,4781,4644],{}," produces a native binary",": AOT-compiled, no Dart runtime needed. Fast startup. But it's platform-specific (a macOS binary doesn't run on Linux). Build per-platform.",[4613,4785,4786,4791,4792,4794,4795,4797,4798,4801],{},[4616,4787,4788],{},[18,4789,4790],{},"WebSocketTransformer.upgrade",": for raw ",[18,4793,4633],{},". With ",[18,4796,20],{},", use the ",[18,4799,4800],{},"shelf_web_socket"," package. WebSockets need an upgrade handshake.",[4613,4803,4804,4810,4811,4813,4814,4817,4818,655],{},[4616,4805,4806,4809],{},[18,4807,4808],{},"serve()"," returns the server",": keep a reference to ",[18,4812,1071],{}," if you need to close it (tests). ",[18,4815,4816],{},"io.serve(handler, address, port)"," returns ",[18,4819,4820],{},"Future\u003CHttpServer>",[4613,4822,4823,4828,4829,4831,4832,4834,4835,655],{},[4616,4824,4825,4827],{},[18,4826,4750],{}," throws on non-UTF8",": if the file isn't valid UTF-8, ",[18,4830,1282],{}," throws. Use ",[18,4833,2130],{}," + manual decoding, or catch ",[18,4836,4837],{},"FileSystemException",[4613,4839,4840,4846,4847,4754,4850,4853,4854,4857],{},[4616,4841,4842,4845],{},[18,4843,4844],{},"Directory.list()"," is lazy",": returns a ",[18,4848,4849],{},"Stream\u003CFileSystemEntity>",[18,4851,4852],{},".toList()"," to materialize, or ",[18,4855,4856],{},"await for"," to process lazily.",[14,4859,4861],{"id":4860},"spot-the-bug","🧠 Spot the Bug",[4863,4864,4865],"p",{},"A developer builds a SQL query with string interpolation, and the app crashes on certain inputs:",[23,4867,4868],{"language":25},[27,4869,4871],{"className":29,"code":4870,"language":25,"meta":31,"style":31},"final name = request.url.queryParameters['name'] ?? '';\nfinal results = await conn.query(\"SELECT * FROM users WHERE name = '$name'\");\n",[18,4872,4873,4895],{"__ignoreMap":31},[35,4874,4875,4877,4879,4881,4884,4886,4888,4890,4893],{"class":37,"line":38},[35,4876,2157],{"class":41},[35,4878,1750],{"class":49},[35,4880,178],{"class":41},[35,4882,4883],{"class":49}," request.url.queryParameters[",[35,4885,1353],{"class":45},[35,4887,633],{"class":49},[35,4889,636],{"class":41},[35,4891,4892],{"class":45}," ''",[35,4894,50],{"class":49},[35,4896,4897,4899,4901,4903,4905,4907,4909,4911,4914,4916,4918,4920],{"class":37,"line":53},[35,4898,2157],{"class":41},[35,4900,3467],{"class":49},[35,4902,178],{"class":41},[35,4904,181],{"class":41},[35,4906,2849],{"class":49},[35,4908,3167],{"class":121},[35,4910,305],{"class":49},[35,4912,4913],{"class":45},"\"SELECT * FROM users WHERE name = '",[35,4915,1212],{"class":45},[35,4917,2864],{"class":117},[35,4919,2867],{"class":45},[35,4921,416],{"class":49},[4863,4923,4924],{},"What's the vulnerability?",[4926,4927,4928,4932,4945,4994,5001,5004,5072],"details",{},[4929,4930,4931],"summary",{},"Answer",[4863,4933,4934,4937,4938,4940,4941,4944],{},[4616,4935,4936],{},"SQL injection",". The ",[18,4939,2864],{}," parameter is interpolated directly into the SQL string. If a user passes ",[18,4942,4943],{},"name = \"'; DROP TABLE users; --\"",", the query becomes:",[27,4946,4950],{"className":4947,"code":4948,"language":4949,"meta":31,"style":31},"language-sql shiki shiki-themes github-light github-dark","SELECT * FROM users WHERE name = ''; DROP TABLE users; --'\n","sql",[18,4951,4952],{"__ignoreMap":31},[35,4953,4954,4957,4960,4963,4966,4969,4972,4975,4977,4980,4983,4986,4989,4991],{"class":37,"line":38},[35,4955,4956],{"class":41},"SELECT",[35,4958,4959],{"class":41}," *",[35,4961,4962],{"class":41}," FROM",[35,4964,4965],{"class":49}," users ",[35,4967,4968],{"class":41},"WHERE",[35,4970,4971],{"class":41}," name",[35,4973,4974],{"class":41}," =",[35,4976,4892],{"class":45},[35,4978,4979],{"class":49},"; ",[35,4981,4982],{"class":41},"DROP",[35,4984,4985],{"class":41}," TABLE",[35,4987,4988],{"class":121}," users",[35,4990,4979],{"class":49},[35,4992,4993],{"class":87},"--'\n",[4863,4995,4996,4997,5000],{},"This executes ",[18,4998,4999],{},"DROP TABLE users"," — a catastrophic SQL injection. Even without a DROP, an attacker could read arbitrary data or bypass authentication.",[4863,5002,5003],{},"The fix — use parameterized queries:",[27,5005,5007],{"className":29,"code":5006,"language":25,"meta":31,"style":31},"final name = request.url.queryParameters['name'] ?? '';\nfinal results = await conn.query(\n  'SELECT * FROM users WHERE name = @name',\n  substitutionValues: {'name': name},\n);\n",[18,5008,5009,5029,5045,5052,5068],{"__ignoreMap":31},[35,5010,5011,5013,5015,5017,5019,5021,5023,5025,5027],{"class":37,"line":38},[35,5012,2157],{"class":41},[35,5014,1750],{"class":49},[35,5016,178],{"class":41},[35,5018,4883],{"class":49},[35,5020,1353],{"class":45},[35,5022,633],{"class":49},[35,5024,636],{"class":41},[35,5026,4892],{"class":45},[35,5028,50],{"class":49},[35,5030,5031,5033,5035,5037,5039,5041,5043],{"class":37,"line":53},[35,5032,2157],{"class":41},[35,5034,3467],{"class":49},[35,5036,178],{"class":41},[35,5038,181],{"class":41},[35,5040,2849],{"class":49},[35,5042,3167],{"class":121},[35,5044,202],{"class":49},[35,5046,5047,5050],{"class":37,"line":67},[35,5048,5049],{"class":45},"  'SELECT * FROM users WHERE name = @name'",[35,5051,227],{"class":49},[35,5053,5054,5057,5059,5061,5063,5065],{"class":37,"line":77},[35,5055,5056],{"class":49},"  substitutionValues",[35,5058,211],{"class":41},[35,5060,482],{"class":49},[35,5062,1353],{"class":45},[35,5064,211],{"class":41},[35,5066,5067],{"class":49}," name},\n",[35,5069,5070],{"class":37,"line":84},[35,5071,416],{"class":49},[4863,5073,5074,5075,5078,5079,5081],{},"The ",[18,5076,5077],{},"@name"," placeholder is substituted safely by the driver (parameter separation, not string escaping). The ",[18,5080,2864],{}," value is never parsed as SQL — it's a data parameter. This is the only safe way to build SQL with user input.",[5083,5084,5085],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":31,"searchDepth":53,"depth":53,"links":5087},[5088,5090,5092,5093,5094,5096,5097,5098,5099,5100],{"id":16,"depth":53,"text":5089},"shelf — Middleware Pipeline Architecture",{"id":1379,"depth":53,"text":5091},"dart_frog — File-Based Routing",{"id":1996,"depth":53,"text":1997},{"id":2780,"depth":53,"text":2781},{"id":3248,"depth":53,"text":5095},"SQLite with sqlite3",{"id":3697,"depth":53,"text":3698},{"id":4205,"depth":53,"text":4206},{"id":4607,"depth":53,"text":4608},{"id":4684,"depth":53,"text":4685},{"id":4860,"depth":53,"text":4861},"Deep-dive into Dart server-side development with shelf middleware pipelines, dart_frog file-based routing, streaming file I\u002FO, parameterized SQL, WebSocket servers, and native AOT deployment. Code-first engineering reference.","md",{},"\u002Fdart\u002F17-server-side-dart",{"title":5,"description":5101},"dart\u002F17-server-side-dart","7IbNKK7UIaa1cfq4g5YOd4c88Fgte-1BMOcl_6ZTarI",1789924647987]