<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ hono - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ hono - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 16:31:21 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/hono/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Get Type Safety Without Code Generation Using tRPC and Hono ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever updated your backend API property name but neglected to also update the frontend? I'm sure you have. When this occurs, it leads to production crashes and unhappy customers, plus you've wasted your entire week fixing the problem. To reso... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/type-safety-without-code-generation-using-trpc-and-hono/</link>
                <guid isPermaLink="false">696535106ca3442fa7874d4b</guid>
                
                    <category>
                        <![CDATA[ trpc ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hono ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tarun Singh ]]>
                </dc:creator>
                <pubDate>Mon, 12 Jan 2026 17:53:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768240380001/79d5aa1f-438e-4a1e-a072-3166b9a36333.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever updated your backend API property name but neglected to also update the frontend? I'm sure you have. When this occurs, it leads to production crashes and unhappy customers, plus you've wasted your entire week fixing the problem.</p>
<p>To resolve this issue in the past, you typically had to create a multitude of TypeScript interfaces by hand, using GraphQL Code Generator to generate the interface files, or hope that it all worked out. Well, there’s a better way to accomplish this now, without the need for code generation.</p>
<p><a target="_blank" href="https://trpc.io/">tRPC</a> and <a target="_blank" href="https://hono.dev/">Hono</a> are two applications that are changing how we develop TypeScript-based applications throughout the entirety of the full-stack.</p>
<p>By the end of this tutorial, you’ll understand:</p>
<ul>
<li><p>Why traditional REST APIs fail at type safety</p>
</li>
<li><p>How tRPC provides full end-to-end type inference between backend and frontend</p>
</li>
<li><p>How Hono delivers type-safe APIs while staying REST-friendly</p>
</li>
<li><p>When to choose tRPC vs Hono for your projects</p>
</li>
<li><p>How these tools improve developer experience, team velocity, and reliability</p>
</li>
</ul>
<p>If you’re building full-stack TypeScript applications and want fewer runtime bugs and faster iteration, this guide is for you.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-problem-with-traditional-apis">The Problem with Traditional APIs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-makes-trpc-different">What Makes tRPC Different?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hono-the-lightweight-challenger">Hono: The Lightweight Challenger</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-this-matters-these-days">Why This Matters These Days</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-future-is-type-safe">The Future is Type-Safe</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along comfortably, you should have:</p>
<ul>
<li><p>Basic knowledge of TypeScript</p>
</li>
<li><p>Familiarity with REST APIs and how frontend-backend communication works</p>
</li>
<li><p>Some experience with Node.js and modern JavaScript frameworks</p>
</li>
<li><p>A general understanding of frontend frameworks like React or Next.js (helpful, but not required)</p>
</li>
</ul>
<p>You don’t need prior experience with tRPC, Hono, or GraphQL.</p>
<h2 id="heading-the-problem-with-traditional-apis">The Problem with Traditional APIs</h2>
<p>You’ve probably written something like this a hundred times:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Backend (Express)</span>
app.post(<span class="hljs-string">'/api/users'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { name, email } = req.body;
  <span class="hljs-comment">// Do stuff with user</span>
  res.json({ id: <span class="hljs-number">1</span>, name, email });
});

<span class="hljs-comment">// Frontend</span>
<span class="hljs-keyword">const</span> createUser = <span class="hljs-keyword">async</span> (name: <span class="hljs-built_in">string</span>, email: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/users'</span>, {
    method: <span class="hljs-string">'POST'</span>,
    headers: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
    body: <span class="hljs-built_in">JSON</span>.stringify({ name, email })
  });
  <span class="hljs-keyword">return</span> response.json(); 
};
</code></pre>
<p>The backend knows the shape of the data. But the frontend...it hopes it gets it right. You end up writing interfaces manually, like:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>If you change the backend tomorrow to return <code>userId</code> instead of <code>id</code>, TypeScript won't catch it. Your types and reality have diverged, and you won't know until runtime.</p>
<p><a target="_blank" href="https://graphql.org/">GraphQL</a> tried to solve this with schemas and codegen, but honestly? Setting up GraphQL feels like assembling IKEA furniture without instructions. You need a schema, resolvers, code generation tools, and suddenly your "simple" API has a 30-minute setup process.</p>
<h2 id="heading-what-makes-trpc-different">What Makes tRPC Different?</h2>
<p>tRPC flips the script entirely. Instead of defining your API in a separate schema language, your TypeScript code is the schema. Here's the same API in tRPC:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Backend (tRPC router)</span>
<span class="hljs-keyword">import</span> { initTRPC } <span class="hljs-keyword">from</span> <span class="hljs-string">'@trpc/server'</span>;
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>;

<span class="hljs-keyword">const</span> t = initTRPC.create();

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> appRouter = t.router({
  createUser: t.procedure
    .input(z.object({
      name: z.string(),
      email: z.string().email(),
    }))
    .mutation(<span class="hljs-function">(<span class="hljs-params">{ input }</span>) =&gt;</span> {
      <span class="hljs-comment">// Do stuff with user</span>
      <span class="hljs-keyword">return</span> { id: <span class="hljs-number">1</span>, name: input.name, email: input.email };
    }),
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AppRouter = <span class="hljs-keyword">typeof</span> appRouter;
</code></pre>
<p>This is where it gets cool. On your frontend:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Frontend - fully type-safe!</span>
<span class="hljs-keyword">import</span> { createTRPCClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@trpc/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AppRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'./server'</span>;

<span class="hljs-keyword">const</span> client = createTRPCClient&lt;AppRouter&gt;({
  url: <span class="hljs-string">'http://localhost:3000/trpc'</span>,
});

<span class="hljs-comment">// TypeScript knows EVERYTHING about this call</span>
<span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> client.createUser.mutate({
  name: <span class="hljs-string">'Alice'</span>,
  email: <span class="hljs-string">'alice@example.com'</span>
});

<span class="hljs-comment">// user is automatically typed as { id: number; name: string; email: string; }</span>
</code></pre>
<p>No code generation or build step, or GraphQL schema. Just pure TypeScript inference doing its thing. If you rename <code>id</code> to <code>userId</code> in your backend, your frontend will immediately show a TypeScript error. You'll catch it before you even save the file.</p>
<p>This is what we call end-to-end type safety, and it's honestly a great transition.</p>
<h2 id="heading-hono-the-lightweight-challenger">Hono: The Lightweight Challenger</h2>
<p>While tRPC is amazing for full-stack TypeScript apps where you control both ends, <a target="_blank" href="https://hono.dev/">Hono</a> takes a slightly different approach. It's a lightweight web framework that gives you type safety while still being a traditional HTTP framework.</p>
<p>Here's the same example in Hono:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>;
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>;
<span class="hljs-keyword">import</span> { zValidator } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/zod-validator'</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono();

<span class="hljs-keyword">const</span> userSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

app.post(<span class="hljs-string">'/api/users'</span>, zValidator(<span class="hljs-string">'json'</span>, userSchema), <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { name, email } = c.req.valid(<span class="hljs-string">'json'</span>);
  <span class="hljs-keyword">return</span> c.json({ id: <span class="hljs-number">1</span>, name, email });
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AppType = <span class="hljs-keyword">typeof</span> app;
</code></pre>
<p>On the frontend, you can use Hono’s RPC client:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { hc } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AppType } <span class="hljs-keyword">from</span> <span class="hljs-string">'./server'</span>;

<span class="hljs-keyword">const</span> client = hc&lt;AppType&gt;(<span class="hljs-string">'http://localhost:3000'</span>);

<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> client.api.users.$post({
  json: { name: <span class="hljs-string">'Bob'</span>, email: <span class="hljs-string">'bob@example.com'</span> }
});

<span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> response.json();
<span class="hljs-comment">// user is fully typed!</span>
</code></pre>
<p>Hono is incredibly fast (it runs on Cloudflare Workers, Deno, Bun, and Node.js), and it gives you that sweet type safety while still being a "regular" HTTP framework. You get RESTful routes, middleware, and all the familiar patterns – just with TypeScript powers.</p>
<h2 id="heading-why-this-matters-these-days">Why This Matters These Days</h2>
<p>You might think to yourself, “Okay, I know what you mean, but why should I care about it?” There’s a reason why these tools are being utilized more now than ever before.</p>
<h3 id="heading-developer-experience-is-essential">Developer experience is essential</h3>
<p>In 2026 and beyond, we’ll no longer accept long feedback loops. The ability to modify your backend code and see what might break on your frontend application without having to run the application will be fantastic for productivity. We’ll spend less time fixing bugs and more time creating new functionalities.</p>
<h3 id="heading-smaller-teams-better-apps">Smaller teams, better apps</h3>
<p>With tRPC or Hono, one developer can create an entire full-stack application with type safety at a very fast pace because they don’t have to switch back and forth between REST documentation and TypeScript interfaces – all the data is flowing to and from their backend code directly to their frontend.</p>
<h3 id="heading-the-end-of-works-on-my-machine">The end of “Works on my machine“</h3>
<p>With type safety, errors are caught at compile time instead of at the time your end-user clicks on a button. This is especially impactful when working in larger teams, when the backend developers and front-end developers may not be in constant communication with one another.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Want to try this out? Here's the fastest way:</p>
<p>For tRPC:</p>
<pre><code class="lang-bash">npm create @trpc/next-app@latest
</code></pre>
<p>This scaffolds a Next.js app with tRPC already configured. Check out the <a target="_blank" href="https://trpc.io/docs/client/nextjs">official tRPC docs</a> for more.</p>
<p>For Hono:</p>
<pre><code class="lang-bash">npm create hono@latest
</code></pre>
<p>Pick your runtime (Node.js, Cloudflare Workers, etc.), and you're off to the races. The <a target="_blank" href="https://hono.dev/">Hono documentation</a> is excellent and super approachable.</p>
<h2 id="heading-the-future-is-type-safe">The Future is Type-Safe</h2>
<p>Look, REST isn't going anywhere, and GraphQL has its place. But for full-stack TypeScript developers, tRPC and Hono represent something special: type safety without the ceremony. No code generation or no schema duplication, just TypeScript doing what it does best.</p>
<p>In the future, when you start a new project, give one of these a shot. Your future self – the one who's refactoring code at 2 AM – will thank you.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Production-Ready Web Apps with the Hono Framework: A Deep Dive ]]>
                </title>
                <description>
                    <![CDATA[ As a dev, you’d probably like to write your application once and not have to worry so much about where it's going to run. This is what the open source framework Hono lets you do, and it’s a game-changer. Hono is a small, incredibly fast web framework... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-production-ready-web-apps-with-hono/</link>
                <guid isPermaLink="false">68bf3ea02c935a9d306bb65a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hono ]]>
                    </category>
                
                    <category>
                        <![CDATA[ javascript framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mayur Vekariya ]]>
                </dc:creator>
                <pubDate>Mon, 08 Sep 2025 20:37:52 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757363825321/562644c8-b2b3-4c1c-92c2-736bcade5aac.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a dev, you’d probably like to write your application once and not have to worry so much about where it's going to run. This is what the open source framework Hono lets you do, and it’s a game-changer. Hono is a small, incredibly fast web framework that embraces the "write once, run anywhere" philosophy.</p>
<p>The JavaScript ecosystem moves quickly. One minute, we're building monolithic Node.js servers. The next, it's all about serverless functions and running code at the edge on platforms like Cloudflare or Vercel. Staying current can feel like a full-time job.</p>
<p><a target="_blank" href="https://hono.dev/">Hono</a> is built on top of Web Standards – the same <code>Request</code> and <code>Response</code> objects in your browser – which means your code is naturally portable across almost any JavaScript runtime.</p>
<p>This guide is a deep dive into this powerful little framework, designed to help you build real, production-ready applications. We’ll skip the quick "Hello, World!" and jump straight into the patterns and features you will actually use, with plenty of detailed code examples along the way.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-you-will-learn-in-this-guide">What You Will Learn in This Guide</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites-for-following-along">Prerequisites for Following Along</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-a-professional-hono-project">How to Set Up a Professional Hono Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-understand-honos-core-api">How to Understand Hono's Core API</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-context-object-in-depth">The Context Object in Depth</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-advanced-features-for-production-apps">How to Use Advanced Features for Production Apps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-deployment-guide-for-hono">Deployment Guide for Hono</a></p>
</li>
</ul>
<h3 id="heading-what-you-will-learn-in-this-guide">What You Will Learn in This Guide</h3>
<p>By the end of this tutorial, you will be able to:</p>
<ul>
<li><p>Structure a Hono project for both development and production.</p>
</li>
<li><p>Implement advanced routing patterns.</p>
</li>
<li><p>Leverage the full power of the <strong>Context</strong> object to manage requests and pass data between middleware.</p>
</li>
<li><p>Write complex custom middleware for authentication, logging, and error handling.</p>
</li>
<li><p>Validate incoming data using the official <strong>Zod</strong> validator for robust APIs.</p>
</li>
<li><p>Build a small, server-rendered application with <strong>JSX</strong> components.</p>
</li>
<li><p>Deploy a Hono application to various modern hosting platforms.</p>
</li>
</ul>
<h3 id="heading-prerequisites-for-following-along">Prerequisites for Following Along</h3>
<p>This is an in-depth guide, but it assumes you have some foundational knowledge. Before you start, you should have:</p>
<ul>
<li><p><strong>Node.js installed:</strong> Version 18 or higher is recommended.</p>
</li>
<li><p><strong>A code editor:</strong> Visual Studio Code is a great choice.</p>
</li>
<li><p><strong>Familiarity with TypeScript:</strong> You should understand basic types, functions, and <code>async</code>/<code>await</code>.</p>
</li>
<li><p><strong>Basic command-line knowledge:</strong> You should be comfortable running commands in your terminal.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-a-professional-hono-project">How to Set Up a Professional Hono Project</h2>
<p>You can get started with Hono using a single command. This will create a new project directory with a recommended structure and configuration files. When prompted, select the <code>nodejs</code> template and choose to install dependencies with your preferred package manager (for example, npm).</p>
<pre><code class="lang-bash">npm create hono@latest hono-production-app
</code></pre>
<p>The command will guide you through the setup:</p>
<pre><code class="lang-bash">&gt; npx create-hono hono-production-app

create-hono version 0.19.2
✔ Using target directory … hono-production-app
✔ Which template <span class="hljs-keyword">do</span> you want to use? nodejs
✔ Do you want to install project dependencies? Yes
✔ Which package manager <span class="hljs-keyword">do</span> you want to use? npm
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: <span class="hljs-built_in">cd</span> hono-production-app
</code></pre>
<p>Now, navigate into your new directory: <code>cd hono-production-app</code>. Let's look at the files that were created:</p>
<ul>
<li><p><code>package.json</code>: Defines your project's dependencies and scripts.</p>
</li>
<li><p><code>tsconfig.json</code>: The TypeScript configuration file.</p>
</li>
<li><p><code>src/index.ts</code>: The entry point of your application.</p>
</li>
</ul>
<p>Now, you can run <code>npm run dev</code> to start your development server. Navigate to <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a>, and you will see "Hello Hono!".</p>
<h2 id="heading-how-to-understand-honos-core-api">How to Understand Hono's Core API</h2>
<p>Hono's API is designed to be minimal, which makes it easy to learn – yet incredibly powerful.</p>
<h3 id="heading-how-to-use-advanced-routing-techniques">How to Use Advanced Routing Techniques</h3>
<p>You may already know <code>app.get()</code> and <code>app.post()</code> from Express, but Hono's router can do much more.</p>
<h4 id="heading-1-how-to-route-with-regular-expressions">1. How to Route with Regular Expressions</h4>
<p>You can constrain a URL parameter to match a specific regular expression. For example, to make sure an <code>:id</code> parameter only accepts numbers, you can do this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Only match routes like /users/123, not /users/abc</span>
app.get(<span class="hljs-string">'/users/:id{[0-9]+}'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> id = c.req.param(<span class="hljs-string">'id'</span>)
  <span class="hljs-keyword">return</span> c.text(<span class="hljs-string">`Fetching data for user ID: <span class="hljs-subst">${id}</span>`</span>)
})
</code></pre>
<h4 id="heading-2-how-to-use-optional-and-wildcard-routes">2. How to Use Optional and Wildcard Routes</h4>
<p>You can define routes that match multiple paths using wildcards (<code>*</code>) or handle optional parameters.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// This will match /files/image.png, /files/docs/report.pdf, and so on.</span>
app.get(<span class="hljs-string">'/files/*'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-comment">// c.req.path will contain the full matched path</span>
  <span class="hljs-keyword">return</span> c.text(<span class="hljs-string">`You are accessing the file at: <span class="hljs-subst">${c.req.path}</span>`</span>)
})

<span class="hljs-comment">// The '?' makes the '/:format?' part of the URL optional</span>
<span class="hljs-comment">// This will match both /api/posts and /api/posts/json</span>
app.get(<span class="hljs-string">'/api/posts/:format?'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> format = c.req.param(<span class="hljs-string">'format'</span>)
  <span class="hljs-keyword">if</span> (format === <span class="hljs-string">'json'</span>) {
    <span class="hljs-keyword">return</span> c.json({ message: <span class="hljs-string">'Here are the posts in JSON format.'</span> })
  }
  <span class="hljs-keyword">return</span> c.text(<span class="hljs-string">'Here are the posts in plain text.'</span>)
})
</code></pre>
<h4 id="heading-3-how-to-group-routes-with-approute">3. How to Group Routes with <code>app.route()</code></h4>
<p>For larger applications, you should organize your routes into logical groups. The <code>app.route()</code> method is perfect for this. It allows you to create modular routers and mount them on a specific prefix.</p>
<p>Let's create a more complex API structure for a blog.</p>
<p><code>src/routes/posts.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>

<span class="hljs-comment">// Create a new router instance specifically for posts</span>
<span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">new</span> Hono()

posts.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.json({ posts: [] }))
posts.post(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.json({ message: <span class="hljs-string">'Post created'</span> }, <span class="hljs-number">201</span>))
posts.get(<span class="hljs-string">'/:id'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.json({ post: { id: c.req.param(<span class="hljs-string">'id'</span>) } }))

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> posts
</code></pre>
<p><code>src/routes/authors.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>

<span class="hljs-keyword">const</span> authors = <span class="hljs-keyword">new</span> Hono()

authors.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.json({ authors: [] }))
authors.get(<span class="hljs-string">'/:id'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.json({ author: { id: c.req.param(<span class="hljs-string">'id'</span>) } }))

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> authors
</code></pre>
<p><code>src/index.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { serve } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/node-server'</span>
<span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>
<span class="hljs-keyword">import</span> { appendTrailingSlash } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono/trailing-slash'</span>;
<span class="hljs-keyword">import</span> posts <span class="hljs-keyword">from</span> <span class="hljs-string">'./routes/posts.js'</span>
<span class="hljs-keyword">import</span> authors <span class="hljs-keyword">from</span> <span class="hljs-string">'./routes/authors.js'</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono()

app.use(appendTrailingSlash());

app.route(<span class="hljs-string">'/posts/'</span>, posts)
app.route(<span class="hljs-string">'/authors/'</span>, authors)

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> c.text(<span class="hljs-string">'Hello Hono!'</span>)
})

serve({
  fetch: app.fetch,
  port: <span class="hljs-number">3000</span>
}, <span class="hljs-function">(<span class="hljs-params">info</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${info.port}</span>`</span>)
})
</code></pre>
<p>This pattern keeps your main <code>index.ts</code> file clean and makes your application much easier to navigate and maintain.</p>
<h2 id="heading-the-context-object-in-depth">The Context Object in Depth</h2>
<p>The <strong>Context</strong> (<code>c</code>) is the heart of Hono. It's an object that gets passed to every middleware and route handler, containing all the information related to the current request. It's essentially a container for the request (<code>c.req</code>) methods for creating a response (<code>c.json</code>, <code>c.html</code>, <code>c.text</code>), as well as a special property for passing data between middleware (<code>c.set</code> and <code>c.get</code>).</p>
<p>While this covers its most common and useful properties, the full Context object contains more. For a comprehensive list of all available properties and methods, you can refer to the official <a target="_blank" href="https://hono.dev/docs/api/context">Hono documentation</a>.</p>
<p>Let's explore how you can use the context object to pass data between middleware and handlers, a crucial technique for things like authentication.</p>
<p>The <code>c.set()</code> and <code>c.get()</code> methods allow you to store and retrieve typed data within the context of a single request.</p>
<p>Replace <code>src/index.ts</code> with this example for authentication:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Context, Next } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>

<span class="hljs-comment">// Define a type for the variables we will store in the context</span>
<span class="hljs-keyword">type</span> AppVariables = {
  user: {
    id: <span class="hljs-built_in">string</span>
    name: <span class="hljs-built_in">string</span>
    roles: <span class="hljs-built_in">string</span>[]
  }
}

<span class="hljs-comment">// Use a generic to tell our Hono app about the variables type</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono&lt;{ Variables: AppVariables }&gt;()

<span class="hljs-comment">// Middleware to "authenticate" a user from a header</span>
<span class="hljs-keyword">const</span> authMiddleware = <span class="hljs-keyword">async</span> (c: Context, next: Next) =&gt; {
  <span class="hljs-keyword">const</span> userId = c.req.header(<span class="hljs-string">'X-User-ID'</span>)
  <span class="hljs-keyword">if</span> (!userId) {
    <span class="hljs-keyword">return</span> c.json({ error: <span class="hljs-string">'Missing X-User-ID header'</span> }, <span class="hljs-number">401</span>)
  }

  <span class="hljs-comment">// In a real app, you would fetch this from a database</span>
  <span class="hljs-keyword">const</span> user = {
    id: userId,
    name: <span class="hljs-string">'Jane Doe'</span>,
    roles: [<span class="hljs-string">'admin'</span>, <span class="hljs-string">'editor'</span>],
  }

  <span class="hljs-comment">// Use c.set() to attach the user data to the context</span>
  c.set(<span class="hljs-string">'user'</span>, user)

  <span class="hljs-keyword">await</span> next()
}

app.get(<span class="hljs-string">'/admin/dashboard'</span>, authMiddleware, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-comment">// Use c.get() to retrieve the typed user data</span>
  <span class="hljs-keyword">const</span> user = c.get(<span class="hljs-string">'user'</span>)

  <span class="hljs-keyword">if</span> (!user.roles.includes(<span class="hljs-string">'admin'</span>)) {
    <span class="hljs-keyword">return</span> c.json({ error: <span class="hljs-string">'Forbidden'</span> }, <span class="hljs-number">403</span>)
  }

  <span class="hljs-keyword">return</span> c.json({
    message: <span class="hljs-string">`Welcome to the admin dashboard, <span class="hljs-subst">${user.name}</span>!`</span>,
    userId: user.id,
  })
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> app
</code></pre>
<p>Let's break down the important parts of the code above.</p>
<ul>
<li><p><strong>Typed context variables</strong>: We define a TypeScript type <code>AppVariables</code> and pass it as a generic to our Hono app <code>new Hono&lt;{ Variables: AppVariables }&gt;()</code>. This is a powerful feature that gives us full type-safety for our context variables, preventing typos and ensuring that the data we store and retrieve is exactly what we expect it to be.</p>
</li>
<li><p><strong>Custom middleware</strong>: The <code>authMiddleware</code> is a custom function that runs before our route handler. It inspects the incoming request's headers (<code>c.req.header('X-User-ID')</code>).</p>
</li>
<li><p><strong>Storing data</strong>: If a valid header is found, the middleware uses <code>c.set('user', user)</code> to store the user object on the context. This data is now available to any subsequent middleware or route handler for the same request.</p>
</li>
<li><p><strong>Retrieving data</strong>: The route handler <code>app.get('/admin/dashboard', ...)</code> then uses <code>c.get('user')</code> to retrieve the user object. Hono's type system ensures that <code>c.get('user')</code> returns a variable with the type <code>{ id: string; name: string; roles: string[]; }</code>.</p>
</li>
<li><p><strong>Flow control</strong>: If the user is missing or doesn't have the "admin" role, the middleware or handler can immediately send an error response using <code>c.json()</code> and a status code, preventing the request from proceeding further.</p>
</li>
</ul>
<p>Now, run <code>npm run dev</code>.</p>
<p>You can test with <code>curl</code> (add header):</p>
<pre><code class="lang-bash">curl -H <span class="hljs-string">"X-User-ID: 123"</span> http://localhost:3000/admin/dashboard
</code></pre>
<p>This will return a welcome message.</p>
<p>Without the header:</p>
<pre><code class="lang-bash">curl http://localhost:3000/admin/dashboard
</code></pre>
<p>This will return a <code>401</code> error.</p>
<p>This demonstrates how to pass typed data securely and efficiently between middleware and route handlers.</p>
<h2 id="heading-how-to-use-advanced-features-for-production-apps">How to Use Advanced Features for Production Apps</h2>
<p>Now we're ready to tackle the features you'll use every day in production: advanced middleware, data validation, and building full-stack applications.</p>
<h3 id="heading-how-to-use-advanced-middleware-patterns">How to Use Advanced Middleware Patterns</h3>
<p>Hono has a powerful set of built-in middleware, including JWT and caching. These are not separate libraries you have to install, but rather functions that come with the Hono package itself.</p>
<p><strong>Step 1:</strong> Replace <code>src/index.ts</code> with this example for JWT and caching:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>
<span class="hljs-keyword">import</span> { serve } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/node-server'</span>
<span class="hljs-keyword">import</span> { jwt, sign } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono/jwt'</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono()
<span class="hljs-keyword">const</span> SECRET = <span class="hljs-string">'my-secret-key'</span> <span class="hljs-comment">// Use an environment variable in production!</span>

<span class="hljs-comment">// Create a simple in-memory cache store</span>
<span class="hljs-keyword">const</span> cacheStore = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();

<span class="hljs-comment">// Custom caching middleware for Node.js</span>
app.use(<span class="hljs-string">'/api/public-data'</span>, <span class="hljs-keyword">async</span> (c, next) =&gt; {
  <span class="hljs-keyword">const</span> cacheKey = c.req.url;

  <span class="hljs-comment">// Check if the response is in our cache</span>
  <span class="hljs-keyword">if</span> (cacheStore.has(cacheKey)) {
    <span class="hljs-keyword">const</span> cachedItem = cacheStore.get(cacheKey);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Serving from custom in-memory cache.'</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(cachedItem.body, { headers: cachedItem.headers });
  }

  <span class="hljs-comment">// If not in cache, proceed to the route handler</span>
  <span class="hljs-keyword">await</span> next();

  <span class="hljs-comment">// After the handler returns, clone and store the response</span>
  <span class="hljs-keyword">if</span> (c.res) {
    <span class="hljs-keyword">const</span> newResponse = c.res.clone();
    <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> newResponse.text();
    <span class="hljs-keyword">const</span> headers = <span class="hljs-built_in">Object</span>.fromEntries(newResponse.headers.entries());
    cacheStore.set(cacheKey, { body, headers });
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Storing response in custom in-memory cache.'</span>);
  }
});

<span class="hljs-comment">// Login to get a JWT</span>
app.post(<span class="hljs-string">'/login'</span>, <span class="hljs-keyword">async</span> (c) =&gt; {
  <span class="hljs-keyword">const</span> { username } = <span class="hljs-keyword">await</span> c.req.json()
  <span class="hljs-keyword">if</span> (username === <span class="hljs-string">'admin'</span>) {
    <span class="hljs-keyword">const</span> payload = {
      sub: username,
      role: <span class="hljs-string">'admin'</span>,
      exp: <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) + <span class="hljs-number">60</span> * <span class="hljs-number">5</span>, <span class="hljs-comment">// 5 minutes expiration</span>
    }
    <span class="hljs-keyword">const</span> token = <span class="hljs-keyword">await</span> sign(payload, SECRET)
    <span class="hljs-keyword">return</span> c.json({ token })
  }
  <span class="hljs-keyword">return</span> c.json({ error: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>)
})

<span class="hljs-comment">// Protected route</span>
app.get(
  <span class="hljs-string">'/api/protected'</span>,
  jwt({ secret: SECRET }),
  <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> payload = c.get(<span class="hljs-string">'jwtPayload'</span>)
    <span class="hljs-keyword">return</span> c.json({ message: <span class="hljs-string">'You have access!'</span>, payload })
  }
)

<span class="hljs-comment">// Cached route</span>
app.get(
  <span class="hljs-string">'/api/public-data'</span>,
  <span class="hljs-keyword">async</span> (c) =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Executing handler with delay...'</span>);
    <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">1000</span>)) <span class="hljs-comment">// Simulate a delay</span>
    <span class="hljs-keyword">return</span> c.json({ data: <span class="hljs-string">'This is some public data that rarely changes.'</span> })
  }
)

serve({ fetch: app.fetch, port: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">info</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${info.port}</span>`</span>)
})
</code></pre>
<p>The code above shows two different types of middleware in action.</p>
<p>First, <strong>JWT middleware</strong> (<code>jwt</code>) is a powerful way to secure your routes. When we call <code>jwt({ secret: SECRET })</code>, we're telling Hono to check for a valid JWT in the <code>Authorization</code> header of the incoming request. If a valid token is found, it decodes the payload and attaches it to the context, where we can retrieve it with <code>c.get('jwtPayload')</code>. If no token is found or if the token is invalid, the middleware automatically stops the request and returns a <code>401 Unauthorized</code> error.</p>
<p>We also have <strong>Custom Cache Middleware</strong> which demonstrates the power of Hono's middleware system for in-memory caching. The middleware first checks an in-memory <code>Map</code> to see if a response for the current URL already exists. If it does, it immediately returns the cached response, preventing the route handler from ever being executed. If the response is not in the cache, it allows the request to continue to the handler. After the handler returns, the middleware intercepts the response and stores a copy in the cache before sending it back to the client. This is a robust and reliable pattern for Node.js environments.</p>
<p><strong>Step 2:</strong> Run <code>npm run dev</code>.</p>
<p><strong>Step 3:</strong> Test the login endpoint with <code>curl</code>:</p>
<p>First, let's test the login endpoint to get a JWT. Open a new terminal and run the following command. The command sends a <code>POST</code> request to the <code>/login</code> endpoint with <code>username: "admin"</code> in the request body.</p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/login -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'{"username": "admin"}'</span>
</code></pre>
<p>This will return a JSON object with a JWT. Copy this token for the next step.</p>
<p>Now, let's test the protected route. We'll use the token we just received in the <code>Authorization</code> header. Replace <code>&lt;your_jwt_token&gt;</code> with the token you copied.</p>
<pre><code class="lang-bash">curl http://localhost:3000/api/protected -H <span class="hljs-string">"Authorization: Bearer &lt;your_jwt_token&gt;"</span>
</code></pre>
<p>You should get a success message with the decoded payload.</p>
<p>Finally, let's test the cached route. You’ll need to run a production build and run the file with <code>node</code> for this to work.</p>
<p>First, run the following command. The <code>1000</code> millisecond delay in the code will make this request take about a second.</p>
<pre><code class="lang-bash">curl -o /dev/null -s -w <span class="hljs-string">'Total: %{time_total}s\n'</span> http://localhost:3000/api/public-data
</code></pre>
<p>Immediately run the <strong>exact same command again</strong>. This time, the response will be almost instantaneous because our custom cache middleware served the response directly from its in-memory store, completely bypassing the <code>setTimeout</code> in the route handler. Run it a third time, and you'll see a similar near-instantaneous response.</p>
<p>Here's an example of what your terminal output should look like when testing the cache. The first request took around 1 second, but subsequent requests were a matter of milliseconds.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757125753352/180cc4a7-f361-4966-a26f-a5d8251f77a4.png" alt="180cc4a7-f361-4966-a26f-a5d8251f77a4" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-create-a-global-error-handler">How to Create a Global Error Handler</h3>
<p>You can define a single global error handler with <code>app.onError()</code>. This is useful for handling unexpected errors in a centralized way, such as validation failures.</p>
<p>Add the following code to your <code>src/index.ts</code>:</p>
<pre><code class="lang-typescript">app.get(<span class="hljs-string">'/users/:id'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> id = c.req.param(<span class="hljs-string">'id'</span>)
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(<span class="hljs-built_in">Number</span>(id))) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'User ID must be a number.'</span>)
  }
  <span class="hljs-keyword">return</span> c.text(<span class="hljs-string">`User ID is <span class="hljs-subst">${id}</span>`</span>)
})

app.onError(<span class="hljs-function">(<span class="hljs-params">err, c</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`<span class="hljs-subst">${err}</span>`</span>)
  <span class="hljs-keyword">return</span> c.json({
    success: <span class="hljs-literal">false</span>,
    message: err.message,
  }, <span class="hljs-number">500</span>)
})
</code></pre>
<p>Now, if you visit <a target="_blank" href="http://localhost:3000/users/abc"><code>http://localhost:3000/users/abc</code></a>, you will get a JSON error response instead of an uncaught exception.</p>
<h3 id="heading-how-to-handle-validation-with-zod">How to Handle Validation with Zod</h3>
<p>For robust APIs, data validation is essential. Hono integrates seamlessly with <a target="_blank" href="https://zod.dev/">Zod</a>, a popular TypeScript-first schema validation library.</p>
<p><strong>Step 1:</strong> Install the necessary dependencies:</p>
<pre><code class="lang-bash">npm install zod @hono/zod-validator
</code></pre>
<p><strong>Step 2:</strong> Replace <code>src/index.ts</code> with the validation example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>
<span class="hljs-keyword">import</span> { serve } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/node-server'</span>
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>
<span class="hljs-keyword">import</span> { zValidator } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/zod-validator'</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono()

<span class="hljs-comment">// Define a Zod schema for the user creation data</span>
<span class="hljs-keyword">const</span> createUserSchema = z.object({
  username: z.string().min(<span class="hljs-number">3</span>).max(<span class="hljs-number">20</span>),
  email: z.string().email(),
  age: z.number().int().positive(),
  tags: z.array(z.string()).optional(),
})

app.post(
  <span class="hljs-string">'/users'</span>,
  zValidator(<span class="hljs-string">'json'</span>, createUserSchema), <span class="hljs-comment">// Use zValidator middleware</span>
  (c) =&gt; {
    <span class="hljs-comment">// The validated data is available on c.req.valid()</span>
    <span class="hljs-keyword">const</span> user = c.req.valid(<span class="hljs-string">'json'</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Creating user: <span class="hljs-subst">${user.username}</span> with email <span class="hljs-subst">${user.email}</span>`</span>)
    <span class="hljs-keyword">return</span> c.json({
      success: <span class="hljs-literal">true</span>,
      message: <span class="hljs-string">'User created successfully!'</span>,
      user: user,
    }, <span class="hljs-number">201</span>)
  }
)

serve({ fetch: app.fetch, port: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">info</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${info.port}</span>`</span>)
})
</code></pre>
<p>This is how the Zod validation is working:</p>
<ol>
<li><p>We first define a schema called <code>createUserSchema</code> using <code>z.object()</code>. This schema is a blueprint for the expected data structure. We use Zod's built-in methods like <code>z.string().min(3)</code>, <code>z.string().email()</code>, and <code>z.number().int().positive()</code> to specify validation rules for each property. For example, <code>username</code> must be a string between 3 and 20 characters, <code>email</code> must be a valid email format, and <code>age</code> must be a positive integer.</p>
</li>
<li><p>We then apply the <a target="_blank" href="https://github.com/honojs/middleware/tree/main/packages/zod-validator"><code>zValidator</code></a> middleware to our route handler. The first argument, <code>'json'</code>, tells the middleware to validate the incoming request's JSON body. The second argument, <code>createUserSchema</code>, tells it which schema to use for the validation.</p>
</li>
<li><p>The <code>zValidator</code> middleware automatically does the heavy lifting. When a request hits the <code>/users</code> endpoint, it will parse the JSON body and attempt to validate it against <code>createUserSchema</code>. If the data is invalid (for example, the <code>email</code> is not in a valid format), the middleware will immediately stop the request and return a <code>400 Bad Request</code> status with a detailed error message, all without us having to write any manual checks.</p>
</li>
<li><p>If the data is valid, the middleware makes it available on the <code>Context</code> object, which we can access with <code>c.req.valid('json')</code>. Hono's type system ensures that this data is correctly typed according to the Zod schema, so we can use it safely in our handler.</p>
</li>
</ol>
<p><strong>Step 3:</strong> Run <code>npm run dev</code>.</p>
<p><strong>Step 4:</strong> Test with <code>curl</code> (valid data):</p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/users -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'{"username": "testuser", "email": "test@example.com", "age": 25}'</span>
</code></pre>
<p>This will return a success message.</p>
<p>Test with invalid data (for example, bad email):</p>
<pre><code class="lang-bash">curl -X POST http://localhost:3000/users -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'{"username": "testuser", "email": "invalid-email", "age": 25}'</span>
</code></pre>
<p>This will automatically return a <code>400</code> status with a detailed error message from Zod.</p>
<h3 id="heading-how-to-build-a-full-stack-app-with-jsx">How to Build a Full-Stack App with JSX</h3>
<p>Hono supports server-side rendering with JSX, allowing you to build full-stack applications without needing a separate framework.</p>
<p><strong>Step 1:</strong> Create <code>src/components/Layout.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { html } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono/html'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Layout = <span class="hljs-function">(<span class="hljs-params">props: { title: <span class="hljs-built_in">string</span>; children?: <span class="hljs-built_in">any</span> }</span>) =&gt;</span> html`<span class="xml">
  <span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span></span><span class="hljs-subst">${props.title}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> { <span class="hljs-attribute">font-family</span>: sans-serif; <span class="hljs-attribute">background</span>: <span class="hljs-number">#f4f4f4</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>; }
        <span class="hljs-selector-class">.container</span> { <span class="hljs-attribute">max-width</span>: <span class="hljs-number">800px</span>; <span class="hljs-attribute">margin</span>: <span class="hljs-number">2rem</span> auto; <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>; <span class="hljs-attribute">background</span>: white; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>; }
        <span class="hljs-selector-tag">header</span> { <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">1rem</span>; }
        <span class="hljs-selector-tag">footer</span> { <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>; <span class="hljs-attribute">text-align</span>: center; <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.8rem</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#777</span>; }
      </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="hljs-subst">${props.title}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
          </span><span class="hljs-subst">${props.children}</span><span class="xml">
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Powered by Hono<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
`</span>
</code></pre>
<p><strong>Step 2:</strong> Create <code>src/components/PostItem.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PostItem = <span class="hljs-function">(<span class="hljs-params">props: { post: { id: <span class="hljs-built_in">number</span>; title: <span class="hljs-built_in">string</span>; author: <span class="hljs-built_in">string</span> } }</span>) =&gt;</span> (
  &lt;article style=<span class="hljs-string">"border-bottom: 1px solid #eee; padding: 1rem 0;"</span>&gt;
    &lt;h3&gt;&lt;a href={<span class="hljs-string">`/posts/<span class="hljs-subst">${props.post.id}</span>`</span>}&gt;{props.post.title}&lt;<span class="hljs-regexp">/a&gt;&lt;/</span>h3&gt;
    &lt;p&gt;&lt;em&gt;By {props.post.author}&lt;<span class="hljs-regexp">/em&gt;&lt;/</span>p&gt;
  &lt;/article&gt;
)
</code></pre>
<p><strong>Step 3:</strong> Update <code>src/index.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Hono } <span class="hljs-keyword">from</span> <span class="hljs-string">'hono'</span>
<span class="hljs-keyword">import</span> { serve } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/node-server'</span>
<span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Layout'</span>
<span class="hljs-keyword">import</span> { PostItem } <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/PostItem'</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Hono()

<span class="hljs-comment">// Mock data</span>
<span class="hljs-keyword">const</span> posts = [
  { id: <span class="hljs-number">1</span>, title: <span class="hljs-string">'Getting Started with Hono'</span>, author: <span class="hljs-string">'Alice'</span> },
  { id: <span class="hljs-number">2</span>, title: <span class="hljs-string">'Advanced Middleware Patterns'</span>, author: <span class="hljs-string">'Bob'</span> },
  { id: <span class="hljs-number">3</span>, title: <span class="hljs-string">'Deploying Hono to the Edge'</span>, author: <span class="hljs-string">'Charlie'</span> },
]

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> c.html(
    &lt;Layout title=<span class="hljs-string">"My Hono Blog"</span>&gt;
      &lt;h2&gt;Recent Posts&lt;/h2&gt;
      {posts.length &gt; <span class="hljs-number">0</span>
        ? posts.map(<span class="hljs-function"><span class="hljs-params">post</span> =&gt;</span> &lt;PostItem post={post} /&gt;)
        : &lt;p&gt;No posts yet!&lt;/p&gt;
      }
    &lt;/Layout&gt;
  )
})

serve({ fetch: app.fetch, port: <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">info</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${info.port}</span>`</span>)
})
</code></pre>
<p>Make sure to update the <code>dev</code> script in your <code>package.json</code> file to have <code>src/index.tsx</code> as the starting point.</p>
<pre><code class="lang-json"><span class="hljs-string">"dev"</span>: <span class="hljs-string">"tsx watch src/index.tsx"</span>
</code></pre>
<p><strong>Step 4:</strong> Run <code>npm run dev</code> and visit <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a>. You will see a fully rendered blog page with the list of posts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756952439130/27ee63cc-6d60-4372-9634-4d1eadf33f32.png" alt="Blog page with list of posts" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-deployment-guide-for-hono">Deployment Guide for Hono</h2>
<p>You have built your application, and now it's time to share it with the world. Here’s how you can deploy your Hono app to some of the most popular platforms.</p>
<h3 id="heading-how-to-deploy-to-nodejs">How to Deploy to Node.js</h3>
<p>For a traditional server environment, you can use the <code>@hono/node-server</code> adapter and a process manager like <code>pm2</code> for production.</p>
<p><code>src/index.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { serve } <span class="hljs-keyword">from</span> <span class="hljs-string">'@hono/node-server'</span>
<span class="hljs-keyword">import</span> app <span class="hljs-keyword">from</span> <span class="hljs-string">'./app'</span> <span class="hljs-comment">// Assuming your Hono app is in app.ts</span>

serve({ fetch: app.fetch, port: <span class="hljs-number">3000</span> })
</code></pre>
<p>You will then build your TypeScript to JavaScript and run <code>pm2 start dist/index.js</code> to run it in the background.</p>
<h3 id="heading-how-to-deploy-to-cloudflare-workers">How to Deploy to Cloudflare Workers</h3>
<p>Hono's true power lies in its portability. The <code>create hono</code> command can set up a project specifically for Cloudflare Workers.</p>
<p>Run the following command and select the <code>cloudflare-workers</code> template:</p>
<pre><code class="lang-bash">npm create hono@latest my-app-hono-cloudflare-worker

create-hono version 0.19.2
✔ Using target directory … my-app-hono-cloudflare-worker
? Which template <span class="hljs-keyword">do</span> you want to use?
  aws-lambda
  bun
❯ cloudflare-workers
  cloudflare-workers+vite
  deno
  fastly
  lambda-edge
</code></pre>
<p>The setup process is identical to the Node.js example, but the project structure is optimized for Cloudflare.</p>
<p>Once the project is set up, you only need to type one command to deploy your application to Cloudflare:</p>
<pre><code class="lang-bash">wrangler deploy
</code></pre>
<p>This command will prompt you to log in to your Cloudflare account and will handle the entire deployment process automatically.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>You've made it! We’ve covered a lot in this guide. You started with a professional project setup and moved all the way through advanced routing, context management, complex middleware patterns, robust data validation, and full-stack JSX components.</p>
<p>You now have the knowledge and the tools to build serious, production-ready applications with Hono. Its simple API doesn't limit its power. Rather, it enhances it by getting out of your way and letting you focus on building great features. And with its helpful portability, you can be confident that the application you build today can be deployed to the platforms of tomorrow.</p>
<p>The web development ecosystem will continue to evolve, but by building on Web Standards, Hono is a framework that's built to last.</p>
<p>To continue your journey, I highly recommend exploring the official <a target="_blank" href="https://hono.dev/docs/">Hono documentation</a>, which is full of even more examples and guides.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
