事前準備(hello worldまで)
環境
OS: macOS Monterey 12.3.1
node -v: v18.0.0.
yarn -v: 1.22.18
# ディレクトリ作成
mkdir cloudflare-works-counter
cd $_
# 初期化
% npx wrangler init -y
Need to install the following packages:
wrangler
Ok to proceed? (y) y
⛅️ wrangler 2.0.5
-------------------
Using npm as package manager.
✨ Created wrangler.toml
npm WARN deprecated rollup-plugin-inject@3.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.
added 57 packages, and audited 58 packages in 10s
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities
✨ Created package.json
added 2 packages, and audited 60 packages in 2s
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities
✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies
✨ Created src/index.ts
To start developing your Worker, run `npm start`
To publish your Worker to the Internet, run `npm run publish`
こんな感じで雛形ができました
% tree -L 1
.
├── node_modules
├── package-lock.json
├── package.json
├── src
│ └── index.ts
├── tsconfig.json
└── wrangler.toml
この時点で yarn start
すると動きますが、package.json
にscriptをひとつ追加します。
エミュレータのMiniflareで起動するコマンドです。
"scripts": {
"start": "wrangler dev",
+ "local": "wrangler dev --local",
"publish": "wrangler publish"
}
b でブラウザを開くと「Hello World!」が表示されます
/src/index.js
を編集してリロードすると表示内容が変わりますね
% yarn local
yarn run v1.22.18
$ wrangler dev --local
⛅️ wrangler 2.0.5
-------------------
⎔ Starting a local server...
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[mf:inf] Worker reloaded! (180B)
[mf:inf] Listening on localhost:8787
[mf:inf] - http://localhost:8787
[mf:inf] Updated Request cf object cache!
GET / 200 OK (7.21ms)
yarn start
でも動きます
(初回はCloudflareをブラウザで開いて認証する必要があったかも)
カウンターを作る
hono導入
まずはhonoを使って書き換えます
「Hono[炎]」はCloudflare Workersに特化した「Webアプリを作るためのフレームワーク」です
% npm add hono
added 1 package, and audited 61 packages in 2s
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello! Cloudflare Works!'))
app.fire()
表示される文言が変わっただけで、やってることは同じ
% curl -X GET "http://localhost:8787"
Hello! Cloudflare Works!
counterのエンドポイントを作る
ユーザーごとにカウントできるように動的なurlにしておきます
+ app.get('/counter/:username', (c) => {
+ const username = c.req.param('username')
+ const count = 0
+
+ return c.text(`${username}, count: ${count}`)
+ })
app.fire()
% curl -X GET "http://localhost:8787/counter/yamada"
yamada, count: 0
KVの追加
for production
% npx wrangler kv:namespace create COUNTER
⛅️ wrangler 2.0.5
-------------------
🌀 Creating namespace with title "cloudflare-works-counter-COUNTER"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "COUNTER", id = "feoifeojeoijfaeojfo" }
wrangler.toml
に設定を追加
compatibility_date = "2022-05-14"
+ kv_namespaces = [
+ { binding = "COUNTER", id = "feoifeojeoijfaeojfo" }
+ ]
for preview
% npx wrangler kv:namespace create COUNTER --preview
⛅️ wrangler 2.0.5
-------------------
🌀 Creating namespace with title "cloudflare-works-counter-COUNTER_preview"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "COUNTER", preview_id = "feuhvbiuehfiuaheoige" }
wrangler.toml
に設定を追加
compatibility_date = "2022-05-14"
kv_namespaces = [
- { binding = "COUNTER", id = "feoifeojeoijfaeojfo" }
+ { binding = "COUNTER", id = "feoifeojeoijfaeojfo", preview_id = "feuhvbiuehfiuaheoige" }
]
カウンターのコードの追加
+ declare const COUNTER: KVNamespace
+ const addCount = async (username: string) => {
+ const value = await COUNTER.get(username)
+ let count = value != null ? parseInt(value) : 0
+ count = count + 1
+ await COUNTER.put(username, `${count}`)
+ return count
+ }
- app.get('/counter/:username', (c) => {
+ app.get('/counter/:username', async (c) => {
const username = c.req.param('username')
+ const count = await addCount(username)
- return c.text(`${username}, count: 0`)
+ return c.text(`${username}, count: ${count}`)
})
app.fire()
KVから値を取得し、加算する関数addCount
を追加します
WorksはKVと統合されているので、COUNTER.get(key)
COUNTER.put(key, value)
と書くだけで取得と登録ができます
初回のためにnull→0の変換、登録済みの値は文字列で返ってくるので数値への変換を行い、加算したのちKVに保存しています
型エラーを解消するためにdeclare const COUNTER: KVNamespace
を書いていますが、なくても動きます
動作確認
% yarn local
略)
別のターミナルから
% curl -X GET "http://localhost:8787/counter/tanaka"
tanaka, count: 1
% curl -X GET "http://localhost:8787/counter/tanaka"
tanaka, count: 2
% curl -X GET "http://localhost:8787/counter/tanaka"
tanaka, count: 3
% curl -X GET "http://localhost:8787/counter/tanaka"
tanaka, count: 4
% curl -X GET "http://localhost:8787/counter/tanaka"
tanaka, count: 5
Production へ deploy
% npx wrangler publish
⛅️ wrangler 2.0.5
-------------------
Uploaded cloudflare-works-counter (0.79 sec)
Published cloudflare-works-counter (3.64 sec)
cloudflare-works-counter.tyakatyaka3390.workers.dev
4秒!速い!
% curl -X GET "https://cloudflare-works-counter.tyakatyaka3390.workers.dev/"
Hello! Cloudflare Works!
curl -X GET "https://cloudflare-works-counter.tyakatyaka3390.workers.dev/counter/tanaka"
tanaka, count: 1
prviewとproductionでKVが分かれているので、ちゃんとtanakaさんのカウンターが初期値からになってますね
Worksの上限に達するまでは置いておくので、試してみてください
おまけ
# KVの一覧
% npx wrangler kv:namespace list
[
{
"id": "",
"title": "cloudflare-works-counter-COUNTER",
"supports_url_encoding": true
},
{
"id": "",
"title": "cloudflare-works-counter-COUNTER_preview",
"supports_url_encoding": true
}
]
# 指定のKVに保存されたkeyの一覧
% npx wrangler kv:key list --binding=COUNTER
[
{
"name": "tanaka"
}
]
# 指定のKV, 指定keyの値を取得
% npx wrangler kv:key get tanaka --binding=COUNTER
2
/src/index.ts
import { Hono } from 'hono'
const app = new Hono()
declare const COUNTER: KVNamespace
const addCount = async (username: string) => {
const value = await COUNTER.get(username)
let count = value != null ? parseInt(value) : 0
count = count + 1
await COUNTER.put(username, `${count}`)
return count
}
app.get('/', (c) => c.text('Hello! Cloudflare Works!'))
app.get('/counter/:username', async (c) => {
const username = c.req.param('username')
const count = await addCount(username)
return c.text(`${username}, count: ${count}`)
})
app.fire()