HonoX
sh
yarn create hono
Directory Structure
.
├── app
│ ├── client.ts
│ ├── global.d.ts
│ ├── islands
│ │ └── counter.tsx
│ ├── routes
│ │ ├── _404.tsx
│ │ ├── _error.tsx
│ │ ├── index.tsx
│ │ └── _renderer.tsx
│ └── server.ts
├── package.json
├── public
│ └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
wrangler.toml
name
の値を修正して実行する。name = "-"
これでは、以下のようなエラーになります。
[vite] Internal server error: Processing wrangler.toml configuration:
- Expected "name" to be of type string, alphanumeric and lowercase with dashes only but got "-".
toml
name = "honox"
...
Start
sh
yarn dev
Accesse Browser
http://localhost:5173/
HonoX RPC
Directory Structure
.
├── app
│ ├── client.ts
│ ├── common
│ │ ├── types
│ │ │ └── index.ts
│ │ └── zod
│ │ └── index.ts
│ ├── global.d.ts
│ ├── islands
│ │ ├── index.tsx
│ │ └── Post
│ │ └── index.tsx
│ ├── routes
│ │ ├── _404.tsx
│ │ ├── api
│ │ │ └── index.ts
│ │ ├── _error.tsx
│ │ ├── index.tsx
│ │ └── _renderer.tsx
│ └── server.ts
├── package.json
├── public
│ └── favicon.ico
├── tsconfig.json
├── vite.config.ts
└── wrangler.toml
Hono API
ts
import { Hono } from 'hono'
import { validator } from 'hono/validator'
import { reqSchema } from '../../common/zod'
import { ReqType } from '../../common/types'
const app = new Hono()
.get('/', async (c) => {
return c.json({ message: 'HonoX🔥' })
})
.post(
'/posts',
validator('json', (value: ReqType, c) => {
const parsed = reqSchema.safeParse(value)
if (!parsed.success) {
return c.json({ message: 'Bad Request', error: '1文字以上140文字以内で入力して下さい。' }, 400)
}
return parsed.data
}),
(c) => {
const res = c.req.valid('json')
return c.json(
{
message: 'Created',
post: res.post,
},
201,
)
},
)
export type AddType = typeof app
export default app
FrontEnd
tsx
import { createRoute } from 'honox/factory'
import Client from '../islands'
import Post from '../islands/Post'
export default createRoute((c) => {
return c.render(
<>
<h1>HonoX</h1>
<Client />
<Post />
</>,
)
})
Get
tsx
import { hc } from 'hono/client'
import { useState } from 'hono/jsx'
import { AddType } from '../routes/api'
const client = hc<AddType>('/api')
const Client = () => {
const [message, setMessage] = useState('')
const onSubmit = async () => {
const res = await client.index.$get()
const data = await res.json()
setMessage(data.message)
}
return (
<>
<h1>Get</h1>
<button onClick={onSubmit}>Get Message</button>
<h1>{message}</h1>
</>
)
}
export default Client
Post
tsx
import { useState } from 'hono/jsx'
import { hc } from 'hono/client'
import { AddType } from '../../routes/api'
import { reqSchema } from '../../common/zod'
import { ReqType } from '../../common/types'
const client = hc<AddType>('/api/')
const Post = () => {
const [value, setValue] = useState('')
const [error, setError] = useState<string | null>(null)
const [posts, setPosts] = useState<string[]>([])
const handleChange = (e: Event) => setValue(e.target instanceof HTMLInputElement ? e.target.value : value)
const valueValidate = (value: string): string | null => {
const parseValue: ReqType = { post: value }
const result = reqSchema.safeParse(parseValue)
if (!result.success) {
return '1文字以上140文字以内で入力して下さい。'
}
return null
}
const handleSubmit = async (e: Event) => {
e.preventDefault()
const valueValidateError = valueValidate(value)
if (valueValidateError) {
setError(valueValidateError)
return
}
try {
setError(null)
const res = await client.posts
.$post({
json: { post: value },
})
.then((res) => res.json())
const resMessage = res.post
setPosts([...posts, resMessage])
setValue('')
} catch {
setError('投稿に失敗しました')
}
}
return (
<>
<h1>Post</h1>
<form onSubmit={handleSubmit}>
<input type='text' value={value} onChange={handleChange} />
<button type='submit'>Submit</button>
</form>
{error && <h1>{error}</h1>}
{posts.map((post, index) => (
<p key={index}>{post}</p>
))}
</>
)
}
export default Post