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
hono@hono/node-server
Frequently Asked Questions
How do I install Node.js Server?
Run the following command:
npx hanma add hono-basic --framework honoWhat 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.