<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tobias Lundin</title>
    <description>The latest articles on DEV Community by Tobias Lundin (@tolu).</description>
    <link>https://dev.to/tolu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F591181%2Fab573db2-9d96-4cd5-8508-d41290a2d037.png</url>
      <title>DEV Community: Tobias Lundin</title>
      <link>https://dev.to/tolu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tolu"/>
    <language>en</language>
    <item>
      <title>SvelteKit RPC with Hono</title>
      <dc:creator>Tobias Lundin</dc:creator>
      <pubDate>Sun, 05 Oct 2025 20:36:22 +0000</pubDate>
      <link>https://dev.to/tolu/sveltekit-rpc-with-hono-mje</link>
      <guid>https://dev.to/tolu/sveltekit-rpc-with-hono-mje</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Original article published at &lt;a href="https://www.tolu.se/blog/sveltekit-rpc-hono/" rel="noopener noreferrer"&gt;tolu.se/blog/sveltekit-rpc-hono/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Working example codebase for article can be found here &lt;a href="https://github.com/tolu/sveltekit-hono-rpc" rel="noopener noreferrer"&gt;github.com/tolu/sveltekit-hono-rpc&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we're working with
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SvelteKit in Brief
&lt;/h3&gt;

&lt;p&gt;SvelteKit is the official full-stack framework for Svelte. It handles routing, server-side rendering, and API endpoints through a file-based convention. Think of it as Svelte's version of Next.js (React), Nuxt (Vue) or SolidStart (SolidJS).&lt;/p&gt;

&lt;h3&gt;
  
  
  Hono in Brief
&lt;/h3&gt;

&lt;p&gt;Hono is a lightweight web framework built on Web Standards, meaning it runs anywhere: Node.js, Deno, Bun, Cloudflare Workers, you name it. It's fast, provides excellent TypeScript support, and comes with middleware for everything from CORS to JWT authentication. Think Express.js, but modern and runtime-agnostic.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is RPC and Why Should You Care?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Remote Procedure Call (RPC)&lt;/strong&gt; is a pattern where you call server-side functions from your client code as if they were local functions. No manual endpoint construction, no maintaining separate client/server type definitions—just function calls that happen to execute on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bit of history&lt;/strong&gt;: RPC isn't new. We've had XML-RPC (1998), JSON-RPC, gRPC, and countless other implementations. What's changed is that modern JavaScript frameworks have rediscovered how pleasant this pattern can be when paired with TypeScript's type inference. Instead of maintaining OpenAPI specs and generating client code, your types just... work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem RPC solves:&lt;/strong&gt; In traditional REST APIs, you maintain parallel universes—server endpoints and client code that calls them—with no guaranteed synchronization. Change a route? Update the client. Modify response shape? Hope you caught all the call sites. RPC collapses this duplication: one function definition serves both client and server, with the type system ensuring they stay in sync.&lt;br&gt;
Modern frameworks like tRPC, Remix actions, Next.js server functions, and SvelteKit's remote functions all embrace this pattern. The differences lie in how much control you retain over the HTTP layer underneath.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why I'm Writing This
&lt;/h2&gt;

&lt;p&gt;I've recently done some more work with &lt;code&gt;SvelteKit&lt;/code&gt;, the first time since after runes (and more) were introduced to Svelte. Every time I return to &lt;code&gt;Svelte&lt;/code&gt; (from &lt;code&gt;React&lt;/code&gt;) I get blown away about how fun &lt;em&gt;Svelte&lt;/em&gt; components are to work with; because of the component model, scoped styling and the built in transitions and animations. The library supports my goals better, letting me easily work directly with DOM APIs instead of fighting the runtime to avoid unnecessary re-renders with &lt;code&gt;refs&lt;/code&gt; and caching.&lt;/p&gt;

&lt;p&gt;What bothers me most about modern frameworks is their reliance on folder-structure routing and magic file names for data loading and compiler instructions (hello "use server").&lt;br&gt;
While these might be great in a lot of cases I really miss the option for code-based routing.&lt;/p&gt;

&lt;p&gt;This is especially true when it comes to API-routes, and here SvelteKit is no different.&lt;/p&gt;
&lt;h2&gt;
  
  
  Type-safe client data loading
&lt;/h2&gt;

&lt;p&gt;Type-safety is important to me and has been since I fell in love with TypeScript before &lt;code&gt;1.0&lt;/code&gt;. When it comes to type-safety across &lt;em&gt;network&lt;/em&gt; and &lt;em&gt;serialization&lt;/em&gt; boundaries in full-stack frameworks it's even more important since validation is essential to trust the data being transmitted. For regular function-to-function calls in the client sphere this is not an issue. Server endpoints however can be reverse-engineered and called by anyone, not just your own client code.&lt;/p&gt;

&lt;p&gt;For loading data from the client SvelteKit offers 2 solutions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://svelte.dev/docs/kit/routing#server" rel="noopener noreferrer"&gt;API-routes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://svelte.dev/docs/kit/remote-functions" rel="noopener noreferrer"&gt;Remote Function&lt;/a&gt; (experimental)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  API-routes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File: &lt;/td&gt;
&lt;td&gt;&lt;code&gt;/src/routes/api/search/[term]/+server.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/search/:term&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;API-routes provide regular endpoints through folder hierarchy, giving you full control over request-response objects and middleware. You can use any HTTP client library—mine is &lt;a href="https://github.com/sindresorhus/ky" rel="noopener noreferrer"&gt;ky&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;+server.ts&lt;/code&gt; file registers handlers by exporting HTTP-verb-named functions like &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** File: /src/routes/api/search/[term]/+server.ts */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sveltejs/kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./$types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;term&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSearchResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/** File: page.svelte */&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// should ofc do this when some input changes...&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/search/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Downsides
&lt;/h4&gt;

&lt;p&gt;Request validation, sharing types with client and endpoint paths are your own problem to solve.&lt;/p&gt;

&lt;p&gt;While folder hierarchy helps with organization, it doesn't tell the full story. Requests involve more than just paths and dynamic variables—they include headers, query parameters, and other metadata.&lt;/p&gt;

&lt;p&gt;Folder structure sometimes scatters related concepts across multiple files that should be grouped together.&lt;/p&gt;
&lt;h3&gt;
  
  
  Remote Functions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File: &lt;/td&gt;
&lt;td&gt;&lt;code&gt;/src/routes/api/search/data.remote.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;auto-generated&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Remote functions &lt;em&gt;always&lt;/em&gt; run on the server but can be called from client code, using build time glue-generation and magic file-names (&lt;code&gt;&amp;lt;name&amp;gt;.remote.ts&lt;/code&gt;). The &lt;code&gt;awaited&lt;/code&gt; response is not a &lt;code&gt;Response&lt;/code&gt; object but rather the data itself.&lt;/p&gt;

&lt;p&gt;Declaring input parameters for remote functions &lt;em&gt;require&lt;/em&gt; &lt;a href="https://standardschema.dev/" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt; for validation purposes, which encourages developers to validate their data.&lt;/p&gt;

&lt;p&gt;The functions can then be imported by client code and called as regular functions. Easy peasy.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** file: src/routes/search/data.remote.ts */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$app/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchSearchResults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/api/search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valibot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSearchResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchSearchResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/** file: src/routes/search/+page.svelte */&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getSearchResults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./data.remote&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// should ofc be called when some input value change in real life&lt;/span&gt;
    &lt;span class="nf"&gt;getSearchResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Downsides
&lt;/h4&gt;

&lt;p&gt;No control over the response headers, so impossible (or hard) to set &lt;code&gt;Cache-Control&lt;/code&gt; or add &lt;code&gt;Server-Timing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All dynamic behavior in the function must be encoded as validated input parameters instead of request-properties.&lt;/p&gt;
&lt;h3&gt;
  
  
  Take away
&lt;/h3&gt;

&lt;p&gt;Neither approach gives me the full control I want over routing and request-response handling while maintaining type-safe communication. They are very nice tools to have but I'd like more control, be closer to the HTTP and maintain control over types and validation, all in one package.&lt;/p&gt;

&lt;p&gt;Also, can I have an OpenAPI spec and Swagger UI on the side? I'm not sure how I would solve that with these primitives.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enter 🔥 Hono RPC (&lt;a href="https://hono.dev/docs/guides/rpc" rel="noopener noreferrer"&gt;docs&lt;/a&gt;)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;First of all Hono is a fantastic web application framework built on web standards and supports any runtime. It has many built-in middlewares for caching, authentication, CORS, JWT, logging etc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's how we'll combine the best of both approaches and add more functionality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;replace &lt;em&gt;API-routes&lt;/em&gt; entirely with &lt;strong&gt;Hono&lt;/strong&gt; for full control of routing and gives us free reigns over code structure and folder hierarchy&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;@hono/openapi&lt;/code&gt; and &lt;code&gt;valibot&lt;/code&gt; for endpoint schema- and response validation and type safety&lt;/li&gt;
&lt;li&gt;replace &lt;em&gt;remote functions&lt;/em&gt; on the client by leveraging the Hono Client for endpoint discovery&lt;/li&gt;
&lt;li&gt;generate a Swagger UI from the OpenAPI specification&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1. Replacing API-routes with Hono
&lt;/h3&gt;

&lt;p&gt;We can pass all traffic on &lt;code&gt;/api/*&lt;/code&gt; to Hono by leveraging rest-parameters in folder names and creating an API-route file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** File: /src/routes/api/[...rest]/+server.ts */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;honoApiApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/api/hono-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./$types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This handler will respond to all HTTP verbs (GET, POST, PUT, PATCH, DELETE, etc.)&lt;/span&gt;
&lt;span class="c1"&gt;// Pass all requests along to the honoApiApp, that we'll create next&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;honoApiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to configure and mount our Hono app on the correct path, and handle the search result query.&lt;/p&gt;

&lt;p&gt;For fun we'll add a global middleware for logging and setting the &lt;code&gt;x-served-by&lt;/code&gt; response header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** File: /src/lib/api/hono-api.ts */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// Here we moved the search term from a query parameter to a path parameter&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/search/:term&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;term&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;internalApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSearchResults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=3600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;honoApiApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// Add global middleware for logging&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(🔥) - [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-served-by&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// Mount the app on the /api-route&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;honoApiApp&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Adding validation and type-safety
&lt;/h3&gt;

&lt;p&gt;While you can add validators to Hono routes without OpenAPI, I chose this approach for several benefits: free Swagger UI, visual API representation, and advanced client response types that handle all defined HTTP status codes.&lt;/p&gt;

&lt;p&gt;So let's extend our endpoint from before with validation and OpenAPI specification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ import { describeRoute, resolver, validator } from 'hono-openapi';
+ import * as v from 'valibot';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const app = new Hono()
&lt;/span&gt;  .get(
    '/search/:term',
&lt;span class="gi"&gt;+    describeRoute({
+      description: 'Provides search results for a given term',
+      responses: {
+        200: {
+          description: 'Search results',
+          content: { 'application/json': {
+            schema: resolver(v.any()) // add actual schema for response here
+          }}, 
+        },
+      },
+    }),
+    validator(
+      'param',
+      v.object({
+        term: v.string(),
+      }),
+    ),
&lt;/span&gt;    (c) =&amp;gt; {
      const term = c.req.param('term');
      const results = await internalApi.getSearchResults();
      return c.json(results, { headers: { 'Cache-Control': 'public, max-age=3600' } });
   }
  );
&lt;span class="err"&gt;
&lt;/span&gt;/** honoApiApp is unchanged... */
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ export type HonoApiType = typeof honoApiApp;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us endpoint validation and as a benefit a type we can use to create an api client for the browser. We'll get back to the OpenAPI when adding Swagger UI later.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Replace &lt;em&gt;remote function&lt;/em&gt; with &lt;em&gt;Hono client&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;First we need to create a new file for our API-client, instantiate it using our new &lt;code&gt;HonoApiType&lt;/code&gt; and export it for use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** File: /src/lib/rpc-client.ts */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HonoApiType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/api/hono-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$app/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Since this file might be imported by server code, on the SSR pass of a page,&lt;/span&gt;
&lt;span class="c1"&gt;// let's ensure we're in the browser before accessing "location"&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HonoApiType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This typed Hono client gives us an object with full IntelliSense and type support for the endpoints paths and response types.&lt;/p&gt;

&lt;p&gt;We can now replace our remote function in our svelte component with the &lt;code&gt;apiClient&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;/** file: src/routes/search/+page.svelte */
&amp;lt;script lang="ts"&amp;gt;
&lt;span class="gd"&gt;-  import { getSearchResults } from './data.remote';
&lt;/span&gt;&lt;span class="gi"&gt;+  import { apiClient } from '$lib/rpc-client';
&lt;/span&gt;  let results = $state();
  let term = $state('foo');
  onMount(() =&amp;gt; {
    // should ofc be called when some input value change in real life
&lt;span class="gd"&gt;-    apiClient(term).then(data =&amp;gt; {
-      results = data;
-    })
&lt;/span&gt;&lt;span class="gi"&gt;+    apiClient.api.search[':term']
+     .$get({ param: { term: term } }).then(res =&amp;gt; res.json())
+     .then(data =&amp;gt; {
+       results = data;
+     });
&lt;/span&gt;  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not all I want in an API-client, but at least we now have intellisense and type safety all the way down. 🙌&lt;/p&gt;

&lt;p&gt;And we have full control over API routes, middleware and response headers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I would love to extend this client with niceties from &lt;code&gt;ky&lt;/code&gt; so that it has automatic retries and syntax-sugar like &lt;code&gt;.json()&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
But that's for another time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Swagger UI from the OpenAPI specification
&lt;/h3&gt;

&lt;p&gt;Now that our endpoint has OpenAPI configuration, we can expose the schema and add Swagger UI for interactive API documentation.&lt;/p&gt;

&lt;p&gt;We'll expose the schema and swagger directly on the &lt;code&gt;honoApiApp&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** File: /src/lib/api/hono-api.ts */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;openAPIRouteHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono-openapi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;swaggerUI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hono/swagger-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// this is right below the code we've already written&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openApiPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/openapi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;honoApiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;openApiPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;openAPIRouteHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;honoApiApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;documentation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Very Own API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API documentation for the My Own API, by way of Hono 🔥&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;honoApiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;swaggerUI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;openApiPath&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  That's it
&lt;/h3&gt;

&lt;p&gt;By adding validation and minimal documentation, we gained response schema types, a type-safe &lt;code&gt;apiClient&lt;/code&gt;, and visual API documentation — all working together seamlessly. In many ways we gained an embedded BFF (Backend-For-Frontend) that required minimal boilerplate code and that can leverege the full power and flexibility of Hono.&lt;/p&gt;

&lt;p&gt;The best part? In using Hono for this API (and RPC client) we are completely independent of SvelteKit. You're (more) free to rewrite the frontend with another library without porting the entire API to another framework's conventions and standards 🙌&lt;/p&gt;

&lt;p&gt;Svelte ❤️🔥 Hono&lt;/p&gt;

&lt;p&gt;✌️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rpc</category>
      <category>sveltekit</category>
      <category>openapi</category>
    </item>
    <item>
      <title>ViteJs - replacing create-react-app in a monorepo</title>
      <dc:creator>Tobias Lundin</dc:creator>
      <pubDate>Mon, 08 Mar 2021 09:20:26 +0000</pubDate>
      <link>https://dev.to/tolu/vitejs-replacing-create-react-app-in-a-monorepo-31nd</link>
      <guid>https://dev.to/tolu/vitejs-replacing-create-react-app-in-a-monorepo-31nd</guid>
      <description>&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@marcsm?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Marc Sendra Martorell&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/speed?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/tolu/test-vite-monorepo" rel="noopener noreferrer"&gt;https://github.com/tolu/test-vite-monorepo&lt;/a&gt; (example repo)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;https://vitejs.dev/&lt;/a&gt; (documentation)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/workspaces-run" rel="noopener noreferrer"&gt;workspaces-run&lt;/a&gt; 
to run same task in all packages from root (might not need after &lt;a href="https://github.com/npm/cli/releases/tag/v7.7.0" rel="noopener noreferrer"&gt;npm 7.7.0&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Premise
&lt;/h2&gt;

&lt;p&gt;The aim is to reduce complexity (nr of deps etc) and increase inner-loop-speed in a monorepo at work using &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;create-react-app&lt;/a&gt; (&lt;em&gt;cra&lt;/em&gt;), &lt;a href="https://github.com/lerna/lerna" rel="noopener noreferrer"&gt;lerna&lt;/a&gt; and &lt;a href="https://github.com/gsoft-inc/craco" rel="noopener noreferrer"&gt;craco&lt;/a&gt; by leveraging &lt;a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces" rel="noopener noreferrer"&gt;npm 7 workspaces&lt;/a&gt; and &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;vite&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;On weekdays I build a streaming-service webapp&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Our original setup
&lt;/h2&gt;

&lt;p&gt;We started out with something like this, a lerna project with 2 &lt;em&gt;cra&lt;/em&gt;-apps (&lt;code&gt;App1&lt;/code&gt; &amp;amp; &lt;code&gt;App2&lt;/code&gt;), a &lt;code&gt;common&lt;/code&gt;-package for shared components/styles with Storybook setup and some general purpose tooling packages.&lt;br&gt;
The (not ejected) &lt;em&gt;cra&lt;/em&gt;-apps use &lt;code&gt;craco&lt;/code&gt; for editing the webpack config with extended contexts (to be able to require packages from outside of root dir) and setting up require-aliases (for sass imports) etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/
├──App1/
│  App2/
│  common/
│  tooling/
├───eslint-cfg
│   prettier-cfg
package.json
readme.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup works well enough but we've noticed some pain points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it's a hassle to update &lt;code&gt;react-scripts&lt;/code&gt; and we don't really want to eject since then we have to manage 400 lines of webpack config by ourselves 😅&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;cra&lt;/em&gt; requires configuration to work with monorepo&lt;/li&gt;
&lt;li&gt;we don't really publish anything so &lt;code&gt;lerna&lt;/code&gt; seems a bit overkill&lt;/li&gt;
&lt;li&gt;a cold start (&lt;code&gt;git clean -fdx &amp;amp;&amp;amp; npm i &amp;amp;&amp;amp; npm start&lt;/code&gt;) clocks in at around 3+min (&lt;code&gt;npm start&lt;/code&gt; is ~1min)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can do better! And hopefullly &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;ViteJs&lt;/a&gt; is the answer!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next gen frontend tooling 🙌
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cleaning up 🧹
&lt;/h3&gt;

&lt;p&gt;First things first, let's get rid of everything we shouldn't need.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;craco&lt;/code&gt; scripts, plugins and inside npm scripts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;craco&lt;/code&gt; and &lt;code&gt;cra&lt;/code&gt; dependencies&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lerna&lt;/code&gt; deps and configs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node-sass&lt;/code&gt;, it's deprecated and we've had issues with &lt;code&gt;node-gyp&lt;/code&gt;, we'll replace this with the official &lt;code&gt;sass&lt;/code&gt;-package instead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let's make it new 🔮&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Time to see what we can do with new tooling!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup &lt;code&gt;npm@7&lt;/code&gt; workspaces
&lt;/h3&gt;

&lt;p&gt;Configure workspaces in root &lt;code&gt;package.json&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"worskpaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apps/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apps/tooling/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick &lt;code&gt;npm i&lt;/code&gt; in the root and we're done. That &lt;em&gt;was&lt;/em&gt; easy!&lt;/p&gt;

&lt;h3&gt;
  
  
  Add &lt;code&gt;vite&lt;/code&gt; and configure for react
&lt;/h3&gt;

&lt;p&gt;Add dependencies&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;vite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@vitejs/plugin-react-refresh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vite-plugin-svgr&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;vite-plugin-svgr&lt;/code&gt; is for importing &lt;code&gt;.svg&lt;/code&gt; files as components so that &lt;code&gt;import { ReactComponent as SvgIcon } from 'some.svg'&lt;/code&gt; keeps working&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to &lt;code&gt;App1&lt;/code&gt; &amp;amp; &lt;code&gt;App2&lt;/code&gt; and create a basic configuration file &lt;code&gt;vite.config.ts&lt;/code&gt; in each app-folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reactRefresh&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react-refresh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;svgr&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-svgr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;reactRefresh&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;svgr&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Fix svg component import's
&lt;/h4&gt;

&lt;p&gt;Since we're importing svg's as components we now get a type error (for &lt;code&gt;import { ReactComponent as SvgLogo } from '...'&lt;/code&gt;) that can be fixed by adding this file to the root each app that imports svg's (i.e. where &lt;code&gt;vite-plugin-svgr&lt;/code&gt; is used)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.d.ts&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ReactComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SVGProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SVGSVGElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add &lt;code&gt;sass&lt;/code&gt;-package
&lt;/h3&gt;

&lt;p&gt;Basically all we needed was to &lt;code&gt;npm i -D sass&lt;/code&gt; in our app's, but for 2 issues in our &lt;code&gt;*.scss&lt;/code&gt;-files since the &lt;code&gt;sass&lt;/code&gt;-package is stricter on some things:&lt;/p&gt;

&lt;p&gt;Remove multiline &lt;code&gt;@warn&lt;/code&gt; statements&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- @warn 'bla,
-        di bla';
&lt;/span&gt;&lt;span class="gi"&gt;+ @warn 'bla, di bla
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Escape return value of some functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;@function pagePercentageMargins($percentage) {
&lt;span class="gd"&gt;-   @return (0.5vw * #{$percentage});
&lt;/span&gt;&lt;span class="gi"&gt;+   @return (#{(0.5 * $percentage)}vw);
&lt;/span&gt;}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other issues to solve
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Using and resolving aliases from common-folder&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To be able to split configuration between our 2 apps we used aliases (standard webpack resolve aliases) set in each app-config that we could use when resolving &lt;code&gt;@imports&lt;/code&gt; from &lt;code&gt;scss&lt;/code&gt;-files in the &lt;code&gt;common&lt;/code&gt;-folder (different theme colors etc).&lt;/p&gt;

&lt;p&gt;Aliases in the webpack-config (via a craco-plugin) are defined like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMON_COLORS: 'path/to/colors.scss'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;, and &lt;code&gt;@imported&lt;/code&gt; using &lt;code&gt;sass-loader&lt;/code&gt; by prepending a tilde sign:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'~COMMON_COLORS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;vite&lt;/code&gt; and &lt;code&gt;sass&lt;/code&gt;, the tilde isn't needed and alises can easily be added to the config. Notice the hack for &lt;code&gt;__dirname&lt;/code&gt; here since we went for a &lt;code&gt;module&lt;/code&gt;-ts-file as config instead of a plain &lt;code&gt;commonJs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// vite.config.ts
&lt;span class="p"&gt;import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import svgr from 'vite-plugin-svgr'
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+import { dirname, resolve } from 'path';
+import { fileURLToPath } from 'url';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+const __dirname = dirname(fileURLToPath(import.meta.url));
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export default defineConfig({
&lt;/span&gt;  plugins: [reactRefresh(), svgr()],
&lt;span class="gi"&gt;+  resolve: {
+    alias: {
+      'COMMON_COLORS': resolve(__dirname, 'src/styles/colors.scss'),
+    }
+  },
&lt;/span&gt;})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To avoid ts-errors when using &lt;code&gt;import.meta&lt;/code&gt; the &lt;code&gt;"module"&lt;/code&gt;-property in &lt;code&gt;tsconfig.json&lt;/code&gt; must be set to &lt;code&gt;esnext&lt;/code&gt;, &lt;code&gt;es2020&lt;/code&gt; or &lt;code&gt;system&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Provide &lt;code&gt;.env&lt;/code&gt; parameters&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In our &lt;code&gt;cra/craco&lt;/code&gt;-setup some variables were provided via &lt;code&gt;.env&lt;/code&gt; files and some set directly in the npm-script (making for long scripts 👀):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cross-env CI=true REACT_APP_VERSION=$npm_package_version craco start"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default in a &lt;code&gt;cra&lt;/code&gt;-setup is that all env-variables that begin with &lt;code&gt;REACT_APP&lt;/code&gt; get's injected via webpack's &lt;code&gt;define&lt;/code&gt;-plugin so you can use them in your scripts like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REACT_APP_VERSION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;vite&lt;/code&gt; the default is that you use &lt;code&gt;import.meta.env&lt;/code&gt; to get at variables. Only variables that begin with &lt;code&gt;VITE_&lt;/code&gt; are exposed and variables are automatically loaded via &lt;code&gt;dot-env&lt;/code&gt; from &lt;code&gt;.env&lt;/code&gt;-files.&lt;/p&gt;

&lt;p&gt;Personally I dont really like long npm-scripts so I'd rather move the &lt;code&gt;version&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; we're using into the configuration.&lt;/p&gt;

&lt;p&gt;In order to get that working, let's add a &lt;code&gt;.env&lt;/code&gt;-file first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_CI=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we'll update our config to provide a global &lt;code&gt;pkgJson&lt;/code&gt; variable that we can use "as-is" instead of via &lt;code&gt;import.meta.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// vite.config.ts
&lt;span class="p"&gt;import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'
import svgr from 'vite-plugin-svgr'
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
&lt;/span&gt;&lt;span class="gi"&gt;+import { name, version } from './package.json';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const __dirname = dirname(fileURLToPath(import.meta.url));
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export default defineConfig({
&lt;/span&gt;  plugins: [reactRefresh(), svgr()],
  resolve: {
    alias: {
      'SASS_VARIABLES': resolve(__dirname, 'src/styles/common-variables.scss'),
    }
  },
&lt;span class="gi"&gt;+  define: {
+    pkgJson: { name, version }
+  }
&lt;/span&gt;})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those were (almost) all the steps needed for us to convert from &lt;code&gt;cra&lt;/code&gt; to &lt;code&gt;vite&lt;/code&gt;, greatly improve install / startup speed and reduce complexity in a world that already has too much of just that 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;🍰🎉🚀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vite v2.0.5 dev server running at:

&amp;gt; Local:    http://localhost:3000/
&amp;gt; Network:  http://172.25.231.128:3000/

ready in 729ms.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;~1 minute&lt;/strong&gt; startup time went down to &lt;strong&gt;sub-second&lt;/strong&gt; 😍🙌&lt;/p&gt;

</description>
      <category>vite</category>
      <category>npm</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
