この記事について
NFC名刺と、そこから飛ばすリンクページを無料構成で自作する手順をまとめます。
- リンクページ:TypeScript + Hono
- ホスティング:Cloudflare Workers(無料)
- CI/CD:GitHub Actionsでテスト、Cloudflareのビルドでデプロイ
- URLの固定化:転送専用の小さいWorkerを1つ挟んで、中身を作り替えてもNFC・QRの再発行が要らないようにする
- 名刺表面:ロゴ(チップ×回路基板)を白反転で中央配置
- 名刺裏面:QRコード(NFC非対応端末向けのバックアップ導線)
- NFCへの書き込み:NFC Toolsアプリ(スマホ)
自分で全部手を動かしながら進められるように、各コマンドの意味も込みで手順化しています。
全体構成
[名刺 表面] [名刺 裏面]
NFCチップ内蔵 QRコード
ロゴ(白反転) 名前/ハンドルネーム
↓ タップ ↓ 読み取り
└───────────┬───────────┘
↓
https://yukisnow.yukisnow-ni2.workers.dev(固定URL・転送専用Worker)
↓ 302リダイレクト
リンクページ(Hono / Cloudflare Workers)
- X
- Qiita
- VRChat
- メール
NFCタップでもQR読み取りでも、まず固定URLの転送用Workerに着地して、そこから実際のリンクページに転送される構成です。リンクページ側の実装や配置場所を後で作り替えても、NFC・QRに刻んだURLは一切変える必要がありません。
1. リンクページの実装(Claude Codeにやらせる)
TSやHTMLを自分で書く代わりに、Claude Codeに投げるプロンプトを用意しておきます。プロジェクトの雛形作成だけ自分の手で行い、中身の実装はプロンプト一発でClaude Codeに任せる流れです。
1-1. Node.jsのバージョン確認
Hono + Wranglerを動かすにはNode.js 18以上が必要です。まず確認します。
node -v
v18未満なら、nvmなどで更新してください。
nvm install 20
nvm use 20
1-2. プロジェクト作成
npm create hono@latest link-page
対話式のセットアップが始まります。
-
Which template do you want to use?→ cloudflare-workers(Cloudflare Workersにそのままデプロイできるテンプレート) -
Do you want to install project dependencies?→ yes -
Which package manager do you want to use?→ npm
完了したらプロジェクトフォルダへ移動します。
cd link-page
依存関係のインストールがスキップされていた場合のみ、以下を実行してください。
npm install
1-3. Claude Codeへ投げるプロンプト
プロジェクトフォルダ直下でclaudeコマンドを起動し、以下のプロンプトをそのまま渡します。今回はテストまで含めて実装してもらいます。
このHono + Cloudflare Workersプロジェクト(link-page)に、NFC名刺から飛ばすリンクページを実装してください。
## やりたいこと
1. `src/index.tsx`(hono/jsx使用)に、以下のリンク一覧を表示するシンプルな1ページを実装する
- X(プレースホルダーURL: https://x.com/yourname)
- Qiita(プレースホルダーURL: https://qiita.com/yourname)
- VRChat(プレースホルダーURL: https://vrchat.com/home/user/usr_xxxxxxxx)
- Email(mailto:you@example.com)
- リンクは `LinkItem` のようなinterfaceで型付けした配列で管理し、後から追加・変更しやすくする(別ファイル `src/links.ts` に切り出す)
2. デザインはダークテーマ(背景 #0f1117 系)、スマホでの閲覧を前提にした縦並びのシンプルなリストUI(インラインstyleで完結させる)
3. 各リンクは新しいタブで開く(`target="_blank" rel="noopener noreferrer"`)
4. QRコードを生成するライブラリ `qrcode` と型定義 `@types/qrcode`、TypeScriptスクリプト実行用の `tsx` を開発依存としてインストールし、`scripts/generate-qr.ts` を作成する
- Cloudflare Workersのランタイムでは`fs`が使えないため、これはNode.js側でローカル実行する「ビルド前スクリプト」として、アプリ本体(`src/`)とは分離すること
- 生成先は `public/qr/card-qr.png`(印刷用に幅1000px、余白あり)と `public/qr/card-qr.svg`(Web確認用)
- QRコードの中身のURLは `https://example.workers.dev` を仮の値として、スクリプト内の定数として分かりやすい場所に置く(後で固定URLに書き換える前提)
5. `package.json` の `scripts` に `generate-qr`(`tsx scripts/generate-qr.ts` を実行)を追加する
## テストについて
6. Vitestをテスト用に導入する(`vitest`、Cloudflare Workers環境をエミュレートする `@cloudflare/vitest-pool-workers` を利用)
7. 以下の観点でユニットテストを書く(`src/index.test.ts`)
- トップページ(`/`)が200を返すこと
- レスポンスのHTMLに、X・Qiita・VRChat・Emailの4つのリンクがすべて含まれていること
- 各リンクの`href`が`links.ts`で定義した値と一致していること
- Emailのリンクだけ`mailto:`で始まっていること
8. `package.json` の `scripts` に `test`(`vitest run`を実行)を追加する
9. 実装後、`npm run dev` で起動確認、`npm test` でテストが全て通ること、`npm run generate-qr` でQR生成まで一通り動くことを確認する
## 制約
- 外部UIライブラリは使わず、素のHono + hono/jsx + インラインstyleで完結させる
- コード量は最小限で、後から自分で読んでも理解できるシンプルな実装にする
Claude Codeが実装を終えたら、links.tsのURLを自分の実際のアカウントに書き換えてください。
1-4. 開発サーバーとテストの確認
npm run dev
表示されたURL(http://localhost:8787など、Wranglerのdevサーバーのポート)でリンクページが表示されればOKです。
続けてテストを実行します。
npm test
全部パスすることを確認してください。ここで落ちるようなら、links.tsの中身とテストの期待値がずれている可能性が高いので、Claude Codeに差分を伝えて直してもらいます。
2. Cloudflare Workersへのデプロイ
2-1. wrangler.jsoncの確認
npm create hono@latestでcloudflare-workersテンプレートを選んでいれば、プロジェクト直下にwrangler.jsoncが自動生成されています。中身がだいたい以下のようになっているか確認してください。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "link-page",
"main": "src/index.tsx",
"compatibility_date": "2026-08-22",
"compatibility_flags": [
"nodejs_compat"
],
"assets": {
"directory": "./public"
}
}
-
name:デプロイ先のCloudflare Workersプロジェクト名。これは後で絶対に変えないこと。名前を変えると別プロジェクト扱いになり、URLも変わってしまいます -
main:Workerのエントリーポイント。ビルド成果物を経由せず、TypeScriptのソースを直接指定します(wranglerが自前でバンドルします) -
assets.directory:ここに置いたファイル(QR画像やアバター画像など)は、Workerを経由せず直接配信されます
package.jsonのscriptsにdeployが無ければ追加します。
"deploy": "wrangler deploy"
2-2. GitHubリポジトリの作成とプッシュ
git init
git add .
git commit -m "Initial commit: link page"
git branch -M main
git remote add origin https://github.com/yourname/link-page.git
git push -u origin main
すでにnpm create hono@latestの対話でgit initされている場合、git initは不要なので省略してください。
-
git remote add origin ...:ローカルのリポジトリとGitHub上のリポジトリを紐付け -
git push -u origin main:-uでリモートのorigin mainを追跡ブランチとして設定(以降はgit pushだけでOKになる)
2-3. Cloudflareダッシュボードでの設定
- Cloudflareダッシュボードにログイン
- 左メニューの Compute → Workers を選択
- Create application → GitHubリポジトリをインポート(Import a repository的な導線)
- 対象のGitHubリポジトリ(
link-page)を選択 - ビルド設定を入力
-
Build command:
npm test(デプロイはwranglerが自前でバンドルするので、事前ビルドは不要) -
Deploy command:
npm run deploy
-
Build command:
- Deploy
Build commandをnpm testだけにしておくことで、テストが1つでも落ちたらそこで止まり、Deploy command(実際のデプロイ)まで進みません。テストがデプロイのゲート代わりになります。
これでビルドが走り、数分でデプロイが完了します。完了するとhttps://link-page.<あなたのサブドメイン>.workers.devのようなURLが発行されます。
2-4. GitHub ActionsでCI(テスト)を回す
Cloudflare側のBuild commandにもテストを仕込んでありますが、それとは別にGitHub Actionsで「push・PRのたびにテストだけ先に回す」CIを用意しておきます。PRを作った時点でテスト結果がGitHub上に見えるようになり、Cloudflareのビルドを待たずに壊れているかどうか分かるのが利点です。
.github/workflows/ci.yml を作成します。
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
-
on.push/on.pull_request:mainへのpushと、main向けのPRの両方をトリガーにする -
actions/checkout@v4:リポジトリの中身をActionsの実行環境にチェックアウトする -
actions/setup-node@v4:指定バージョンのNode.jsをセットアップ。cache: "npm"でnpm ciの依存関係キャッシュを効かせて実行時間を短縮する -
npm ci:package-lock.jsonに固定されたバージョンどおりに厳密インストール(npm installと違い、ロックファイルと矛盾があれば失敗する。CI向け) -
npm test:Vitestのテストを実行
コミットしてpushすれば、GitHubリポジトリの Actions タブでCIが走る様子を確認できます。
git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push
2-5. CI/CDの役割分担
この構成では、CIとCDを次のように分担させています。
-
CI(テスト):GitHub Actions。push・PRのたびに
npm testを実行し、結果をGitHub上に可視化する -
CD(デプロイ):Cloudflare側のGit連携。
mainへのpushをフックに自動でビルド・デプロイが走る。Build command自体にもnpm testを仕込んであるので、仮にGitHub Actions側のCIを見落としていても、テストが落ちていればDeploy commandまで進まない
つまりテストは二重にかかっていますが、GitHub Actionsは「早く気づくための可視化」、Cloudflare側のBuild commandは「デプロイを止める最後の砦」という役割の違いがあります。他ブランチやPRを作ると、そのブランチ専用のプレビューデプロイもCloudflare側で自動生成されます。
3. URLを固定化する(転送専用のWorkerを1つ挟む)
link-page本体は今後、実装をやり直したりプロジェクト名を変えたりする可能性があります。そのたびにNFCへの再書き込みやQRの刷り直しをしたくないので、NFC・QRには直接link-pageのURLを刻まず、間に転送専用の小さいWorkerを1つ挟みます。
3-1. 新しいプロジェクトを作成
link-pageとは別の、独立した小さいプロジェクトとして作ります。
cd C:\projects
mkdir card-link
cd card-link
npm init -y
npm install -D wrangler
3-2. wrangler.jsoncを作成
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "card-link",
"main": "src/index.ts",
"compatibility_date": "2026-08-22"
}
nameはここでも一度決めたら変えないルールにします。
3-3. src/index.tsを作成
// NFC名刺・QRコードの転送先。ここを書き換えて deploy するだけで、
// カード側は一切変更不要。
const DESTINATION_URL = "https://example.workers.dev";
export default {
async fetch(): Promise<Response> {
return Response.redirect(DESTINATION_URL, 302);
},
};
DESTINATION_URLには、2章でデプロイしたlink-page本体のURLを入れます。
3-4. デプロイ
このプロジェクトは中身がほぼ1行の定数だけで、変更頻度も低いので、GitHub連携までは組まず、手元から直接デプロイする運用にします。
npx wrangler login
npx wrangler deploy
成功するとhttps://card-link.<あなたのサブドメイン>.workers.devのようなURLが発行されます。これが今後NFC・QRに刻む最終的な固定URLになります。
記録として、コードはローカルでGit管理だけしておくと安心です(GitHubへのpushは必須ではありません)。
git init
git add .
git commit -m "Initial commit: redirect worker"
今後link-page側を作り替えたくなったら、DESTINATION_URLだけ書き換えてnpx wrangler deployし直せば、NFC・QRは無傷のままです。
3-5. QRコードのURLを固定URLに差し替え
link-page側のscripts/generate-qr.tsにあるSITE_URLを、3-4で発行された固定URL(card-linkの方)に書き換えて再生成します。
npm run generate-qr
git add public/qr
git commit -m "Update QR with fixed redirect URL"
git push
4. NFCカードへの書き込み
固定URL(card-linkの方)を、スマホのNFC Toolsアプリ(iOS/Android)でNFCカードに書き込みます。手順はアプリの案内に沿って進めればOKです。
1点だけ注意:書き込み後に使えるLock tag(読み取り専用化)は不可逆の操作なので、URLに間違いがないことを確認してから、一番最後にやってください。
5. 名刺デザインの入稿
5-1. 表面(ロゴ)
- チップ×回路基板をモチーフにしたロゴを中央配置
- 黒マット素材に白反転で載せる想定
- シンプルな1パターンのみで使い回せるようにしておくと、再入稿のたびにデザインを作り直す手間がない
5-2. 裏面(QR + 名前)
-
public/qr/card-qr.png(1000px幅)をそのまま配置 - QRコードの下か横に、名前またはハンドルネームを添える
- QRコードの周囲には十分な余白(クワイエットゾーン)を確保する。詰めすぎると読み取り精度が落ちる
5-3. 入稿時の注意
印刷会社ごとに入稿データの解像度・カラーモード(CMYK/RGBなど)・塗り足しの規定が異なります。発注先の入稿仕様ページを必ず確認してから、最終データを書き出してください。
まとめ
- リンクページはHonoで実装し、Cloudflare Workersにデプロイ(ドメイン込みで無料)
- QRコードはCloudflare Workersランタイムでは動かないため、Node.js側のビルド前スクリプトとして静止画生成に分離
- Vitestでルーティングとリンク内容をテストし、Cloudflare側のBuild commandに
npm testを組み込んでデプロイ前のゲートにした - GitHub ActionsでもCIを回し、push・PRのたびに早期にテスト結果を可視化
- NFC・QRには
link-page本体のURLを直接刻まず、転送専用の小さいWorkerを1つ挟むことでURLを固定化。本体の実装や配置を後で作り替えても、NFC・QRの再発行が不要 - NFCの書き込みはNFC Toolsアプリで行い、ロックは最後の工程として実施(不可逆のため)
- 表面はNFCタップ、裏面はQR読み取りの二段構えにすることで、NFC非対応端末にも対応できる
無料枠だけで、名刺からリンクページまで一通り完結する構成になっています。


