Overview

Deploy Hono on Node.js with security headers, structured logging, and graceful shutdown. Best for traditional Node.js hosting.

Features

  • Security headers via secureHeaders()
  • Structured JSON logging for observability
  • Health, ready, and live endpoints for k8s
  • Graceful shutdown with configurable timeout
  • CORS with configurable origins
  • Request timing middleware

Installation

npx hanma add hono-basic --framework hono

Dependencies

hono, @hono/node-server

Code

import { Hono } from "hono";
import { logger } from "hono/logger";
import { requestId } from "hono/request-id";
import { cors } from "hono/cors";
import { secureHeaders } from "hono/secure-headers";
import { timing } from "hono/timing";
import { serve } from "@hono/node-server";
import type { Context } from "hono";

// Environment configuration
const config = {
  port: Number(process.env.PORT) || 3000,
  env: process.env.NODE_ENV || "development",
  corsOrigins: process.env.CORS_ORIGINS?.split(",") || ["*"],
  shutdownTimeout: Number(process.env.SHUTDOWN_TIMEOUT) || 10000,
};

// Application type with custom variables
type AppEnv = {
  Variables: {
    requestId: string;
    startTime: number;
  };
};

const app = new Hono<AppEnv>();

// Security and observability middleware
app.use("*", secureHeaders());
app.use("*", timing());
app.use("*", requestId());
app.use("*", logger());
app.use("*", cors({
  origin: config.corsOrigins,
  allowMethods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
  allowHeaders: ["Content-Type", "Authorization", "X-Request-ID"],
  exposeHeaders: ["X-Request-ID", "X-Response-Time"],
  maxAge: 86400,
  credentials: true,
}));

// Request timing middleware
app.use("*", async (c, next) => {
  c.set("startTime", Date.now());
  await next();
});

// Health check endpoint
app.get("/health", (c) => {
  const uptime = process.uptime();
  const memory = process.memoryUsage();
  
  return c.json({
    status: "healthy",
    timestamp: new Date().toISOString(),
    runtime: "node",
    version: process.version,
    uptime: {
      seconds: Math.floor(uptime),
      formatted: formatUptime(uptime),
    },
    memory: {
      heapUsed: formatBytes(memory.heapUsed),
      heapTotal: formatBytes(memory.heapTotal),
      rss: formatBytes(memory.rss),
    },
  });
});

// Readiness check for k8s/container orchestration
app.get("/ready", (c) => {
  // Add your readiness checks here (DB connection, cache, etc.)
  return c.json({ ready: true });
});

// Liveness check
app.get("/live", (c) => c.text("OK"));

// Example route
app.get("/", (c) => {
  return c.json({
    message: "Hello from Hono!",
    requestId: c.get("requestId"),
    environment: config.env,
  });
});

// 404 handler
app.notFound((c) => {
  return c.json(
    {
      success: false,
      error: {
        code: "NOT_FOUND",
        message: `Path ${c.req.method} ${c.req.path} not found`,
      },
      requestId: c.get("requestId"),
    },
    404
  );
});

// Global error handler
app.onError((err, c) => {
  const requestId = c.get("requestId") || "unknown";
  const status = "status" in err ? (err.status as number) : 500;
  
  // Structured error logging
  console.error(JSON.stringify({
    level: "error",
    message: err.message,
    stack: err.stack,
    requestId,
    path: c.req.path,
    method: c.req.method,
    timestamp: new Date().toISOString(),
  }));
  
  return c.json(
    {
      success: false,
      error: {
        code: status === 500 ? "INTERNAL_ERROR" : "REQUEST_ERROR",
        message: config.env === "production" 
          ? "An unexpected error occurred" 
          : err.message,
      },
      requestId,
    },
    status
  );
});

// Utility functions
function formatUptime(seconds: number): string {
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  const mins = Math.floor((seconds % 3600) / 60);
  return `${days}d ${hours}h ${mins}m`;
}

function formatBytes(bytes: number): string {
  const units = ["B", "KB", "MB", "GB"];
  let i = 0;
  while (bytes >= 1024 && i < units.length - 1) {
    bytes /= 1024;
    i++;
  }
  return `${bytes.toFixed(2)} ${units[i]}`;
}

// Server instance
let server: ReturnType<typeof serve> | null = null;
let isShuttingDown = false;

// Start server
export const startServer = () => {
  if (server) {
    console.log("Server already running");
    return;
  }

  server = serve({
    fetch: app.fetch,
    port: config.port,
  }, (info) => {
    console.log(`Hono running on http://localhost:${info.port}`, JSON.stringify({
      level: "info",
      message: "Server started",
      environment: config.env,
      timestamp: new Date().toISOString(),
    }));
  });
};

// Graceful shutdown
export const shutdown = (signal: string) => {
  if (isShuttingDown || !server) return;
  isShuttingDown = true;
  
  console.log(`${signal} received. Shutting down...`);
  
  server.close(() => {
    console.log("Server stopped", JSON.stringify({
      level: "info",
      message: "Server stopped",
      timestamp: new Date().toISOString(),
    }));
    process.exit(0);
  });
  
  // Force shutdown after timeout
  setTimeout(() => {
    console.error("Forced shutdown due to timeout", JSON.stringify({
      level: "error",
      message: "Forced shutdown due to timeout",
      timestamp: new Date().toISOString(),
    }));
    process.exit(1);
  }, config.shutdownTimeout).unref();
};

Usage

// Server starts automatically
// Health check: GET /health
// Readiness: GET /ready
// Liveness: GET /live

Dependencies

Frequently Asked Questions

How do I install Node.js Server?
Run the following command: npx hanma add hono-basic --framework hono
What dependencies are required?
This snippet requires: hono, @hono/node-server. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to src/index.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.