Last Updated: 3/9/2026
Hello World
Let’s build your first Hono application from scratch and understand what’s happening.
Prerequisites
Before you start, complete the Quick Start to set up your development environment.
The Simplest Hono App
Create a file called index.ts:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default appThis is a complete web application. Let’s break it down.
Understanding the Code
Import Hono
import { Hono } from 'hono'Hono is the main class. Everything starts here.
Create an App Instance
const app = new Hono()This creates your application. You can create multiple apps and compose them together.
Define a Route
app.get('/', (c) => {
return c.text('Hello Hono!')
})This registers a route handler:
app.get()— Handle GET requests'/'— The path to match(c) => { ... }— The handler functionc— The Context object (more on this below)c.text('Hello Hono!')— Return a text response
Export the App
export default appDifferent runtimes expect different exports. Cloudflare Workers and Bun use this pattern. For Node.js, you’d start the server differently (see Node.js deployment).
The Context Object
The c parameter is a Context object. It’s your interface to the request and response:
app.get('/hello', (c) => {
// Access the request
const userAgent = c.req.header('User-Agent')
// Set response headers
c.header('X-Custom-Header', 'Hello')
// Return different response types
return c.text('Plain text')
// return c.json({ message: 'JSON response' })
// return c.html('<h1>HTML response</h1>')
})Learn more in Context API.
Different Response Types
Text Response
app.get('/text', (c) => {
return c.text('Hello Hono!')
})Returns Content-Type: text/plain.
JSON Response
app.get('/json', (c) => {
return c.json({
ok: true,
message: 'Hello Hono!',
})
})Returns Content-Type: application/json.
HTML Response
app.get('/html', (c) => {
return c.html('<h1>Hello Hono!</h1>')
})Returns Content-Type: text/html.
Custom Status Code
app.post('/posts', (c) => {
return c.json({ message: 'Created!' }, 201)
})The second argument sets the HTTP status code.
Custom Headers
app.get('/custom', (c) => {
return c.json(
{ message: 'Hello' },
200,
{ 'X-Custom': 'My header' }
)
})The third argument sets custom headers.
HTTP Methods
Handle different HTTP methods:
app.get('/posts', (c) => c.text('List posts')) // GET
app.post('/posts', (c) => c.text('Create post')) // POST
app.put('/posts/:id', (c) => c.text('Update post')) // PUT
app.delete('/posts/:id', (c) => c.text('Delete')) // DELETEPath Parameters
Capture values from the URL:
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
return c.text(`Post ID: ${id}`)
})Visit /posts/123 → “Post ID: 123”
Multiple Parameters
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param()
return c.text(`Post ${postId}, Comment ${commentId}`)
})Query Parameters
Access query strings:
app.get('/search', (c) => {
const query = c.req.query('q')
const page = c.req.query('page') ?? '1'
return c.text(`Search: ${query}, Page: ${page}`)
})Visit /search?q=hono&page=2 → “Search: hono, Page: 2”
Request Body
Handle POST data:
app.post('/posts', async (c) => {
const body = await c.req.json()
return c.json({
message: 'Created!',
data: body,
}, 201)
})Note: Use
awaitwhen accessing the request body.
Raw Response
You can also return a raw Response object:
app.get('/raw', () => {
return new Response('Raw response', {
status: 200,
headers: { 'Content-Type': 'text/plain' },
})
})This is useful when you need full control.
What’s Next
Learn routing: Read Basic Concepts to understand request/response flow and middleware.
Add middleware: Check out the Middleware Guide to add authentication, logging, and more.
Go deeper: Explore the Routing guide for advanced patterns like wildcards and regex.
Deploy: See Deployment guides for your platform.