Skip to main content
The Adapter helper provides utilities for detecting the current JavaScript runtime and accessing environment variables in a runtime-agnostic way. This enables writing code that works across different platforms like Node.js, Deno, Bun, Cloudflare Workers, and more.

Import

import { env, getRuntimeKey } from 'hono/adapter'

Runtime Detection

getRuntimeKey

Detect the current JavaScript runtime:
import { getRuntimeKey } from 'hono/adapter'

const runtime = getRuntimeKey()
console.log(runtime) // 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'other'
Returns: Runtime - A string identifying the current runtime
type Runtime = 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'other'

Supported Runtimes

  • 'node' - Node.js
  • 'deno' - Deno
  • 'bun' - Bun
  • 'workerd' - Cloudflare Workers
  • 'fastly' - Fastly Compute
  • 'edge-light' - Vercel Edge Runtime and similar
  • 'other' - Unknown or unsupported runtime

Environment Variables

env

Access environment variables in a runtime-agnostic way:
import { env } from 'hono/adapter'

app.get('/var', (c) => {
  const { DATABASE_URL } = env<{ DATABASE_URL: string }>(c)
  return c.json({ url: DATABASE_URL })
})
Parameters:
c
Context
required
The Hono context object
runtime
Runtime
Optional runtime override. If not specified, automatically detected using getRuntimeKey()
Returns: T & Context['env'] - Object containing environment variables

Type-safe Environment Variables

With Generic Type Parameter

Define environment variable types inline:
import { env } from 'hono/adapter'

app.get('/config', (c) => {
  const { API_KEY, PORT, DEBUG } = env<{
    API_KEY: string
    PORT: string
    DEBUG: string
  }>(c)
  
  return c.json({
    apiKey: API_KEY,
    port: parseInt(PORT),
    debug: DEBUG === 'true',
  })
})

With Hono Generics

Define environment variables at the application level:
import { Hono } from 'hono'
import { env } from 'hono/adapter'

type Env = {
  Bindings: {
    DATABASE_URL: string
    API_KEY: string
    ENVIRONMENT: 'development' | 'production'
  }
}

const app = new Hono<Env>()

app.get('/env', (c) => {
  const { DATABASE_URL, API_KEY, ENVIRONMENT } = env(c)
  
  // All variables are properly typed
  return c.json({
    db: DATABASE_URL,
    key: API_KEY,
    env: ENVIRONMENT,
  })
})

Combined Approach

Combine both generic types:
import { env } from 'hono/adapter'

type Env = {
  Bindings: {
    DATABASE_URL: string
  }
}

const app = new Hono<Env>()

app.get('/config', (c) => {
  const { DATABASE_URL, EXTRA_VAR } = env<{
    DATABASE_URL: string
    EXTRA_VAR: string
  }>(c)
  
  return c.json({ DATABASE_URL, EXTRA_VAR })
})

Runtime-specific Behavior

The env function adapts to different runtimes automatically:

Node.js and Bun

Accesses process.env:
const { DATABASE_URL } = env(c)
// Reads from process.env.DATABASE_URL

Deno

Accesses Deno.env.toObject():
const { DATABASE_URL } = env(c)
// Reads from Deno.env.get('DATABASE_URL')

Cloudflare Workers

Accesses environment bindings from context:
const { DATABASE_URL } = env(c)
// Reads from c.env.DATABASE_URL (Cloudflare bindings)

Fastly Compute

Returns empty object (use ConfigStore instead):
const vars = env(c)
// Returns {} - use Fastly ConfigStore API for configuration

Edge Runtime

Accesses process.env:
const { DATABASE_URL } = env(c)
// Reads from process.env.DATABASE_URL

Use Cases

Runtime-specific Configuration

Adjust behavior based on runtime:
import { getRuntimeKey } from 'hono/adapter'

app.get('/info', (c) => {
  const runtime = getRuntimeKey()
  
  const config = {
    node: { features: ['filesystem', 'native-modules'] },
    deno: { features: ['typescript', 'web-standard'] },
    bun: { features: ['fast-startup', 'native-bundler'] },
    workerd: { features: ['edge-computing', 'global-network'] },
  }[runtime] || { features: [] }
  
  return c.json({ runtime, ...config })
})

Database Connection

Connect to database using environment variables:
import { env } from 'hono/adapter'

type Env = {
  Bindings: {
    DATABASE_URL: string
    DATABASE_POOL_SIZE: string
  }
}

const app = new Hono<Env>()

app.get('/db', async (c) => {
  const { DATABASE_URL, DATABASE_POOL_SIZE } = env(c)
  
  const db = await connectToDatabase({
    url: DATABASE_URL,
    poolSize: parseInt(DATABASE_POOL_SIZE || '10'),
  })
  
  const users = await db.query('SELECT * FROM users')
  return c.json(users)
})

API Keys and Secrets

Access API keys securely:
import { env } from 'hono/adapter'

app.get('/external-api', async (c) => {
  const { API_KEY } = env<{ API_KEY: string }>(c)
  
  const response = await fetch('https://api.example.com/data', {
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
    },
  })
  
  return c.json(await response.json())
})

Feature Flags

Implement feature flags with environment variables:
import { env } from 'hono/adapter'

app.get('/feature', (c) => {
  const { FEATURE_NEW_UI, FEATURE_BETA } = env<{
    FEATURE_NEW_UI?: string
    FEATURE_BETA?: string
  }>(c)
  
  const features = {
    newUI: FEATURE_NEW_UI === 'true',
    beta: FEATURE_BETA === 'true',
  }
  
  return c.json({ features })
})

Manual Runtime Override

Override automatic runtime detection when needed:
import { env } from 'hono/adapter'

app.get('/force-node', (c) => {
  // Force Node.js environment variable access
  const vars = env(c, 'node')
  return c.json(vars)
})

Known User Agents

The helper uses user agents for runtime detection:
const knownUserAgents = {
  deno: 'Deno',
  bun: 'Bun',
  workerd: 'Cloudflare-Workers',
  node: 'Node.js',
}

Detection Priority

Runtime detection follows this priority:
  1. Check navigator.userAgent against known user agents
  2. Check for EdgeRuntime global (Edge Runtime)
  3. Check for fastly global (Fastly Compute)
  4. Check process.release.name === 'node' (Node.js)
  5. Return 'other' if none match

Type Definitions

type Runtime = 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'other'

function getRuntimeKey(): Runtime

function env<
  T extends Record<string, unknown>,
  C extends Context = Context<{ Bindings: T }>
>(
  c: T extends Record<string, unknown> ? Context : C,
  runtime?: Runtime
): T & C['env']

Best Practices

Type Your Environment

Always provide TypeScript types for environment variables to catch errors early

Use Hono Bindings

Define environment types in Hono’s generic Bindings for better type inference

Validate Variables

Validate that required environment variables exist before using them

Avoid Runtime Checks

Rely on automatic detection rather than manual runtime checking when possible
For Cloudflare Workers, environment variables are accessed via bindings (c.env) rather than traditional environment variables. The env helper handles this automatically.
On Fastly Compute, standard environment variables are not available. Use the Fastly ConfigStore API for configuration data instead.

Error Handling

Handle missing environment variables gracefully:
import { env } from 'hono/adapter'

app.get('/secure', (c) => {
  const { SECRET_KEY } = env<{ SECRET_KEY?: string }>(c)
  
  if (!SECRET_KEY) {
    return c.json({ error: 'SECRET_KEY not configured' }, 500)
  }
  
  return c.json({ message: 'Secret key is configured' })
})

Build docs developers (and LLMs) love