Hono용 CLI 어댑터 구축
Source: Dev.to
Overview
hono-cli-adapter를 사용하면 Hono 앱을 CLI에서 직접 호출할 수 있습니다.
비즈니스 로직은 Hono에 그대로 두어 Postman이나 Insomnia로 디버깅하고, 동일한 코드를 CLI로도 배포하며, stdout에 직접 쓰는 것을 피할 수 있습니다—CLI가 모든 출력을 제어합니다.
보너스: 엔트리포인트만 교체하면 간단한 MCP 서버 지원도 가능합니다.
CLI 도구를 디버깅하는 것은 번거롭습니다: 실행 → 인자 수정 → 다시 실행—요청 히스토리가 없고, 쉽게 검토할 수 없습니다.
hono-cli-adapter를 사용하면 CLI 로직이 HTTP 엔드포인트 뒤에 숨어 있지만 실제 HTTP 서버가 필요하지 않습니다. 라이브러리가 CLI 인자를 HTTP 요청으로 변환하고 app.fetch()를 직접 호출합니다.
Why Hono?
- Web‑tool friendly – 개발 중에 Postman/Insomnia를 사용할 수 있습니다. 요청을 저장하고, 응답을 검사하며, 빠르게 반복합니다.
- MCP ready – 엔트리포인트만 교체하면 로컬 및 원격 MCP 서버를 모두 지원합니다. 동일한 로직, 다른 전송 방식.
- Testable – Hono 앱이 진리의 원천이며 독립적으로 테스트할 수 있습니다.
Installation
npm install hono-cli-adapter
Basic Usage
CLI 스크립트를 생성합니다 (예: my-cli.js):
#!/usr/bin/env node
import { cli } from 'hono-cli-adapter';
import { app } from './app.js';
await cli(app);
실행 가능하도록 만듭니다:
chmod +x my-cli.js
Running commands
# Call /hello/:name route
node my-cli.js hello Taro
# → "Hello, Taro!"
# List available routes
node my-cli.js --list
# Show help
node my-cli.js --help
CLI 인자는 URL 경로와 쿼리 파라미터가 된 뒤 app.fetch()에 전달됩니다.
Design Constraints
1. Thin CLI, fat Hono
모든 비즈니스 로직은 Hono에 존재하고, CLI는 플래그와 출력만 처리합니다. 이렇게 하면 CLI와 HTTP 간 동작이 일관되고, Hono 앱을 독립적으로 테스트할 수 있습니다.
2. No side effects
라이브러리는 stdout에 절대 쓰지 않습니다. 출력 포맷은 사용자가 직접 결정합니다:
const { code, lines } = await runCli(app, process);
for (const l of lines) console.log(l); // 또는 JSON.stringify, 파이프 등
process.exit(code);
3. POST‑only
CLI 명령은 동작을 트리거하므로 기본적으로 POST를 사용합니다. 필요에 따라 GET 지원을 나중에 추가할 수 있습니다.
Environment Variable Precedence
세 단계, 마지막 것이 우선합니다:
process.env(기본)options.env(어댑터 설정)--env플래그 (최고 우선순위)
await cli(app, process, { env: { API_URL: 'https://dev.example.com' } });
node my-cli.js do-thing --env API_KEY=secret-123
Passing JSON Body
-- 뒤에 오는 토큰은 JSON 본문이 됩니다:
node my-cli.js create-user -- name=Taro email=taro@example.com
# Sends: { "name": "Taro", "email": "taro@example.com" }
Transforming Requests per Command
await adaptAndFetch(app, process.argv.slice(2), {
beforeFetch: {
upload: async (req, argv) => {
if (argv.file) {
const buf = await fs.readFile(argv.file);
const headers = new Headers(req.headers);
headers.set('content-type', 'application/octet-stream');
return new Request(req, { body: buf, headers });
}
}
}
});
Enriching --help with OpenAPI
await runCli(app, process, { openapi: myOpenApiSpec });
생성된 도움말에는 파라미터 타입, 필수/선택 플래그, 설명이 표시됩니다. hono-openapi와 잘 어울립니다.
Implementation Notes
listPostRoutes는 Hono 내부 라우터 구조를 검사합니다. 주요 Hono 업데이트 시 깨질 수 있으니, 프로덕션에서는 자체 라우트 목록을 유지하는 것을 고려하세요.- ESM only – CommonJS 지원이 없습니다. Node 18 이상이 필요합니다.
Conclusion
Hono + CLI는 더 많은 주목을 받아야 할 패턴입니다. 개발 중에 웹 도구를 활용하고, 손쉬운 MCP 지원을 받으며, 로직을 중복하지 않고 테스트 가능한 코어를 얻을 수 있습니다.
Check it out on GitHub: