はじめに
シゴトでdevcontainerを使ってNextJSを動かすととても重くて、最近はフロントをhostで開発している。そもそもdevcontainerで快適なぐらいのないの?ってのでたどり着いたフレームワーク。FastAPIのようなノリで使えそう。まぁでもこの記事ではdockerは使わない。あくまでhonoの記事。
このIssueで使うかー
プロジェクトの作成
日本製!
console
cd C:\Users\yoshi\OneDrive\dev
npm create hono@latest
Need to install the following packages:
create-hono@0.14.3
Ok to proceed? (y) y
create-hono version 0.14.3
? Target directory hono_playground
? Which template do you want to use? nodejs
? Do you want to install project dependencies? yes
? Which package manager do you want to use? npm
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd hono_playground
npm notice
npm notice New major version of npm available! 9.6.7 -> 11.0.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0
npm notice Run npm install -g npm@11.0.0 to update!
npm notice
code .\hono_playground\
npm run dev
tsconfig
- なんだかhonoの標準のgenerateが古いらしい... importの仕方とかが慣れないかたちになるのでtsconfigをいじる
{
"compilerOptions": {
"target": "ESNext",
- "module": "NodeNext",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
"strict": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"types": [
"node"
],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
}
}
テキストフォームを使ってPOST送信
- json でPOSTする
-
c.req.json()
で受ける -
json
で返す -
result
を受けてHTMLを書き換える
ついでにこれのエッセンスもいれちゃおう
HTMLのinputの配列は添字に key が指定できる
{
"use[1]": "on",
"name_short[1]": "資格A",
"name_long[1]": "資格A 長い名前",
"address[1]": "住所A",
"use[2]": "on",
"name_short[2]": "資格B",
"name_long[2]": "資格B 長い名前",
"address[2]": "住所B"
}
src\index.ts
import { serve } from "@hono/node-server";
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => {
return c.html(`
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1>フォーム送信デモ</h1>
<form id="demoForm" class="mt-4">
<div class="mb-3">
<label for="name" class="form-label">名前</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="message" class="form-label">メッセージ</label>
<textarea class="form-control" id="message" name="message" rows="3" required></textarea>
</div>
<h3 class="mt-4">資格リスト</h3>
<table class="table">
<thead>
<tr>
<th>使う</th>
<th>資格名</th>
<th>資格名(長い名前)</th>
<th>住所</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="use[1]" checked></td>
<td><input type="text" name="name_short[1]" value="資格A" class="form-control"></td>
<td><input type="text" name="name_long[1]" value="資格A 長い名前" class="form-control"></td>
<td><input type="text" name="address[1]" value="住所A" class="form-control"></td>
</tr>
<tr>
<td><input type="checkbox" name="use[2]"></td>
<td><input type="text" name="name_short[2]" value="資格B" class="form-control"></td>
<td><input type="text" name="name_long[2]" value="資格B 長い名前" class="form-control"></td>
<td><input type="text" name="address[2]" value="住所B" class="form-control"></td>
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-primary">送信</button>
</form>
<div id="responseMessage" class="mt-3"></div>
</div>
<script>
const form = document.getElementById('demoForm')
const responseMessage = document.getElementById('responseMessage')
form.addEventListener('submit', async (event) => {
event.preventDefault()
const formData = new FormData(form)
const data = Object.fromEntries(formData)
const response = await fetch('/submit', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
})
const result = await response.json()
responseMessage.innerHTML = \`<div class="alert alert-success">サーバーからの応答: \${result.message}<br>\${JSON.stringify(result.certifications, null, 2)}</div>\`
})
</script>
</body>
</html>
`);
});
app.post("/submit", async (c) => {
const body = await c.req.json();
console.log(body);
const { name, message } = body;
// 配列データを再構築
const certifications: Array<{
id: string;
name_short: string;
name_long: string;
address: string;
used: boolean;
}> = [];
for (const key in body) {
if (key.startsWith("use[")) {
const id = key.match(/\[(\d+)\]/)?.[1]; // `use[1]` -> `1`
if (id) {
certifications.push({
id,
name_short: body[`name_short[${id}]`] || "", // 対応する name_short を取得
name_long: body[`name_long[${id}]`] || "", // 対応する name_long を取得
address: body[`address[${id}]`] || "", // 対応する address を取得
used: body[key] === "on", // チェックボックスが選択されている場合
});
}
}
}
return c.json({
message: `こんにちは、${name}!メッセージ「${message}」を受け取りました。`,
certifications,
});
});
const port = 3000;
console.log(`Server is running on http://localhost:${port}`);
serve({
fetch: app.fetch,
port,
});