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 :
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:
Check navigator.userAgent against known user agents
Check for EdgeRuntime global (Edge Runtime)
Check for fastly global (Fastly Compute)
Check process.release.name === 'node' (Node.js)
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' })
})