Last Updated: 3/9/2026
Deno
Deno is a modern JavaScript runtime built on V8 with native TypeScript support, secure by default, and built-in tooling. Deno Deploy is Deno’s serverless edge platform.
Hono works excellently on Deno with zero configuration and no build step required.
Requirements
Install Deno by following the official installation guide .
Quick Start
Create a new Hono app for Deno:
deno init --npm hono --template=deno my-appSetup
1. Create Project
deno init --npm hono --template=deno my-app
cd my-appNo npm install needed—Deno manages dependencies automatically!
2. Write Your Application
Edit main.ts:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Deno!')
})
app.get('/api/hello', (c) => {
return c.json({
message: 'Hello from Deno',
version: Deno.version.deno,
})
})
Deno.serve(app.fetch)3. Run Locally
Start the development server:
deno task startAccess http://localhost:8000 in your browser.
Configuration
Change Port Number
Specify the port in Deno.serve():
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Deno!'))
Deno.serve({ port: 3000 }, app.fetch)
console.log('Server running on http://localhost:3000')deno.json Configuration
Configure tasks, imports, and compiler options:
{
"tasks": {
"start": "deno run --allow-net --allow-env main.ts",
"dev": "deno run --allow-net --allow-env --watch main.ts"
},
"imports": {
"hono": "jsr:@hono/hono"
},
"compilerOptions": {
"jsx": "precompile",
"jsxImportSource": "hono/jsx"
}
}Serving Static Files
Use serveStatic from hono/deno:
Directory Structure
.
├── favicon.ico
├── main.ts
└── static
├── hello.txt
├── image.png
└── demo
└── index.htmlServe Static Files
import { Hono } from 'hono'
import { serveStatic } from 'hono/deno'
const app = new Hono()
// Serve files from ./static at /static/*
app.use('/static/*', serveStatic({ root: './' }))
// Serve favicon
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))
// Fallback for missing files
app.get('*', serveStatic({ path: './static/fallback.txt' }))
app.get('/api/hello', (c) => c.text('API works!'))
Deno.serve(app.fetch)Access files:
http://localhost:8000/static/hello.txthttp://localhost:8000/static/image.pnghttp://localhost:8000/favicon.ico
Rewrite Request Paths
Map URLs to different directories:
app.use(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) =>
path.replace(/^\/static/, '/public'),
})
)Custom MIME Types
app.use(
'/static/*',
serveStatic({
mimes: {
m3u8: 'application/vnd.apple.mpegurl',
ts: 'video/mp2t',
},
})
)onFound Callback
Run logic when a file is found:
app.use(
'/static/*',
serveStatic({
onFound: (_path, c) => {
c.header('Cache-Control', 'public, immutable, max-age=31536000')
},
})
)onNotFound Callback
Handle missing files:
app.use(
'/static/*',
serveStatic({
onNotFound: (path, c) => {
console.log(`${path} not found, requested: ${c.req.path}`)
},
})
)Precompressed Files
Serve .br or .gz files based on Accept-Encoding:
app.use(
'/static/*',
serveStatic({
precompressed: true, // Serves .br, .zst, or .gz if available
})
)npm vs JSR
Hono is available on both npm and JSR .
Using JSR (Recommended)
{
"imports": {
"hono": "jsr:@hono/hono",
"hono/": "jsr:@hono/hono/"
}
}Using npm
{
"imports": {
"hono": "npm:hono",
"hono/": "npm:/hono/"
}
}Middleware Compatibility
Use Hono from the same registry as your middleware:
With npm middleware:
{
"imports": {
"hono": "npm:hono",
"zod": "npm:zod",
"@hono/zod-validator": "npm:@hono/zod-validator"
}
}With JSR middleware:
{
"imports": {
"hono": "jsr:@hono/hono",
"zod": "npm:zod",
"@hono/zod-validator": "jsr:@hono/zod-validator"
}
}Testing
Deno has built-in testing with Deno.test:
Install Test Dependencies
deno add jsr:@std/assertWrite Tests
Create main_test.ts:
import { Hono } from 'hono'
import { assertEquals } from '@std/assert'
Deno.test('GET / returns Hello Deno', async () => {
const app = new Hono()
app.get('/', (c) => c.text('Hello Deno!'))
const res = await app.request('http://localhost/')
assertEquals(res.status, 200)
assertEquals(await res.text(), 'Hello Deno!')
})
Deno.test('GET /api/hello returns JSON', async () => {
const app = new Hono()
app.get('/api/hello', (c) => c.json({ message: 'Hello' }))
const res = await app.request('http://localhost/api/hello')
const data = await res.json()
assertEquals(res.status, 200)
assertEquals(data.message, 'Hello')
})Run Tests
deno test main_test.tsWith coverage:
deno test --coverage=coverage main_test.ts
deno coverage coverageDeno Deploy
Deno Deploy is a globally distributed edge platform for running JavaScript and TypeScript.
Deploy from GitHub
- Push your code to GitHub
- Go to dash.deno.com/new
- Connect your repository
- Select the entry file (
main.ts) - Click Deploy
Every push to your repository triggers automatic deployment.
Deploy from CLI
Install Deno Deploy CLI:
deno install -A --global jsr:@deno/deployctlDeploy:
deployctl deploy --project=my-project main.tsEnvironment Variables
Set environment variables in the Deno Deploy dashboard:
- Go to your project settings
- Add environment variables
- Access them via
Deno.env.get():
const app = new Hono()
app.get('/config', (c) => {
return c.json({
apiUrl: Deno.env.get('API_URL'),
environment: Deno.env.get('DENO_DEPLOYMENT_ID') ? 'production' : 'development',
})
})For local development, create .env:
API_URL=http://localhost:8000
DATABASE_URL=postgresql://localhost/mydbLoad with:
deno run --allow-net --allow-env --env main.tsDeno KV
Deno KV is a built-in key-value database:
import { Hono } from 'hono'
const app = new Hono()
const kv = await Deno.openKv()
app.get('/counter', async (c) => {
const result = await kv.get(['counter'])
const count = (result.value as number) || 0
await kv.set(['counter'], count + 1)
return c.json({ count: count + 1 })
})
Deno.serve(app.fetch)Permissions
Deno is secure by default. Grant permissions explicitly:
# Network access
deno run --allow-net main.ts
# Environment variables
deno run --allow-env main.ts
# File system read
deno run --allow-read main.ts
# All permissions
deno run -A main.tsIn deno.json:
{
"tasks": {
"start": "deno run --allow-net --allow-env main.ts"
}
}Best Practices
Use TypeScript
No build step required:
interface User {
id: number
name: string
}
app.get('/users/:id', async (c) => {
const id = parseInt(c.req.param('id'))
const user: User = await fetchUser(id)
return c.json(user)
})Handle Errors
app.onError((err, c) => {
console.error('Error:', err)
return c.json(
{ error: 'Internal Server Error' },
500
)
})Use Deno Standard Library
import { assertEquals } from 'jsr:@std/assert'
import { delay } from 'jsr:@std/async'
import { parse } from 'jsr:@std/yaml'What’s Next
- Deno Deploy Documentation - Official Deno Deploy docs
- Deno KV - Learn about Deno’s database
- Best Practices - Hono app patterns