Skip to Content

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-app

Setup

1. Create Project

deno init --npm hono --template=deno my-app cd my-app

No 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 start

Access 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.html

Serve 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.txt
  • http://localhost:8000/static/image.png
  • http://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 .

{ "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/assert

Write 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.ts

With coverage:

deno test --coverage=coverage main_test.ts deno coverage coverage

Deno Deploy

Deno Deploy  is a globally distributed edge platform for running JavaScript and TypeScript.

Deploy from GitHub

  1. Push your code to GitHub
  2. Go to dash.deno.com/new 
  3. Connect your repository
  4. Select the entry file (main.ts)
  5. Click Deploy

Every push to your repository triggers automatic deployment.

Deploy from CLI

Install Deno Deploy CLI:

deno install -A --global jsr:@deno/deployctl

Deploy:

deployctl deploy --project=my-project main.ts

Environment Variables

Set environment variables in the Deno Deploy dashboard:

  1. Go to your project settings
  2. Add environment variables
  3. 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/mydb

Load with:

deno run --allow-net --allow-env --env main.ts

Deno 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.ts

In 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