std9.jp

nuxt3 の ServerAPI で Cloudflare Workers KV を使う方法

目次 (7)
  1. 依存関係
  2. Cloudflare の Workers と KV の設定
  3. nuxt3のビルド先設定とStorage設定
  4. アプリケーション部分
  5. /api/count API の GET メソッド
  6. /api/count API の PATCH メソッド
  7. nuxt3のビルドとデプロイ

nuxt3 の ServerAPI で Cloudflare Workers KV を扱うためのメモ。

# nuxt3プロジェクトをセットアップ
$ npx nuxi init example-cloudflare-kv
$ cd example-cloudflare-kv
$ npm i

# パッケージを追加
$ npm i -D @cloudflare/workers-types wrangler

# Cloudflareにログインする
$ npx wrangler login

依存関係

{
  < 省略 >

  "devDependencies": {
    "@cloudflare/workers-types": "^4.20231121.0",
    "@nuxt/devtools": "latest",
    "nuxt": "^3.8.2",
    "vue": "^3.3.10",
    "vue-router": "^4.2.5",
    "wrangler": "^3.19.0"
  }
}

Cloudflare の Workers と KV の設定

wrangler.toml
name = "example-cloudflare-kv"
compatibility_date = "2023-12-07"
account_id = "<Cloudflare アカウントID>"
type = "javascript"
main = "./.output/server/index.mjs"
workers_dev = true

[site]
bucket = ".output/public"

[vars]
CLOUDFLARE_ENV = "production"

[build]
command = ""
upload.format = "service-worker"

[[kv_namespaces]]
binding = "STORAGE"
id = "<名前空間ID>"
preview_id = "<名前空間 ID>"

nuxt3のビルド先設定とStorage設定

export default defineNuxtConfig({
  nitro: {
    preset: `cloudflare`,

    // storage: Production
    storage: {
      db: {
        driver: `cloudflare-kv-binding`,
        binding: `STORAGE`,
      },
    },

    // storage: Development
    devStorage: {
      db: {
        driver: `memory`,
      },
    },
  },
});

アプリケーション部分

<template>
  <p>{{ countRef.clicks }}</p>
  <button id="app-count-button" class="btn btn-border" @click="onClick">
    POST
  </button>
</template>

<script setup lang="ts">
import { Count } from "./server/api/count.patch";

const countRef = ref<Count>();

onMounted(async () => {
  // api: count取得
  const { data } = await useFetch<Count>("/api/count");

  countRef.value = {
    clicks: data.value?.clicks || 0,
  };
});

async function onClick() {
  // api: countのインクリメント
  const { data: count } = await useFetch<Count>("/api/count", {
    method: `PATCH`,
    body: { field: "CLICKS", operator: "INCREMENT" },
  });

  if (countRef.value && count.value) {
    countRef.value.clicks = count.value.clicks;
  }
}
</script>

/api/count API の GET メソッド

export default defineEventHandler(async (event) => {
  // storage: データを取得
  const count = await useStorage().getItem(`db:count`);

  // api: countを返す
  return count;
});

/api/count API の PATCH メソッド

export interface Count {
  clicks: number;
}

export interface CountUp {
  field: "CLICKS";
  operator: "INCREMENT";
}

export default defineEventHandler(async (event) => {
  // api: パラメータを取得
  const body: CountUp = JSON.parse((await readRawBody(event)) as string);

  // storage: データを取得
  const count: Count = (await useStorage().getItem(`db:count`)) ?? {
    clicks: 0,
  };

  // インクリメント指示の判定
  if (body.field === `CLICKS` && body.operator === `INCREMENT`) {
    // クリック数をカウントアップ
    count.clicks++;

    // storage: データを保存
    await useStorage().setItem(`db:count`, JSON.stringify(count));
  }

  // api: countを返す
  return count;
});

nuxt3のビルドとデプロイ

# nuxt3のビルド
$ nuxt build

# cloudflare worksにdeploy
$ wrangler publish