[{"data":1,"prerenderedAt":1819},["ShallowReactive",2],{"navigation_docs":3,"-guide-adoption":93,"-guide-adoption-surround":1814},[4,71],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":70},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,18,22,26,30,34,38,42,46,50,54,58,62,66],{"title":11,"path":12,"stem":13},"Getting started","\u002Fguide\u002Fgetting-started","1.guide\u002F01.getting-started",{"title":15,"path":16,"stem":17},"Adoption paths","\u002Fguide\u002Fadoption","1.guide\u002F02.adoption",{"title":19,"path":20,"stem":21},"Routes and typed $fetch","\u002Fguide\u002Froutes","1.guide\u002F02.routes",{"title":23,"path":24,"stem":25},"Kyo handlers","\u002Fguide\u002Fkyo","1.guide\u002F03.kyo",{"title":27,"path":28,"stem":29},"ZIO handlers","\u002Fguide\u002Fzio","1.guide\u002F04.zio",{"title":31,"path":32,"stem":33},"Configuration and secrets","\u002Fguide\u002Fconfiguration","1.guide\u002F05.configuration",{"title":35,"path":36,"stem":37},"Application and request lifetimes","\u002Fguide\u002Flifetimes","1.guide\u002F06.lifetimes",{"title":39,"path":40,"stem":41},"Schemas","\u002Fguide\u002Fschema","1.guide\u002F07.schema",{"title":43,"path":44,"stem":45},"PostgreSQL","\u002Fguide\u002Fdatabase","1.guide\u002F08.database",{"title":47,"path":48,"stem":49},"Streaming and cancellation","\u002Fguide\u002Fstreaming","1.guide\u002F09.streaming",{"title":51,"path":52,"stem":53},"Testing","\u002Fguide\u002Ftesting","1.guide\u002F10.testing",{"title":55,"path":56,"stem":57},"Nitro storage and NuxtHub KV","\u002Fguide\u002Fstorage","1.guide\u002F11.storage",{"title":59,"path":60,"stem":61},"Debug Scala handlers","\u002Fguide\u002Fdebugging","1.guide\u002F12.debugging",{"title":63,"path":64,"stem":65},"npm build tooling","\u002Fguide\u002Fnpm-tooling","1.guide\u002F13.npm-tooling",{"title":67,"path":68,"stem":69},"Set up h34s with a coding assistant","\u002Fguide\u002Fagent-setup","1.guide\u002F14.agent-setup",false,{"title":72,"icon":73,"path":74,"stem":75,"children":76,"page":70},"Reference","i-lucide-library","\u002Freference","2.reference",[77,81,85,89],{"title":78,"path":79,"stem":80},"Architecture","\u002Freference\u002Farchitecture","2.reference\u002F1.architecture",{"title":82,"path":83,"stem":84},"Releases","\u002Freference\u002Freleases","2.reference\u002F2.releases",{"title":86,"path":87,"stem":88},"Compatibility","\u002Freference\u002Fcompatibility","2.reference\u002F3.compatibility",{"title":90,"path":91,"stem":92},"Migration guide","\u002Freference\u002Fmigration","2.reference\u002F4.migration",{"id":94,"title":15,"body":95,"description":1808,"extension":1809,"links":1810,"meta":1811,"navigation":681,"path":16,"seo":1812,"stem":17,"__hash__":1813},"docs\u002F1.guide\u002F02.adoption.md",{"type":96,"value":97,"toc":1797},"minimark",[98,102,105,128,136,147,152,168,184,187,204,207,280,294,298,359,366,370,389,392,434,451,455,458,517,540,544,558,581,1054,1057,1063,1066,1083,1113,1120,1128,1135,1353,1361,1374,1377,1652,1680,1694,1698,1755,1759,1762,1793],[99,100,101],"p",{},"h34s is a toolbox. You add a Scala.js server module to a Nuxt application, write handlers in Kyo (or ZIO), and mount them as Nuxt routes. Your models, services, configuration and resources stay in your application.",[99,103,104],{},"Pick the path that matches what you have:",[106,107,108,116,122],"ol",{},[109,110,111],"li",{},[112,113,115],"a",{"href":114},"#_1-start-a-new-project","Start a new project",[109,117,118],{},[112,119,121],{"href":120},"#_2-add-scala-handlers-to-an-existing-nuxt-app","Add Scala handlers to an existing Nuxt app",[109,123,124],{},[112,125,127],{"href":126},"#_3-add-nuxt-to-an-existing-scalajs-project","Add Nuxt to an existing Scala.js project",[99,129,130,131,135],{},"Every path ends with the same steps: ",[112,132,134],{"href":133},"#write-mount-and-run-a-handler","write a handler, mount it, run it",".",[99,137,138,139,141,142,146],{},"Prefer a coding assistant? Give it the prompt in ",[112,140,67],{"href":68},"; it follows the steps on this page. For path 3, the prompt uses the ",[143,144,145],"code",{},"0.2.0-alpha.1"," layout, with the Nuxt application at the root of your sbt build.",[148,149,151],"h2",{"id":150},"get-the-libraries","Get the libraries",[99,153,154,160,161,164,165,135],{},[155,156,157,158,135],"strong",{},"The API in these guides is ",[143,159,145],{}," The older ",[143,162,163],{},"0.1.0-alpha.1"," has a different API; see the ",[112,166,167],{"href":91},"migration guide",[99,169,170,171,173,174,176,177,183],{},"Install ",[143,172,145],{}," from GitHub Packages with a GitHub token, as ",[112,175,82],{"href":83}," describes. Its ",[112,178,182],{"href":179,"rel":180},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Fh34s\u002Factions\u002Fruns\u002F39145732257",[181],"nofollow","release workflow"," published the packages and verified them through the registry.",[99,185,186],{},"To build against h34s source changes that are not released, use one of these:",[188,189,190,201],"ul",{},[109,191,192,193,196,197,200],{},"Publish an h34s checkout to your local Ivy repository: run ",[143,194,195],{},"sbt releasePublishLocal"," in it (JDK 25), then depend on the version in its ",[143,198,199],{},"version.sbt",". sbt resolves the local Ivy repository by default, before the registry, so remove that local copy before you depend on a registry release with the same version. This path has not been exercised by hand; the repository's own consumer check publishes to a temporary Maven directory instead.",[109,202,203],{},"Reference the h34s source projects from your sbt build.",[99,205,206],{},"Pick the modules for your runtime. Kyo applications never need ZIO; ZIO applications get Kyo through the ZIO adapter:",[208,209,210,226],"table",{},[211,212,213],"thead",{},[214,215,216,220,223],"tr",{},[217,218,219],"th",{},"You want",[217,221,222],{},"Kyo",[217,224,225],{},"ZIO",[227,228,229,245,265],"tbody",{},[214,230,231,235,240],{},[232,233,234],"td",{},"Handlers",[232,236,237],{},[143,238,239],{},"kyo-h3-ext",[232,241,242],{},[143,243,244],{},"zio-h3-ext",[214,246,247,250,255],{},[232,248,249],{},"Schemas (optional)",[232,251,252],{},[143,253,254],{},"native-converter-kyo-schema-ext",[232,256,257,260,261,264],{},[143,258,259],{},"native-converter-zio-schema-ext"," + ",[143,262,263],{},"zio-schema-derivation"," 1.7.5",[214,266,267,270,275],{},[232,268,269],{},"Nitro storage (optional)",[232,271,272],{},[143,273,274],{},"kyo-nuxthub-ext",[232,276,277],{},[143,278,279],{},"zio-nuxthub-ext",[99,281,282,283,286,287,290,291,135],{},"On the npm side, every application needs ",[143,284,285],{},"@yuri4n\u002Fh34s-nuxt"," (a dev dependency) and ",[143,288,289],{},"h3@1.15.4"," (a runtime dependency). See ",[112,292,293],{"href":64},"npm tooling",[148,295,297],{"id":296},"toolchain","Toolchain",[208,299,300,313],{},[211,301,302],{},[214,303,304,307,310],{},[217,305,306],{},"Tool",[217,308,309],{},"Version",[217,311,312],{},"Why",[227,314,315,326,337,348],{},[214,316,317,320,323],{},[232,318,319],{},"JDK",[232,321,322],{},"25",[232,324,325],{},"Runs sbt; Kyo 1.0.0-RC7 macros need it. Not needed in production",[214,327,328,331,334],{},[232,329,330],{},"sbt",[232,332,333],{},"1.11.7",[232,335,336],{},"Builds and links Scala.js",[214,338,339,342,345],{},[232,340,341],{},"Scala \u002F Scala.js",[232,343,344],{},"3.9.0 \u002F 1.22.0",[232,346,347],{},"Every h34s module uses this pair",[214,349,350,353,356],{},[232,351,352],{},"Node \u002F pnpm",[232,354,355],{},"24 \u002F 10.34.4",[232,357,358],{},"Runs Nuxt and your handlers",[99,360,361,362,365],{},"See ",[112,363,364],{"href":87},"compatibility"," for what was tested.",[148,367,369],{"id":368},"_1-start-a-new-project","1. Start a new project",[99,371,372,373,376,377,379,380,382,383,385,386,388],{},"The project tour arranges the demo's greeting files in layout A: the setup files from this guide, and the demo's greeting handler, models and ",[143,374,375],{},"AppHandler"," as the Scala code. It is not a generated or compiled project. The Scala files also use the schema bridge (",[143,378,254],{},", or ",[143,381,259],{}," with ",[143,384,263],{},"), and the ZIO ",[143,387,375],{}," also wires the demo's Notes service. Pick a file to read it, and switch between Kyo and ZIO:",[390,391],"project-tour",{},[106,393,394,397,424,429],{},[109,395,396],{},"Create a Nuxt 4 application with Nuxt's own tooling. h34s has no project generator.",[109,398,399,400,403,404,407,408,411,412,415,416,419,420,135],{},"Add an sbt build at the Nuxt root: ",[143,401,402],{},"build.sbt",", ",[143,405,406],{},"project\u002F"," and ",[143,409,410],{},".jvmopts"," next to ",[143,413,414],{},"nuxt.config.ts",", and your Scala sources in ",[143,417,418],{},"scala-server\u002F",". See ",[112,421,423],{"href":422},"#configure-the-scalajs-module","Configure the Scala.js module",[109,425,426,427,135],{},"Add the npm package and the Nuxt module, as in ",[112,428,293],{"href":64},[109,430,431,135],{},[112,432,433],{"href":133},"Write, mount and run a handler",[99,435,436,437,440,441,444,445,450],{},"To see a working setup first, follow the ",[112,438,439],{"href":12},"getting-started tutorial",", or run the demo in ",[143,442,443],{},"examples\u002Fnuxt\u002F"," with the ",[112,446,449],{"href":447,"rel":448},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Fh34s\u002Fblob\u002Fmain\u002Fdocs\u002Fdevelopment.md",[181],"development guide",". Its Kyo greeting needs no database. The demo is a reference, not a template: you do not need to clone this repository to use h34s.",[148,452,454],{"id":453},"_2-add-scala-handlers-to-an-existing-nuxt-app","2. Add Scala handlers to an existing Nuxt app",[99,456,457],{},"Keep your TypeScript handlers. Scala handlers live next to them and declare their own routes.",[106,459,460,484,496,499],{},[109,461,462,463,465,466,468,469,472,473,479,480,483],{},"Add an sbt build at the Nuxt root, with your Scala sources in ",[143,464,418],{},", as in ",[112,467,423],{"href":422},". Point its linker output at a generated directory such as ",[143,470,471],{},"server\u002F.scala",". If you keep a separate sbt build next to the app instead, set ",[112,474,476],{"href":475},"\u002Fguide\u002Fnpm-tooling#point-the-cli-at-your-sbt-build",[143,477,478],{},"sbtRoot"," in ",[143,481,482],{},"h34s.config.mjs",". ",[109,485,486,487,407,489,491,492,419,494,135],{},"Add ",[143,488,285],{},[143,490,289],{},", and register the module in ",[143,493,414],{},[112,495,293],{"href":64},[109,497,498],{},"Move only the routes you want to Scala. A TypeScript route and a Scala route can share a path prefix. Give each Scala route its own path and method: two handlers for the same route and method conflict.",[109,500,501,502,505,506,509,510,513,514,135],{},"Read your existing ",[143,503,504],{},"runtimeConfig"," keys from Scala with ",[143,507,508],{},"RuntimeConfig"," (Kyo) or ",[143,511,512],{},"ZRuntimeConfig"," (ZIO). See ",[112,515,516],{"href":32},"configuration",[99,518,519,520,523,524,527,528,531,532,535,536,539],{},"The h34s wire format wraps values in ",[143,521,522],{},"{\"data\": ...}",". If existing clients expect the raw value, pass ",[143,525,526],{},"HandlerOptions(success = SuccessEnvelope.Raw)"," for those routes. ",[143,529,530],{},"Raw"," sends a string as ",[143,533,534],{},"text\u002Fplain; charset=utf-8"," and any other value as JSON (",[143,537,538],{},"application\u002Fjson","), unless you set a content type.",[148,541,543],{"id":542},"_3-add-nuxt-to-an-existing-scalajs-project","3. Add Nuxt to an existing Scala.js project",[99,545,546,547,550,551,553,554,557],{},"Keep your sbt build where it is, and put the Nuxt application in ",[143,548,549],{},"web\u002F"," below it. Your ",[143,552,402],{}," does not change: the h34s settings go in a new ",[143,555,556],{},"h34s.sbt"," next to it, and sbt loads both files.",[99,559,560,563,564,566,567,563,569,572,573,575,576,578,579,135],{},[155,561,562],{},"Status."," ",[143,565,478],{}," is new in ",[143,568,285],{},[143,570,571],{},"0.3.0-alpha.1",", which is not released yet. With ",[143,574,145],{},", create the Nuxt application at the root of your sbt build instead, as in path 1, and keep the h34s settings in ",[143,577,556],{}," with the linker output ",[143,580,471],{},[106,582,583,589,605,949,957,1040],{},[109,584,585,586,588],{},"Create a Nuxt 4 application in ",[143,587,549],{}," with Nuxt's own tooling.",[109,590,591,592,594,595,598,599,601,602,604],{},"Check ",[143,593,406],{},". If no file there adds ",[143,596,597],{},"sbt-scalajs"," 1.22.0 or later, add or update the line from ",[112,600,423],{"href":422},". If ",[143,603,410],{}," is missing, add it from the same section.",[109,606,607,608,411,610,612,613,616,617,931,934,935,938,939,942,943,946,947,135],{},"Create ",[143,609,556],{},[143,611,402],{},", with a dedicated server module that links into ",[143,614,615],{},"web\u002Fserver\u002F.scala",":",[618,619,624],"pre",{"className":620,"code":621,"language":622,"meta":623,"style":623},"language-scala shiki shiki-themes github-light github-light github-dark","\u002F\u002F h34s.sbt\nimport org.scalajs.linker.interface.{ModuleKind, OutputPatterns}\n\nlazy val server = project.in(file(\"scala-server\"))\n  .enablePlugins(ScalaJSPlugin)\n  .settings(\n    scalaVersion := \"3.9.0\",\n    libraryDependencies += \"dev.yuri4n\" %%% \"kyo-h3-ext\" % \"0.2.0-alpha.1\",\n    scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule)\n      .withOutputPatterns(OutputPatterns.fromJSFile(\"%s.js\"))),\n    Compile \u002F fastLinkJS \u002F scalaJSLinkerConfig ~= (_.withSourceMap(true)),\n    Compile \u002F fastLinkJS \u002F scalaJSLinkerOutputDirectory :=\n      (LocalRootProject \u002F baseDirectory).value \u002F \"web\" \u002F \"server\" \u002F \".scala\",\n    Compile \u002F fullLinkJS \u002F scalaJSLinkerOutputDirectory :=\n      (LocalRootProject \u002F baseDirectory).value \u002F \"web\" \u002F \"server\" \u002F \".scala\",\n  )\n","scala","",[143,625,626,635,676,683,709,721,727,742,768,789,806,836,853,884,900,925],{"__ignoreMap":623},[627,628,631],"span",{"class":629,"line":630},"line",1,[627,632,634],{"class":633},"sCsY4","\u002F\u002F h34s.sbt\n",[627,636,638,642,646,649,652,654,657,659,662,665,668,670,673],{"class":629,"line":637},2,[627,639,641],{"class":640},"s8jYJ","import",[627,643,645],{"class":644},"snPdu"," org",[627,647,135],{"class":648},"sxrX7",[627,650,651],{"class":644},"scalajs",[627,653,135],{"class":648},[627,655,656],{"class":644},"linker",[627,658,135],{"class":648},[627,660,661],{"class":644},"interface",[627,663,664],{"class":648},".{",[627,666,667],{"class":644},"ModuleKind",[627,669,403],{"class":648},[627,671,672],{"class":644},"OutputPatterns",[627,674,675],{"class":648},"}\n",[627,677,679],{"class":629,"line":678},3,[627,680,682],{"emptyLinePlaceholder":681},true,"\n",[627,684,686,689,692,696,699,702,706],{"class":629,"line":685},4,[627,687,688],{"class":640},"lazy",[627,690,691],{"class":640}," val",[627,693,695],{"class":694},"sP4rz"," server",[627,697,698],{"class":640}," =",[627,700,701],{"class":648}," project.in(file(",[627,703,705],{"class":704},"sIIMD","\"scala-server\"",[627,707,708],{"class":648},"))\n",[627,710,712,715,718],{"class":629,"line":711},5,[627,713,714],{"class":648},"  .enablePlugins(",[627,716,717],{"class":644},"ScalaJSPlugin",[627,719,720],{"class":648},")\n",[627,722,724],{"class":629,"line":723},6,[627,725,726],{"class":648},"  .settings(\n",[627,728,730,733,736,739],{"class":629,"line":729},7,[627,731,732],{"class":648},"    scalaVersion ",[627,734,735],{"class":640},":=",[627,737,738],{"class":704}," \"3.9.0\"",[627,740,741],{"class":648},",\n",[627,743,745,748,751,754,757,760,763,766],{"class":629,"line":744},8,[627,746,747],{"class":648},"    libraryDependencies ",[627,749,750],{"class":640},"+=",[627,752,753],{"class":704}," \"dev.yuri4n\"",[627,755,756],{"class":640}," %%%",[627,758,759],{"class":704}," \"kyo-h3-ext\"",[627,761,762],{"class":640}," %",[627,764,765],{"class":704}," \"0.2.0-alpha.1\"",[627,767,741],{"class":648},[627,769,771,774,777,780,782,784,787],{"class":629,"line":770},9,[627,772,773],{"class":648},"    scalaJSLinkerConfig ",[627,775,776],{"class":640},"~=",[627,778,779],{"class":648}," (_.withModuleKind(",[627,781,667],{"class":644},[627,783,135],{"class":648},[627,785,786],{"class":644},"ESModule",[627,788,720],{"class":648},[627,790,792,795,797,800,803],{"class":629,"line":791},10,[627,793,794],{"class":648},"      .withOutputPatterns(",[627,796,672],{"class":644},[627,798,799],{"class":648},".fromJSFile(",[627,801,802],{"class":704},"\"%s.js\"",[627,804,805],{"class":648},"))),\n",[627,807,809,812,815,818,821,824,826,829,833],{"class":629,"line":808},11,[627,810,811],{"class":644},"    Compile",[627,813,814],{"class":640}," \u002F",[627,816,817],{"class":648}," fastLinkJS ",[627,819,820],{"class":640},"\u002F",[627,822,823],{"class":648}," scalaJSLinkerConfig ",[627,825,776],{"class":640},[627,827,828],{"class":648}," (_.withSourceMap(",[627,830,832],{"class":831},"sBjJW","true",[627,834,835],{"class":648},")),\n",[627,837,839,841,843,845,847,850],{"class":629,"line":838},12,[627,840,811],{"class":644},[627,842,814],{"class":640},[627,844,817],{"class":648},[627,846,820],{"class":640},[627,848,849],{"class":648}," scalaJSLinkerOutputDirectory ",[627,851,852],{"class":640},":=\n",[627,854,856,859,862,864,867,869,872,874,877,879,882],{"class":629,"line":855},13,[627,857,858],{"class":648},"      (",[627,860,861],{"class":644},"LocalRootProject",[627,863,814],{"class":640},[627,865,866],{"class":648}," baseDirectory).value ",[627,868,820],{"class":640},[627,870,871],{"class":704}," \"web\"",[627,873,814],{"class":640},[627,875,876],{"class":704}," \"server\"",[627,878,814],{"class":640},[627,880,881],{"class":704}," \".scala\"",[627,883,741],{"class":648},[627,885,887,889,891,894,896,898],{"class":629,"line":886},14,[627,888,811],{"class":644},[627,890,814],{"class":640},[627,892,893],{"class":648}," fullLinkJS ",[627,895,820],{"class":640},[627,897,849],{"class":648},[627,899,852],{"class":640},[627,901,903,905,907,909,911,913,915,917,919,921,923],{"class":629,"line":902},15,[627,904,858],{"class":648},[627,906,861],{"class":644},[627,908,814],{"class":640},[627,910,866],{"class":648},[627,912,820],{"class":640},[627,914,871],{"class":704},[627,916,814],{"class":640},[627,918,876],{"class":704},[627,920,814],{"class":640},[627,922,881],{"class":704},[627,924,741],{"class":648},[627,926,928],{"class":629,"line":927},16,[627,929,930],{"class":648},"  )\n",[932,933],"br",{},"Keep your models and services in their own modules. To use one from the server module, add ",[143,936,937],{},".dependsOn(LocalProject(\"core\"))",", with the ID of your project instead of ",[143,940,941],{},"core",". If your build already has a project named ",[143,944,945],{},"server",", pick another name and use it in ",[143,948,482],{},[109,950,951,952,954,955,135],{},"In ",[143,953,549],{},", add the npm package, the Nuxt module and the scripts, as in ",[112,956,293],{"href":64},[109,958,607,959,382,962,965,966,969,970],{},[143,960,961],{},"web\u002Fh34s.config.mjs",[143,963,964],{},"sbtRoot: '..'",", so that ",[143,967,968],{},"h34s-scala"," runs sbt in your build:",[618,971,975],{"className":972,"code":973,"language":974,"meta":623,"style":623},"language-js shiki shiki-themes github-light github-light github-dark","import { defineConfig } from '@yuri4n\u002Fh34s-nuxt\u002Fbuild'\n\nexport default defineConfig({\n  sbtRoot: '..',\n  modules: [{ project: 'server', output: 'server\u002F.scala' }],\n})\n","js",[143,976,977,990,994,1008,1018,1035],{"__ignoreMap":623},[627,978,979,981,984,987],{"class":629,"line":630},[627,980,641],{"class":640},[627,982,983],{"class":648}," { defineConfig } ",[627,985,986],{"class":640},"from",[627,988,989],{"class":704}," '@yuri4n\u002Fh34s-nuxt\u002Fbuild'\n",[627,991,992],{"class":629,"line":637},[627,993,682],{"emptyLinePlaceholder":681},[627,995,996,999,1002,1005],{"class":629,"line":678},[627,997,998],{"class":640},"export",[627,1000,1001],{"class":640}," default",[627,1003,1004],{"class":644}," defineConfig",[627,1006,1007],{"class":648},"({\n",[627,1009,1010,1013,1016],{"class":629,"line":685},[627,1011,1012],{"class":648},"  sbtRoot: ",[627,1014,1015],{"class":704},"'..'",[627,1017,741],{"class":648},[627,1019,1020,1023,1026,1029,1032],{"class":629,"line":711},[627,1021,1022],{"class":648},"  modules: [{ project: ",[627,1024,1025],{"class":704},"'server'",[627,1027,1028],{"class":648},", output: ",[627,1030,1031],{"class":704},"'server\u002F.scala'",[627,1033,1034],{"class":648}," }],\n",[627,1036,1037],{"class":629,"line":723},[627,1038,1039],{"class":648},"})\n",[109,1041,1042,1044,1045,1048,1049,1051,1052,135],{},[112,1043,433],{"href":133},". Put the handler in ",[143,1046,1047],{},"scala-server\u002Fsrc\u002Fmain\u002Fscala\u002F",", skip the step that writes ",[143,1050,482],{},", and run the scripts in ",[143,1053,549],{},[99,1055,1056],{},"Only Scala.js-compatible code can enter Nitro. Your shared models must cross-build for Scala.js. Use Scala 3.9.0 for the modules that h34s handlers depend on.",[99,1058,1059,1060,135],{},"If your backend is JVM-only, you have two choices: cross-build the models and services that can run on Scala.js, or keep the JVM server and call it over HTTP from Nuxt. JDBC libraries, including Doobie, do not run in Scala.js. For PostgreSQL inside Nitro, use kyo-sql or Skunk; see ",[112,1061,1062],{"href":44},"databases",[148,1064,423],{"id":1065},"configure-the-scalajs-module",[618,1067,1071],{"className":1068,"code":1069,"language":1070,"meta":623,"style":623},"language-properties shiki shiki-themes github-light github-light github-dark","# project\u002Fbuild.properties\nsbt.version=1.11.7\n","properties",[143,1072,1073,1078],{"__ignoreMap":623},[627,1074,1075],{"class":629,"line":630},[627,1076,1077],{},"# project\u002Fbuild.properties\n",[627,1079,1080],{"class":629,"line":637},[627,1081,1082],{},"sbt.version=1.11.7\n",[618,1084,1086],{"className":620,"code":1085,"language":622,"meta":623,"style":623},"\u002F\u002F project\u002Fplugins.sbt\naddSbtPlugin(\"org.scala-js\" % \"sbt-scalajs\" % \"1.22.0\")\n",[143,1087,1088,1093],{"__ignoreMap":623},[627,1089,1090],{"class":629,"line":630},[627,1091,1092],{"class":633},"\u002F\u002F project\u002Fplugins.sbt\n",[627,1094,1095,1098,1101,1103,1106,1108,1111],{"class":629,"line":637},[627,1096,1097],{"class":648},"addSbtPlugin(",[627,1099,1100],{"class":704},"\"org.scala-js\"",[627,1102,762],{"class":640},[627,1104,1105],{"class":704}," \"sbt-scalajs\"",[627,1107,762],{"class":640},[627,1109,1110],{"class":704}," \"1.22.0\"",[627,1112,720],{"class":648},[99,1114,607,1115,411,1117,1119],{},[143,1116,410],{},[143,1118,402],{},", with the JVM options for sbt:",[618,1121,1126],{"className":1122,"code":1124,"language":1125,"meta":623},[1123],"language-text","-Xss4m\n-Xmx4g\n","text",[143,1127,1124],{"__ignoreMap":623},[99,1129,1130,1131,1134],{},"The repository's own builds use these options, and its fresh-consumer check (",[143,1132,1133],{},"scripts\u002Fcheck-consumers.mjs",") copies the same file into each consumer project. A build without them has not been tested.",[618,1136,1138],{"className":620,"code":1137,"language":622,"meta":623,"style":623},"\u002F\u002F build.sbt\nimport org.scalajs.linker.interface.{ModuleKind, OutputPatterns}\n\nlazy val server = project.in(file(\"scala-server\"))\n  .enablePlugins(ScalaJSPlugin)\n  .settings(\n    scalaVersion := \"3.9.0\",\n    libraryDependencies += \"dev.yuri4n\" %%% \"kyo-h3-ext\" % \"0.2.0-alpha.1\",\n    scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule)\n      .withOutputPatterns(OutputPatterns.fromJSFile(\"%s.js\"))),\n    Compile \u002F fastLinkJS \u002F scalaJSLinkerConfig ~= (_.withSourceMap(true)),\n    Compile \u002F fastLinkJS \u002F scalaJSLinkerOutputDirectory :=\n      (LocalRootProject \u002F baseDirectory).value \u002F \"server\" \u002F \".scala\",\n    Compile \u002F fullLinkJS \u002F scalaJSLinkerOutputDirectory :=\n      (LocalRootProject \u002F baseDirectory).value \u002F \"server\" \u002F \".scala\",\n  )\n",[143,1139,1140,1145,1173,1177,1193,1201,1205,1215,1233,1249,1261,1281,1295,1315,1329,1349],{"__ignoreMap":623},[627,1141,1142],{"class":629,"line":630},[627,1143,1144],{"class":633},"\u002F\u002F build.sbt\n",[627,1146,1147,1149,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171],{"class":629,"line":637},[627,1148,641],{"class":640},[627,1150,645],{"class":644},[627,1152,135],{"class":648},[627,1154,651],{"class":644},[627,1156,135],{"class":648},[627,1158,656],{"class":644},[627,1160,135],{"class":648},[627,1162,661],{"class":644},[627,1164,664],{"class":648},[627,1166,667],{"class":644},[627,1168,403],{"class":648},[627,1170,672],{"class":644},[627,1172,675],{"class":648},[627,1174,1175],{"class":629,"line":678},[627,1176,682],{"emptyLinePlaceholder":681},[627,1178,1179,1181,1183,1185,1187,1189,1191],{"class":629,"line":685},[627,1180,688],{"class":640},[627,1182,691],{"class":640},[627,1184,695],{"class":694},[627,1186,698],{"class":640},[627,1188,701],{"class":648},[627,1190,705],{"class":704},[627,1192,708],{"class":648},[627,1194,1195,1197,1199],{"class":629,"line":711},[627,1196,714],{"class":648},[627,1198,717],{"class":644},[627,1200,720],{"class":648},[627,1202,1203],{"class":629,"line":723},[627,1204,726],{"class":648},[627,1206,1207,1209,1211,1213],{"class":629,"line":729},[627,1208,732],{"class":648},[627,1210,735],{"class":640},[627,1212,738],{"class":704},[627,1214,741],{"class":648},[627,1216,1217,1219,1221,1223,1225,1227,1229,1231],{"class":629,"line":744},[627,1218,747],{"class":648},[627,1220,750],{"class":640},[627,1222,753],{"class":704},[627,1224,756],{"class":640},[627,1226,759],{"class":704},[627,1228,762],{"class":640},[627,1230,765],{"class":704},[627,1232,741],{"class":648},[627,1234,1235,1237,1239,1241,1243,1245,1247],{"class":629,"line":770},[627,1236,773],{"class":648},[627,1238,776],{"class":640},[627,1240,779],{"class":648},[627,1242,667],{"class":644},[627,1244,135],{"class":648},[627,1246,786],{"class":644},[627,1248,720],{"class":648},[627,1250,1251,1253,1255,1257,1259],{"class":629,"line":791},[627,1252,794],{"class":648},[627,1254,672],{"class":644},[627,1256,799],{"class":648},[627,1258,802],{"class":704},[627,1260,805],{"class":648},[627,1262,1263,1265,1267,1269,1271,1273,1275,1277,1279],{"class":629,"line":808},[627,1264,811],{"class":644},[627,1266,814],{"class":640},[627,1268,817],{"class":648},[627,1270,820],{"class":640},[627,1272,823],{"class":648},[627,1274,776],{"class":640},[627,1276,828],{"class":648},[627,1278,832],{"class":831},[627,1280,835],{"class":648},[627,1282,1283,1285,1287,1289,1291,1293],{"class":629,"line":838},[627,1284,811],{"class":644},[627,1286,814],{"class":640},[627,1288,817],{"class":648},[627,1290,820],{"class":640},[627,1292,849],{"class":648},[627,1294,852],{"class":640},[627,1296,1297,1299,1301,1303,1305,1307,1309,1311,1313],{"class":629,"line":855},[627,1298,858],{"class":648},[627,1300,861],{"class":644},[627,1302,814],{"class":640},[627,1304,866],{"class":648},[627,1306,820],{"class":640},[627,1308,876],{"class":704},[627,1310,814],{"class":640},[627,1312,881],{"class":704},[627,1314,741],{"class":648},[627,1316,1317,1319,1321,1323,1325,1327],{"class":629,"line":886},[627,1318,811],{"class":644},[627,1320,814],{"class":640},[627,1322,893],{"class":648},[627,1324,820],{"class":640},[627,1326,849],{"class":648},[627,1328,852],{"class":640},[627,1330,1331,1333,1335,1337,1339,1341,1343,1345,1347],{"class":629,"line":902},[627,1332,858],{"class":648},[627,1334,861],{"class":644},[627,1336,814],{"class":640},[627,1338,866],{"class":648},[627,1340,820],{"class":640},[627,1342,876],{"class":704},[627,1344,814],{"class":640},[627,1346,881],{"class":704},[627,1348,741],{"class":648},[627,1350,1351],{"class":629,"line":927},[627,1352,930],{"class":648},[99,1354,1355,1356,382,1358,1360],{},"For ZIO, replace ",[143,1357,239],{},[143,1359,244],{},". Run sbt with JDK 25.",[99,1362,1363,1366,1367,1370,1371,1373],{},[143,1364,1365],{},"OutputPatterns.fromJSFile(\"%s.js\")"," makes each exported module a ",[143,1368,1369],{},"\u003Cname>.js"," file, which Nitro mounts. Keep only generated files in ",[143,1372,471],{},": a production build clears it.",[148,1375,433],{"id":1376},"write-mount-and-run-a-handler",[106,1378,1379,1579,1630,1642],{},[109,1380,1381,1382,1385,1386,616,1389],{},"Write a handler, export it as the ",[143,1383,1384],{},"default"," export of its own module, and give it a route with ",[143,1387,1388],{},".at",[618,1390,1392],{"className":620,"code":1391,"language":622,"meta":623,"style":623},"import dev.yuri4n.exts.kyoh3.*\nimport org.getshaka.nativeconverter.NativeConverter\n\nimport scala.scalajs.js.annotation.JSExportTopLevel\n\nfinal case class Greeting(message: String) derives NativeConverter\n\n@JSExportTopLevel(\"default\", \"hello-get\")\nval hello: EventHandler = KHandler.define(_ => Greeting(\"Hello from Scala\")).at(Route.get(\"\u002Fapi\u002Fhello\"))\n",[143,1393,1394,1421,1442,1446,1471,1475,1510,1514,1534],{"__ignoreMap":623},[627,1395,1396,1398,1401,1403,1406,1408,1411,1413,1416,1418],{"class":629,"line":630},[627,1397,641],{"class":640},[627,1399,1400],{"class":644}," dev",[627,1402,135],{"class":648},[627,1404,1405],{"class":644},"yuri4n",[627,1407,135],{"class":648},[627,1409,1410],{"class":644},"exts",[627,1412,135],{"class":648},[627,1414,1415],{"class":644},"kyoh3",[627,1417,135],{"class":648},[627,1419,1420],{"class":644},"*\n",[627,1422,1423,1425,1427,1429,1432,1434,1437,1439],{"class":629,"line":637},[627,1424,641],{"class":640},[627,1426,645],{"class":644},[627,1428,135],{"class":648},[627,1430,1431],{"class":644},"getshaka",[627,1433,135],{"class":648},[627,1435,1436],{"class":644},"nativeconverter",[627,1438,135],{"class":648},[627,1440,1441],{"class":644},"NativeConverter\n",[627,1443,1444],{"class":629,"line":678},[627,1445,682],{"emptyLinePlaceholder":681},[627,1447,1448,1450,1453,1455,1457,1459,1461,1463,1466,1468],{"class":629,"line":685},[627,1449,641],{"class":640},[627,1451,1452],{"class":644}," scala",[627,1454,135],{"class":648},[627,1456,651],{"class":644},[627,1458,135],{"class":648},[627,1460,974],{"class":644},[627,1462,135],{"class":648},[627,1464,1465],{"class":644},"annotation",[627,1467,135],{"class":648},[627,1469,1470],{"class":644},"JSExportTopLevel\n",[627,1472,1473],{"class":629,"line":711},[627,1474,682],{"emptyLinePlaceholder":681},[627,1476,1477,1480,1483,1486,1489,1492,1495,1498,1501,1504,1507],{"class":629,"line":723},[627,1478,1479],{"class":640},"final",[627,1481,1482],{"class":640}," case",[627,1484,1485],{"class":640}," class",[627,1487,1488],{"class":644}," Greeting",[627,1490,1491],{"class":648},"(",[627,1493,1494],{"class":694},"message",[627,1496,1497],{"class":648},": ",[627,1499,1500],{"class":644},"String",[627,1502,1503],{"class":648},") ",[627,1505,1506],{"class":640},"derives",[627,1508,1509],{"class":644}," NativeConverter\n",[627,1511,1512],{"class":629,"line":729},[627,1513,682],{"emptyLinePlaceholder":681},[627,1515,1516,1519,1522,1524,1527,1529,1532],{"class":629,"line":744},[627,1517,1518],{"class":640},"@",[627,1520,1521],{"class":644},"JSExportTopLevel",[627,1523,1491],{"class":648},[627,1525,1526],{"class":704},"\"default\"",[627,1528,403],{"class":648},[627,1530,1531],{"class":704},"\"hello-get\"",[627,1533,720],{"class":648},[627,1535,1536,1539,1542,1544,1547,1549,1552,1555,1558,1560,1562,1565,1568,1571,1574,1577],{"class":629,"line":770},[627,1537,1538],{"class":640},"val",[627,1540,1541],{"class":694}," hello",[627,1543,616],{"class":640},[627,1545,1546],{"class":644}," EventHandler",[627,1548,698],{"class":640},[627,1550,1551],{"class":644}," KHandler",[627,1553,1554],{"class":648},".define(_ ",[627,1556,1557],{"class":640},"=>",[627,1559,1488],{"class":644},[627,1561,1491],{"class":648},[627,1563,1564],{"class":704},"\"Hello from Scala\"",[627,1566,1567],{"class":648},")).at(",[627,1569,1570],{"class":644},"Route",[627,1572,1573],{"class":648},".get(",[627,1575,1576],{"class":704},"\"\u002Fapi\u002Fhello\"",[627,1578,708],{"class":648},[109,1580,1581,1582,1584,1585],{},"Point ",[143,1583,482],{}," at the linked output:",[618,1586,1588],{"className":972,"code":1587,"language":974,"meta":623,"style":623},"import { defineConfig } from '@yuri4n\u002Fh34s-nuxt\u002Fbuild'\n\nexport default defineConfig({\n  modules: [{ project: 'server', output: 'server\u002F.scala' }],\n})\n",[143,1589,1590,1600,1604,1614,1626],{"__ignoreMap":623},[627,1591,1592,1594,1596,1598],{"class":629,"line":630},[627,1593,641],{"class":640},[627,1595,983],{"class":648},[627,1597,986],{"class":640},[627,1599,989],{"class":704},[627,1601,1602],{"class":629,"line":637},[627,1603,682],{"emptyLinePlaceholder":681},[627,1605,1606,1608,1610,1612],{"class":629,"line":678},[627,1607,998],{"class":640},[627,1609,1001],{"class":640},[627,1611,1004],{"class":644},[627,1613,1007],{"class":648},[627,1615,1616,1618,1620,1622,1624],{"class":629,"line":685},[627,1617,1022],{"class":648},[627,1619,1025],{"class":704},[627,1621,1028],{"class":648},[627,1623,1031],{"class":704},[627,1625,1034],{"class":648},[627,1627,1628],{"class":629,"line":711},[627,1629,1039],{"class":648},[109,1631,1632,1633,1497,1635,1638,1639,483],{},"Link and start Nuxt with the scripts from ",[112,1634,293],{"href":64},[143,1636,1637],{},"pnpm dev:scala",". The link step prints ",[143,1640,1641],{},"[h34s] 1 route and 0 models in server\u002F.scala",[109,1643,1644,1645,1648,1649,135],{},"Request ",[143,1646,1647],{},"http:\u002F\u002F127.0.0.1:3000\u002Fapi\u002Fhello",". Expected body: ",[143,1650,1651],{},"{\"data\":{\"message\":\"Hello from Scala\"}}",[99,1653,1654,1655,1657,1658,1661,1662,1665,1666,1669,1670,1673,1674,135],{},"You write no route file: the Nuxt module mounts the route from the manifest that ",[143,1656,968],{}," writes after linking, and ",[143,1659,1660],{},"$fetch('\u002Fapi\u002Fhello')"," is typed from the generated declaration. ",[143,1663,1664],{},"derives NativeConverter"," types the body as ",[143,1667,1668],{},"unknown","; derive ",[143,1671,1672],{},"NativeSchema"," from a schema to get its fields. See ",[112,1675,1676,1677],{"href":20},"routes and typed ",[143,1678,1679],{},"$fetch",[99,1681,1682,1683,1686,1687,1690,1691,1693],{},"If the request returns 404, check that the link step printed the route; if Nuxt warns that ",[143,1684,1685],{},"server\u002F.scala\u002Fh34s-routes.json"," is missing, link first and restart Nuxt. If Nitro cannot resolve ",[143,1688,1689],{},"h3",", add ",[143,1692,289],{}," to your app's dependencies.",[148,1695,1697],{"id":1696},"grow-from-there","Grow from there",[188,1699,1700,1712,1721,1734,1740],{},[109,1701,1702,1703,1497,1705,1708,1709,135],{},"Wire your services in one ",[143,1704,375],{},[112,1706,222],{"href":1707},"\u002Fguide\u002Fkyo#compose-services-in-your-apphandler"," or ",[112,1710,225],{"href":1711},"\u002Fguide\u002Fzio#compose-services-in-your-apphandler",[109,1713,1714,1715,1717,1718,135],{},"Read ",[112,1716,516],{"href":32}," and keep secrets in ",[143,1719,1720],{},"Secret",[109,1722,1723,1724,1727,1728,1497,1731,135],{},"Hold pools in ",[143,1725,1726],{},"Shared"," \u002F ",[143,1729,1730],{},"ZShared",[112,1732,1733],{"href":36},"lifetimes",[109,1735,1736,1737,135],{},"Validate input with ",[112,1738,1739],{"href":40},"schemas",[109,1741,486,1742,403,1745,403,1748,1751,1752,135],{},[112,1743,1744],{"href":48},"streaming",[112,1746,1747],{"href":56},"storage",[112,1749,1750],{"href":52},"tests"," and a ",[112,1753,1754],{"href":44},"database",[148,1756,1758],{"id":1757},"what-is-verified","What is verified",[99,1760,1761],{},"The repository's consumer check builds four fresh sbt projects (Kyo, ZIO, Kyo Schema, ZIO Schema) against a temporary Maven repository and serves real h3 requests. A complete Nuxt application created from scratch with these steps has not been verified yet, on any path.",[99,1763,1764,1765,1767,1768,1770,1771,479,1774,1776,1777,1779,1780,1782,1783,1785,1786,1789,1790,1792],{},"For path 3, the unit tests of ",[143,1766,285],{}," run the CLI with ",[143,1769,478],{}," and a fake sbt. Once, in a cloud session on 2026-10-20, ",[143,1772,1773],{},"h34s-scala dev",[143,1775,549],{}," linked a real sbt build of this shape: an unchanged ",[143,1778,402],{},", a new ",[143,1781,556],{}," whose server module depends on a project of ",[143,1784,402],{}," through ",[143,1787,1788],{},"LocalProject",", and the output in ",[143,1791,615],{},". That run used no h34s library and no Nuxt.",[1794,1795,1796],"style",{},"html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP4rz, html code.shiki .sP4rz{--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":623,"searchDepth":637,"depth":637,"links":1798},[1799,1800,1801,1802,1803,1804,1805,1806,1807],{"id":150,"depth":637,"text":151},{"id":296,"depth":637,"text":297},{"id":368,"depth":637,"text":369},{"id":453,"depth":637,"text":454},{"id":542,"depth":637,"text":543},{"id":1065,"depth":637,"text":423},{"id":1376,"depth":637,"text":433},{"id":1696,"depth":637,"text":1697},{"id":1757,"depth":637,"text":1758},"Add h34s to a new Nuxt app, an existing Nuxt app, or an existing Scala.js project.","md",null,{},{"title":15,"description":1808},"UX4ZAcWDZhsOht8VXHqDVYMdgpM3JPqfmfD1Qpzzihk",[1815,1817],{"title":11,"path":12,"stem":13,"description":1816,"children":-1},"Run the demo, make your first request to a Kyo handler, and see validation and errors on the wire.",{"title":19,"path":20,"stem":21,"description":1818,"children":-1},"Mount Scala handlers from Scala with .at(Route...), and let Nuxt type $fetch from the generated declarations.",1792533381916]