Open28

Bun & Hono🔥 & Cloudflare Workers🌥️

midorinotanukimidorinotanuki

Honoでアプリを新規作成

https://hono.dev/top

$ bunx create-hono hono_app

create-hono version 0.3.2
✔ Using target directory … hono_app
✔ Which template do you want to use? › cloudflare-workers
cloned honojs/starter#main to /Users/midori/workspace/hono_app
✔ Copied project files
.
├── README.md
├── package.json
├── src
│   └── index.ts
├── tsconfig.json
└── wrangler.toml
midorinotanukimidorinotanuki

node_modulesのインストール

$ bun install
bun install v1.0.26 (c75e768a)

Checked 76 installs across 102 packages (no changes) [4.42s]

bun.lockbが追加される。が、バイナリファイルなので参照できない。

なぜバイナリーなのか?
一言で言えば、パフォーマンスです。Bunのロックファイルは、保存とロードが驚くほど速く、一般的なロックファイルよりも多くのデータを保存する。

Bunのロックファイルを調べるには?
bun install -yを実行すると、より簡単に検査できるYarn互換のyarn.lock(v1)が生成されます。

ってことで

$ bun install -y
bun install v1.0.26 (c75e768a)

Checked 76 installs across 102 packages (no changes) [32.00ms]

yarn.lockが追加された。

├── package.json
├── src
│   └── index.ts
├── tsconfig.json
├── wrangler.toml
└── yarn.lock
midorinotanukimidorinotanuki

2進数、もしくはコンピュータが読み取ることのできるデータ形式のことをバイナリまたはバイナリ形式と呼ぶ。

midorinotanukimidorinotanuki

Bunのロックファイルはなぜ速いのか?
すべてのデータに線形配列を使用している。パッケージは自動インクリメントの整数IDかパッケージ名のハッシュで参照される。8文字以上の文字列は重複排除される。ディスクに保存する前に、パッケージツリーを歩き、依存関係順にパッケージをクローンすることで、ロックファイルはガベージコレクションされ、決定論的になります。

midorinotanukimidorinotanuki

RUN

$ bun run --hot src/index.ts

http://localhost:3000

midorinotanukimidorinotanuki
$ npm run dev
> dev
> wrangler dev src/index.ts

 ⛅️ wrangler 3.28.1
-------------------
⎔ Starting local server...
[wrangler:inf] Ready on http://localhost:8787
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit     │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯

お〜、npmの方が便利は便利。

midorinotanukimidorinotanuki

Cloudflare Workersにデプロイ

$ bun run deploy
 wrangler deploy --minify src/index.ts
 ⛅️ wrangler 3.28.1
-------------------
Total Upload: 20.15 KiB / gzip: 7.45 KiB
Uploaded hono_app (0.96 sec)
Published hono_app (3.99 sec)
  https://hono_app.mintgreen7programming.workers.dev
Current Deployment ID: 

?!?!?!?!?!めちゃ簡単だが....ビビる....

midorinotanukimidorinotanuki

Cloudflare Workersとは

概要

・世界中にデプロイされ、リクエストの送信元に最も近い地域のデータセンターにリクエストが届く。つまり、ユーザーに最も近い場所でサービスを提供するエッジコンピューティングという形態である

→情報の高速化、ネットワーク負荷の軽減、必要なデータのみをクラウドサーバへ送るためセキュリティも強化される

・サーバーレスである
→私たちがサーバを運用する必要がない

・似たサービスにAWS LambdaやGCP Cloud Runがあるが、Cloudflare Workersはコンテナ仮想化ではなくJavaScriptランタイムのV8を実質的な仮想化ソフトウェアとして利用している。
→コンテナの起動を待つ必要がなく、コールドスタートとホットスタートの概念もない。

料金

midorinotanukimidorinotanuki
app.get('/', (c) => {
  const posts = getPosts()
  return c.html(<Top posts={posts} />)
})

(c)ってなに

midorinotanukimidorinotanuki

GPT
app.get('/'):ルートパス('/')へのHTTP GETリクエストを処理するルートを設定します。

(c) => { ...}:これは、ルートのハンドラとして機能するアロー関数です。(c)はコンテキストまたはリクエスト/レスポンスオブジェクトを表すパラメータです。

const posts = getPosts():getPosts関数を使って投稿を取得しているように見えます。このスニペットには、この関数の詳細が記載されていません。

return c.html(<Top posts={posts} />):この行は、<Top>と呼ばれるコンポーネントまたはテンプレートによって生成されたHTMLコンテンツでクライアントに応答し、そのコンポーネントにpropとして投稿を渡しているようです。

midorinotanukimidorinotanuki

htmlヘルパー

htmlヘルパーを使うと、JavaScriptのテンプレート・リテラルにhtmlというタグでHTMLを書くことができる。raw()を使用すると、コンテンツはそのままレンダリングされます。これらの文字列は自分でエスケープする必要があります。

import { Hono } from 'hono'
import { html, raw } from 'hono/html'
const app = new Hono()

app.get('/:username', (c) => {
  const { username } = c.req.param()
  return c.html(
    html`<!DOCTYPE html>
      <h1>Hello! ${username}!</h1>`
  )
})
midorinotanukimidorinotanuki

HonoRequest

HonoRequestは、Requestオブジェクトをラップしたc.reqから取り出せるオブジェクトである。

midorinotanukimidorinotanuki

param()

パスパラメータの値を取得する。

// Captured params
app.get('/entry/:id', (c) => {
  const id = c.req.param('id')
  ...
})

// Get all params at once
app.get('/entry/:id/comment/:commentId', (c) => {
  const { id, commentId } = c.req.param()
})
midorinotanukimidorinotanuki

Context

RequestとResponseを処理するために、Contextオブジェクトを使うことができる。

midorinotanukimidorinotanuki

req

reqはHonoRequestのインスタンスである。

app.get('/hello', (c) => {
  const userAgent = c.req.header('User-Agent')
  ...
})
midorinotanukimidorinotanuki

body()

HTTP レスポンスを返します。

c.header()でヘッダを設定し、c.statusでHTTPステータスコードを設定します。
これは、c.text() や c.json() などでも設定できます。
⚠️注意:テキストまたはHTMLを返す場合は、c.text()またはc.html()を使用することを推奨する。

app.get('/welcome', (c) => {
  // Set headers
  c.header('X-Message', 'Hello!')
  c.header('Content-Type', 'text/plain')

  // Set HTTP status code
  c.status(201)

  // Return the response body
  return c.body('Thank you for coming')
})

次のようにも書ける

app.get('/welcome', (c) => {
  return c.body('Thank you for coming', 201, {
    'X-Message': 'Hello!',
    'Content-Type': 'text/plain',
  })
})

レスポンス結果

new Response('Thank you for coming', {
  status: 201,
  headers: {
    'X-Message': 'Hello',
    'Content-Type': 'text/plain',
  },
})
midorinotanukimidorinotanuki

text()

Content-Type:text/plainとしてテキストをレンダリングする。

app.get('/say', (c) => {
  return c.text('Hello!')
})
midorinotanukimidorinotanuki

json()

JSONをContent-Type:application/jsonとしてレンダリングする。

app.get('/api', (c) => {
  return c.json({ message: 'Hello!' })
})
midorinotanukimidorinotanuki

html()

HTMLをContent-Type:text/htmlとしてレンダリングする。

app.get('/', (c) => {
  return c.html('<h1>Hello! Hono!</h1>')
})
midorinotanukimidorinotanuki

notFound()

Not Found レスポンスを返す。

app.get('/notfound', (c) => {
  return c.notFound()
})
midorinotanukimidorinotanuki

//error
Component' は値を参照していますが、ここでは型として使用されています。'typeof Component' を意図していましたか?

→.tsxに変えるの忘れてた