Skip to main content

Hono

  • honojs/hono
    • Fast, Lightweight, Web-standards
    • 支持环境
      • Cloudflare Workers
      • Cloudflare Pages
      • Fastly Compute
      • Deno
      • Bun
      • Lagon
      • Vercel
      • AWS Lambda
      • Lambda@Edge
    • used by:
npm add hono @hono/node-server @hono/zod-openapi @hono/trpc-server
npm add -D typescript @types/node vitest

# MISC
npm add dotenv @nestjs/common @wener/nestjs @wener/utils

# for Bun
npm add -D @types/bun

# for Dev
npm add -D ts-node esbuild @swc/cli

npm node --loader ts-node/esm --watch ./src/main.ts

# build
# =====================
# swc support decorate
pnpm swc ./src -d ./dist/out
# bundle all deps
pnpm esbuild ./dist/out/main.js --external:{http,fs,path,stream,crypto,os,node:\*} --define:process.env.NODE_ENV=\"production\" --bundle --format=esm --outdir=dist/app --minify-syntax --charset=utf8 --target=es2022,node20 --sourcemap=external --legal-comments=external

cat << EOF
FROM wener/node:20

WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs

COPY rootfs /

RUN mkdir -p /app && chown -R nodejs:nodejs /app
USER nodejs
# for Native Addon
#RUN --mount=type=cache,target=/home/nodejs/.npm,uid=1001,gid=1001 \
# npm i sharp pg mysql2 --platform=linuxmusl

COPY --chown=nodejs:nodejs dist/app/ /app/

CMD [ "node" , "--enable-source-maps", "main.mj" ]
EOF

echo '{"name": "server","type": "module"}' > dist/app/package.json
docker buildx build -t server --load --platform linux/amd64 .
import { serve } from '@hono/node-server';
import { Hono } from 'hono';

const app = new Hono();
app.get('/', (c) => c.text('Hono meets Node.js'));

// for NodeJS
serve(app, (info) => {
console.log(`Listening on http://localhost:${info.port}`); // Listening on http://localhost:3000
});
// for tRPC

import { trpcServer } from '@hono/trpc-server';
import { appRouter } from './router';

const app = new Hono();

app.use(
'/trpc/*',
trpcServer({
router: appRouter,
}),
);

Notes

  • App - Hono
  • Routing
    • /posts/:id/comment/:comment_id
    • /api/animal/:type? - 可选
    • /post/:date{[0-9]+}/:title{[a-z]+} - 正则
    • /posts/:filename{.+\\.png$} - 包含 slash
    • *
    • /wild/*/card
caution
  • /api/foo/* 这种方式无法获取 param('*'), 使用 /api/foo/:target{.+} 可以获取 param
const app = new Hono({
// 处理带 Host
getPath: (req) => req.url.replace(/^https?:\/(.+?)$/, '$1'),
});

app.get('/www1.example.com/hello', (c) => c.text('hello www1'));
app.get('/www2.example.com/hello', (c) => c.text('hello www2'));

html

  1. 配置 tsconfig
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}
  1. 使用 pragma directive
/** @jsx jsx */
/** @jsxFrag Fragment */
import { jsx, Fragment } from 'hono/jsx';

FAQ

Hijack

  • 如果已经处理,则返回 RESPONSE_ALREADY_SENT
import { serve } from '@hono/node-server';
import type { HttpBindings } from '@hono/node-server';
import { RESPONSE_ALREADY_SENT } from '@hono/node-server/utils/response';
import { Hono } from 'hono';

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

app.get('/', (c) => {
const { outgoing } = c.env;
outgoing.writeHead(200, { 'Content-Type': 'text/plain' });
outgoing.end('Hello World\n');

return RESPONSE_ALREADY_SENT;
});

OpenAPI Type Error

pnpm add @asteasolutions/zod-to-openapi openapi3-ts