edgeone-makers-recipes について
EdgeOne Makers 向けのアプリレシピとプロジェクトテンプレート集です。API 関数、Blob と KV による永続化、各言語の構成、開発プレビューの要件を扱います。
主な機能
- 01フルスタックプロジェクトのテンプレート
- 02Blob を使った永続化パターン
- 03KV を使う Edge Functions の例
- 04Node.js、Python、Go のディレクトリ構成
- 05開発プレビューとファイル命名のガイド
活用シーン
- 01API ルートを備えたサイトの構築
- 02動的サイトへの Blob 永続化の追加
- 03KV を使ったカウンターの作成
- 04言語に応じた関数ディレクトリ構成の選択
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tencentedgeone/ edgeone-makers-tools --skill edgeone-makers-recipesインストール後、Claude Code で/edgeone-makers-recipesを入力して読み込まれたか確認します。
同梱ファイルreferences/youth-site-scenarios.md
詳細
よく使うレシピ
⛔ プレビュー禁止:開発を終えたら、必ず
edgeone makers devで開発サーバーを起動し、present_filesを使ってhttp://127.0.0.1:8088/を開き、プレビューしてください。file://プロトコルで HTML ファイルを開かないでください(IDE が自動的に開いた場合も無視してください)。また、python -m http.serverやnpx serveなどのセルフホスト型サーバーは使用しないでください。Next.js プロジェクトでは、next.configにallowedDevOrigins: ["127.0.0.1"]も設定してください。プロジェクトで Blob/KV を使用する場合は、-n <project-name>を渡してください。つまりedgeone makers dev -n <project-name>です。この名前は自動プロビジョニングに必須です。引数なしのdevは、サンドボックスで対話型の選択画面が表示されたまま停止します。
⚠️
.env.exampleは必須ファイルです:AI Gateway を使用するすべてのプロジェクト(Agent プロジェクト、LLM を呼び出す Cloud Functions)は、プロジェクトのルートにAI_GATEWAY_API_KEY=とAI_GATEWAY_BASE_URL=を宣言した.env.exampleを必ず作成してください。CLI はデプロイ時にこのファイルに基づいて環境変数を自動注入します。ファイルがない場合、環境変数は注入されず、ランタイムでエラーが発生します。
📝
index.htmlは必ず最後に作成してください:index.htmlを作成すると、IDE のfile://プレビューがすぐに起動します。WorkBuddy ではこれを避けられません。このプレビューが壊れて見える時間を最小限にするため、まず依存ファイルをすべて作成してください。style.css、script.js、Cloud Functions(functions/内のファイル)、静的アセットなど、ページが読み込むものはすべて先に用意します。その後、index.htmlを最後に作成してください。file://プレビューはアセットがすべて揃った状態で開き、edgeone makers devに引き継がれるまでその状態を保ちます(上記のプレビュー禁止を参照)。また、各index.htmlは一度に書き上げてください。空のひな形を作成してから何度も編集して埋めるのは避けてください(保存のたびに再レンダリングされ、ちらつきます)。小さな単一ページのツールなら、CSS と JS を 1 つのindex.htmlにインラインで記述してください。
⛔ レシピに記載されたファイル名をそのまま使ってください。気づきにくい失敗につながる落とし穴が 2 つあります:Cloud Function を作成する前に、以下で該当するシナリオを確認し、正確なファイル名をそのまま使用してください。ファイル名を間違えても、通常は明確なエラーにならず、代わりに暗黙のフォールバックが発生します。
- すべての関数ファイルには言語の拡張子が必要です —
.js(Node)、.py(Python)、.go(Go)。拡張子のないファイル(例:api/upload-url、api/file)は関数として認識されません。プラットフォームは静的なindex.htmlのフォールバックを暗黙的に配信するため、/api/*が JSON ではなく HTML を「不可解にも」返します。api/upload-url.js、api/file.jsのように命名してください。[[default]].jsは、それが置かれたディレクトリ用のキャッチオールです(api/[[default]].js→/api/*)。また、どちらのエクスポート形式も使えます — フレームワークのインスタンス(Express/Koa のexport default app)でも、通常のonRequest/onRequestGet/… ハンドラーでもかまいません。edgeone makers devでローカル検証したところ、[[default]].js内にexport default appがなくても、単独のonRequestで/foo/anythingに200 application/jsonが問題なく返されました。ドキュメントの 「ビルダーがファイルを関数として認識するのは、export default appがある場合のみ」 という記述は、Express/Koa フレームワークのセクションにあります。これはビルダーがフレームワークのインスタンスを判別する方法を説明しているのであり、「キャッチオールにはexport default appが必要」という意味ではありません。⚠️ 注意:この記述はデプロイ時のビルダーについてのもので、上記の検証は、より寛容なローカル開発サーバーで行われました。そのため、キャッチオールとonRequestを組み合わせてデプロイする場合は、デプロイ後にルートをもう一度確認してください(「ローカルで動く」ことは「ビルド時に認識される」ことを意味しません)。キャッチオールが不要な場合は、ルートごとに具体的なファイルを 1 つ用意する方法が最も安全です(api/messages.js、api/artworks/[id]/like.js)。パラメーターには[id]フォルダー/ファイルを使い、追加の引数はクエリ文字列で渡します(/api/file?key=...)。
一般的な EdgeOne Makers アプリケーション向けのプロジェクト構成テンプレート。
フルスタックアプリ — Node.js(静的サイト+API)
my-app/
├── index.html # Frontend
├── style.css
├── script.js
├── cloud-functions/
│ └── api/
│ ├── users.js # GET/POST /api/users
│ └── users/[id].js # GET/PUT/DELETE /api/users/:id
└── package.json
フロントエンドから API を呼び出します。
const res = await fetch('/api/users');
const users = await res.json();
💾 データはどこに保存されますか? このプラットフォームにはデータベースがありません。上記の API のひな形は空のデータを返します。レコード、アップロード、投票、ユーザーごとの状態を実際に永続化するには、Blob をバックエンドとして使用してください。以下のレシピと、makers-storage → バックエンドとしての Blob を参照してください。
Blob による永続化を備えた動的サイト(ゲストブック/ギャラリー/投票/状態保存)
リレーショナルデータは不要でも、実際のバックエンドが必要なあらゆる生成サイトに適した基本構成です。フロントエンド → Cloud Function → Blob。データベースもコンソールでの設定も不要です。
my-app/
├── index.html # Frontend (form + list)
├── script.js
├── cloud-functions/
│ └── api/
│ └── messages.js # GET lists entries, POST appends one
├── package.json # depends on @edgeone/pages-blob
cloud-functions/api/messages.js — レコードごとに 1 ファイル(パターン 1):
import { getStore } from "@edgeone/pages-blob";
export async function onRequest({ request }) {
const store = getStore("guestbook");
if (request.method === "POST") {
const { name, text } = await request.json();
const id = `${Date.now()}-${Math.round(Math.random() * 1e6)}`;
await store.setJSON(`entries/${id}.json`, { id, name, text, ts: Date.now() });
return Response.json({ ok: true, id });
}
const { blobs } = await store.list({ prefix: "entries/" });
const items = await Promise.all(blobs.map((b) => store.get(b.key, { type: "json" })));
items.sort((a, b) => b.ts - a.ts);
return Response.json({ items });
}
index.html フロントエンドからは、他の API と同じように呼び出します。
await fetch('/api/messages', { // post
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, text }),
});
const { items } = await fetch('/api/messages').then((r) => r.json()); // list
他のデータ形式では、キーの方式を変更します。状態保存には users/<uid>.json、投票には counts/<option>.json(強い一貫性)、ファイルアップロードには uploads/<id>.jpg + items/<id>.json を使用します。パターンの詳細は、makers-storage → バックエンドとしての Blob を参照してください。
フルスタックアプリ — Go(Gin フレームワーク)
my-app/
├── index.html # Frontend
├── style.css
├── script.js
├── cloud-functions/
│ └── api.go # Gin app — all /api/* routes
├── go.mod
└── package.json
cloud-functions/api.go:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/users", listUsersHandler)
r.POST("/users", createUserHandler)
r.GET("/users/:id", getUserHandler)
r.Run(":9000")
}
フルスタックアプリ — Python(Flask)
my-app/
├── index.html # Frontend
├── style.css
├── script.js
├── cloud-functions/
│ └── api/
│ └── index.py # Flask app — all /api/* routes
├── cloud-functions/requirements.txt
└── package.json
cloud-functions/api/index.py:
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/users', methods=['GET'])
def get_users():
return jsonify({'users': []})
@app.route('/users', methods=['POST'])
def create_user():
data = request.get_json()
return jsonify({'message': 'Created', 'user': data}), 201
フルスタックアプリ — Python(FastAPI)
my-app/
├── index.html
├── cloud-functions/
│ └── api/
│ └── index.py # FastAPI app — all /api/* routes
├── cloud-functions/requirements.txt
└── package.json
cloud-functions/api/index.py:
from fastapi import FastAPI
app = FastAPI()
@app.get('/items')
async def list_items():
return {'items': []}
@app.get('/items/{item_id}')
async def get_item(item_id: int):
return {'item_id': item_id}
フルスタックアプリ — Go(ハンドラーモード)
my-app/
├── index.html
├── cloud-functions/
│ └── api/
│ ├── users/
│ │ ├── list.go # GET /api/users/list
│ │ └── [id].go # GET /api/users/:id
│ └── hello.go # GET /api/hello
├── go.mod
└── package.json
エッジ API + KV カウンター
⚠️ 前提条件:コンソールで KV Storage を有効にし、先に名前空間をバインドする必要があります。../makers-storage/references/kv.md を参照してください。
my-app/
├── index.html
├── edge-functions/
│ └── api/
│ └── visit.js # Edge function with KV
└── package.json
edge-functions/api/visit.js:
export async function onRequest() {
// ⚠️ my_kv is a global variable (name set when binding namespace in console)
let count = await my_kv.get('visits') || '0';
count = String(Number(count) + 1);
await my_kv.put('visits', count);
return new Response(JSON.stringify({ visits: count }), {
headers: { 'Content-Type': 'application/json' },
});
}
セットアップ手順:
- EdgeOne Makers コンソールにログインします
- 「KV Storage」に移動し、「Apply Now」をクリックします
- 名前空間を作成します(例:
my-kv-store) - プロジェクトにバインドし、変数名を
my_kvに設定します - デプロイするか、
edgeone makers devを実行してテストします
Express フルスタック
my-app/
├── index.html
├── cloud-functions/
│ └── api/
│ └── [[default]].js # Express app handles all /api/*
└── package.json
ミドルウェア+API の組み合わせ
my-app/
├── middleware.js # Auth guard for /api/*
├── cloud-functions/
│ └── api/
│ ├── public.js # No auth needed (matcher excludes it)
│ └── data.js # Protected by middleware
└── package.json
複数言語の Cloud Functions
同じ cloud-functions/ ディレクトリで、異なる言語を使用できます。
my-app/
├── index.html
├── cloud-functions/
│ ├── api/
│ │ ├── users.js # Node.js — /api/users
│ │ └── hello.py # Python — /api/hello
│ └── service.go # Go — /service
├── go.mod
├── cloud-functions/requirements.txt
└── package.json
注記: 各ファイルは、それぞれ独自のランタイムを持つ独立した関数としてビルドおよびデプロイされます。プラットフォームはファイル拡張子から言語を判別します。