Skip to main content
The Netlify adapter provides utilities for running Hono applications on Netlify Functions and Netlify Edge Functions.

Import

import { handle, getConnInfo } from 'hono/netlify'

Functions

handle()

Converts a Hono application into a Netlify function handler.
function handle(
  app: Hono
): (req: Request, context: any) => Response | Promise<Response>

Parameters

  • app - The Hono application instance

Returns

A request handler function that receives both the request and Netlify context.

Example

import { Hono } from 'hono'
import { handle } from 'hono/netlify'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello from Netlify!')
})

app.get('/api/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: 'John Doe' })
})

app.post('/api/form', async (c) => {
  const body = await c.req.parseBody()
  return c.json({ success: true, data: body })
})

export default handle(app)

getConnInfo()

Extracts connection information from the Netlify context.
function getConnInfo(c: Context): ConnInfo

Returns

interface ConnInfo {
  remote: {
    address?: string  // Client IP from Netlify context
  }
}

Example

import { Hono } from 'hono'
import { handle, getConnInfo } from 'hono/netlify'

const app = new Hono()

app.get('/', (c) => {
  const info = getConnInfo(c)
  return c.text(`Your IP: ${info.remote.address}`)
})

export default handle(app)

Accessing Netlify Context

The Netlify context is available through c.env.context and provides access to geolocation data and other Netlify-specific features.

Context Interface

interface NetlifyContext {
  ip?: string
  geo?: {
    city?: string
    country?: {
      code?: string  // e.g., "US"
      name?: string  // e.g., "United States"
    }
    subdivision?: {
      code?: string  // e.g., "CA"
      name?: string  // e.g., "California"
    }
    latitude?: number
    longitude?: number
    timezone?: string
    postalCode?: string
  }
  requestId?: string
}

Example with Geolocation

import { Hono } from 'hono'
import { handle, getConnInfo } from 'hono/netlify'

type Bindings = {
  context: {
    ip?: string
    geo?: {
      city?: string
      country?: { code?: string; name?: string }
      latitude?: number
      longitude?: number
    }
  }
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/api/location', (c) => {
  const info = getConnInfo(c)
  const { geo } = c.env.context
  
  return c.json({
    ip: info.remote.address,
    city: geo?.city,
    country: geo?.country?.name,
    countryCode: geo?.country?.code,
    coordinates: {
      lat: geo?.latitude,
      lon: geo?.longitude
    }
  })
})

app.get('/personalized', (c) => {
  const { geo } = c.env.context
  const country = geo?.country?.code || 'US'
  
  return c.json({
    message: `Content personalized for ${country}`,
    currency: country === 'US' ? 'USD' : 'EUR'
  })
})

export default handle(app)

Platform-Specific Notes

Netlify Functions vs Edge Functions

Netlify supports two types of functions:
  • Netlify Functions: Traditional serverless functions running on AWS Lambda
  • Netlify Edge Functions: Run on Deno at the edge, closer to users
The adapter works with both types.

Netlify Functions

Create functions in the netlify/functions/ directory:
project/
├── netlify/
│   └── functions/
│       ├── api.ts
│       └── hello.ts
└── netlify.toml

Netlify Edge Functions

Create edge functions in the netlify/edge-functions/ directory:
project/
├── netlify/
│   └── edge-functions/
│       └── api.ts
└── netlify.toml

File-Based Routing

// netlify/functions/api.ts
import { Hono } from 'hono'
import { handle } from 'hono/netlify'

const app = new Hono()

app.get('/api/*', (c) => {
  return c.json({ path: c.req.path })
})

export default handle(app)

Environment Variables

Access Netlify environment variables:
import { Hono } from 'hono'
import { handle } from 'hono/netlify'

const app = new Hono()

app.get('/api/config', (c) => {
  return c.json({
    context: Netlify.env.get('CONTEXT'), // 'production', 'deploy-preview', or 'branch-deploy'
    siteUrl: Netlify.env.get('URL'),
    deployUrl: Netlify.env.get('DEPLOY_URL'),
  })
})

export default handle(app)

Request Context

The Netlify context provides additional functionality:
import { Hono } from 'hono'
import { handle } from 'hono/netlify'

const app = new Hono()

app.get('/api/request-info', (c) => {
  const netlifyContext = c.env.context
  
  return c.json({
    requestId: netlifyContext.requestId,
    ip: netlifyContext.ip,
    geo: netlifyContext.geo
  })
})

export default handle(app)

Configuration

netlify.toml

[build]
  command = "npm run build"
  publish = "dist"
  functions = "netlify/functions"

[functions]
  directory = "netlify/functions"
  node_bundler = "esbuild"

[[edge_functions]]
  function = "api"
  path = "/api/*"

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/api/:splat"
  status = 200

Function Configuration

For Netlify Functions, you can add configuration:
// netlify/functions/api.ts
import { Hono } from 'hono'
import { handle } from 'hono/netlify'
import type { Config } from '@netlify/functions'

const app = new Hono()

app.get('/*', (c) => c.json({ message: 'Hello' }))

export default handle(app)

export const config: Config = {
  path: "/api/*",
  schedule: "@hourly" // For scheduled functions
}

Deployment

Using Netlify CLI

# Install Netlify CLI
npm install -g netlify-cli

# Login
netlify login

# Deploy
netlify deploy

# Deploy to production
netlify deploy --prod

Using Git Integration

  1. Connect your repository at app.netlify.com
  2. Configure build settings
  3. Deploy automatically on every push

Build Settings

  • Build command: npm run build
  • Publish directory: dist
  • Functions directory: netlify/functions

Complete Example

// netlify/functions/api.ts
import { Hono } from 'hono'
import { handle, getConnInfo } from 'hono/netlify'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'

type Bindings = {
  context: {
    ip?: string
    geo?: {
      city?: string
      country?: { code?: string; name?: string }
    }
    requestId?: string
  }
}

const app = new Hono<{ Bindings: Bindings }>()

app.use('*', cors())
app.use('*', logger())

app.get('/', (c) => {
  return c.json({ message: 'Hello from Netlify!' })
})

app.get('/api/location', (c) => {
  const info = getConnInfo(c)
  const { geo } = c.env.context
  
  return c.json({
    ip: info.remote.address,
    city: geo?.city,
    country: geo?.country?.name
  })
})

app.get('/api/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: `User ${id}` })
})

app.post('/api/echo', async (c) => {
  const body = await c.req.json()
  return c.json(body)
})

app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})

app.onError((err, c) => {
  console.error(err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

export default handle(app)

See Also

Build docs developers (and LLMs) love