Upgrade to Pro — share decks privately, control downloads, hide ads and more …

「Hono遍歴」と「HonoXでブログ作成」

 「Hono遍歴」と「HonoXでブログ作成」

Workers Tech Talks in Osaka #1

Yasuhiro Sugawara

February 24, 2024
Tweet

Other Decks in Programming

Transcript

  1. 5

  2. サクッと作りやすい APIが直感的でわかりやすい import { Hono } from 'hono' const app

    = new Hono() app.get('/', (c) => c.text('Hono!')) export default app デプロイに1分もかかってない 6
  3. 9

  4. 10

  5. HonoXプロジェクトを作成 必要なパッケージをインストール $ npm install hono honox HonoXプロジェクトを作成 $ npm

    create hono@latest create-hono version 0.5.0 Target directory … blog Which template do you want to use? › x-basic cloned honojs/starter#main to /Users/xxx/ Do you want to install project dependencies? … yes Which package manager do you want to use? › yarn Installed project dependencies Copied project files 21
  6. D1データベースを作成 D1データベースを作成 $ npx wrangler d1 create blog [[d1_databases]] binding

    = "DB" # i.e. available in your Worker on env.DB database_name = "blog" database_id = "xxxxxxxxxxxxxxx" wrangler.tomlに、作成したデータベースの情報を追記 D1データベースに、記事のデータを記録するテーブルを作成 $ npx wrangler d1 execute blog --file=./createArticleTable.sql 23
  7. 記事作成リクエストを受け付ける app/routes/articles/create.tsx export const POST = createRoute( zValidator('form', schema, (result,

    c) => {...}), async (c) => { const { title, content } = c.req.valid('form') await createArticle(c.env.DB, { title, content }) return c.redirect('/', 303) } ) POST /articles/create を受け付ける 26
  8. 記事をプレビュー export default function ContentForm({ initialValue = '' }: Props)

    { const [value, setValue] = useState(initialValue) const [preview, setPreview] = useState(false) const handleChange = (e: Event) => { const target = e.target as HTMLTextAreaElement setValue(target.value) } return ( <> <div> <label> プレビュー <input type="checkbox" id="preview" checked={preview} onChange={() => setPreview((prev) => !prev)} /> </label> </div> {preview ? ( <> <div dangerouslySetInnerHTML={{ __html: parseMarkdown(value) }} /> <input type="hidden" name="content" value={value} /> </> ) : ( <textarea id="content" name="content" onChange={handleChange} > {value} </textarea> )} </> ) } 28
  9. 画像アップロードREST APIを作成 app/routes/images/create.tsx export const POST = createRoute( async (c)

    => { const { file } = c.req.valid('form') const form = new FormData() const client = CloudflareImageClient.new(c.env.CLOUDFLARE_API_TOKEN, c.env.CLOUDFLARE_ACCOUNT_ID) const res = await client.upload(file) return c.json(res) } ) POST /images/create 30
  10. アップロード後、テキストエリアにアップロードした画像を追加 export default function ContentForm() { const [file, setFile] =

    useState<File | null>(null) const handleFileInputChange = (e: Event) => { const fileList = e.target.files as FileList setFile(fileList[0]) } const handleImageUpload = async (e: Event) => { e.preventDefault() const formData = new FormData() formData.append('file', file) const res = await fetch('/images/create', { method: 'POST', body: formData }) const json = await response.json() setValue(`${value}\n![](${json['result']['variants'][0]})`) } return ( <> ... <input type="file" id="file" name="file" accept="image/*" onChange={handleFileInputChange} /> <button type="submit" onClick={handleImageUpload}> 画像添付</button> ... <textarea id="content" name="content">{value}</textarea> </> ) } 31