← Dynamic Workers / dynamic-workers / usage
Statické prostředky
Dynamic Workers mohou vedle kódu vašeho Workeru obsluhovat i statická aktiva, jako jsou HTML stránky, balíčky JavaScriptu, obrázky a další soubory. To se hodí ve chvíli, kdy potřebujete, aby Dynamic Worker obsloužil celou fullstack aplikaci.
Statická aktiva pro Dynamic Workers fungují jinak než statická aktiva v běžných Workers. Assety tak nenahráváte při nasazení, ale poskytujete je za běhu prostřednictvím Worker Loaderu get() callbacku, kde je načtete z R2, KV nebo jiného úložiště.
Jak to funguje
Nastavení statických aktiv pro Dynamic Workers se skládá ze tří částí:
- Uložení aktiv: Nahrajte statické soubory do KV namespace, klíčované podle ID projektu a cesty k souboru.
- Definování bindingu pro aktiva v loader Workeru: Vytvořte třídu, která obsluhuje požadavky na statické soubory tak, že je čte z KV a vrací se správnými hlavičkami.
- Předání bindingu Dynamic Workeru: Dynamic Worker jej používá k obsluze statických souborů zavoláním
env.ASSETS.fetch(request).
Uložení statických aktiv
Statická aktiva jsou uložena v KV namespace, odděleně podle ID projektu, takže soubory každého projektu jsou od sebe izolované:
project/{projectId}/assets/index.html → file content
project/{projectId}/assets/app.js → file content
project/{projectId}/manifest → asset manifestKdyž uživatel nasadí svůj projekt přes nahrávací API vaší platformy, uložte každý soubor do KV pod jeho cestou:
await env.KV_ASSETS.put(`project/${projectId}/assets${pathname}`, fileContent);Dále musíte uložit manifest, tedy mapování, které handleru pro aktiva říká, jaké soubory existují a jaké mají typy obsahu. Použijte buildAssetManifest() z @cloudflare/worker-bundler k jeho vygenerování z vašich assetů:
import { buildAssetManifest } from "@cloudflare/worker-bundler";
const assets = {
"/index.html": htmlContent,
"/app.js": jsContent,
"/style.css": cssContent,
};
const manifest = await buildAssetManifest(assets);
await env.KV_ASSETS.put(
`project/${projectId}/manifest`,
JSON.stringify(manifest),
);import { buildAssetManifest } from "@cloudflare/worker-bundler";
const assets = {
"/index.html": htmlContent,
"/app.js": jsContent,
"/style.css": cssContent,
};
const manifest = await buildAssetManifest(assets);
await env.KV_ASSETS.put(
`project/${projectId}/manifest`,
JSON.stringify(manifest),
);Přidání bindingů do loader Workeru
Udělení přístupu načítacímu Workeru ke KV namespace, kam jste aktiva uložili:
{
"worker_loaders": [{ "binding": "LOADER" }],
"kv_namespaces": [
{
"binding": "KV_ASSETS",
"id": "<your-kv-namespace-id>",
},
],
}[[worker_loaders]]
binding = "LOADER"
[[kv_namespaces]]
binding = "KV_ASSETS"
id = "<your-kv-namespace-id>"Definování bindingu pro aktiva
V loader Workeru vytvořte třídu, která rozšiřuje WorkerEntrypoint a definujte fetch() metodu. WorkerEntrypoint umožňuje volat tuto metodu z Dynamic Workeru pomocí RPC. Když Dynamic Worker zavolá env.ASSETS.fetch(request), spustí se tato metoda v loader Workeru, kde běží binding KV a vaše logika pro obsluhu assetů.
Třída přijímá projectId prop, aby věděl, assety kterého projektu má vyhledat. Když fetch() se zavolá, provede toto:
- Načte manifest aktiv projektu z KV.
- Přeloží pathname požadavku na soubor.
- Načte obsah souboru z KV.
- Vrátí
Responsese správnýmContent-Typehlavička.
Použijte @cloudflare/worker-bundler k obsluze statických assetů
Místo psaní vlastní logiky pro párování cest požadavků se soubory, rozpoznávání typů obsahu a nastavování cache hlaviček použijte @cloudflare/worker-bundler balíček pro obsluhu statických assetů. Ve svém fetch() metodu předejte handleAssetRequest() dvě věci:
- A manifest, mapování cesty na typ obsahu, které jste při nahrávání uložili do KV, vytvořené pomocí
buildAssetManifest(). Tím se řeknehandleAssetRequest()které soubory existují a jaké mají typy obsahu. - A objekt úložiště, řekne
handleAssetRequest()jak číst soubory z vašeho jmenného prostoru KV. Má jednu metodu,get(pathname), která přečte a vrátí obsah pro danou cestu k souboru.
handleAssetRequest() obslouží soubor, pokud v manifestu najde shodu, a to se správnými hlavičkami pro typ obsahu a cachování.
import { WorkerEntrypoint } from "cloudflare:workers";
import { handleAssetRequest } from "@cloudflare/worker-bundler";
export class AssetBinding extends WorkerEntrypoint {
async fetch(request) {
const { projectId } = this.ctx.props;
// Load the project's asset manifest from KV
const manifest = await this.env.KV_ASSETS.get(
`project/${projectId}/manifest`,
{ type: "json", cacheTtl: 300 },
);
if (!manifest) {
return new Response("No assets found", { status: 404 });
}
// Storage object — handleAssetRequest calls get() to
// read file content when it needs to serve an asset
const storage = {
async get(pathname) {
return this.env.KV_ASSETS.get(
`project/${projectId}/assets${pathname}`,
{ type: "arrayBuffer", cacheTtl: 86_400 },
);
},
};
const response = await handleAssetRequest(request, manifest, storage);
return response ?? new Response("Not Found", { status: 404 });
}
}import { WorkerEntrypoint } from "cloudflare:workers";
import { handleAssetRequest } from "@cloudflare/worker-bundler";
export class AssetBinding extends WorkerEntrypoint {
async fetch(request: Request) {
const { projectId } = this.ctx.props;
// Load the project's asset manifest from KV
const manifest = await this.env.KV_ASSETS.get(
`project/${projectId}/manifest`,
{ type: "json", cacheTtl: 300 },
);
if (!manifest) {
return new Response("No assets found", { status: 404 });
}
// Storage object — handleAssetRequest calls get() to
// read file content when it needs to serve an asset
const storage = {
async get(pathname: string) {
return this.env.KV_ASSETS.get(
`project/${projectId}/assets${pathname}`,
{ type: "arrayBuffer", cacheTtl: 86_400 },
);
},
};
const response = await handleAssetRequest(request, manifest, storage);
return response ?? new Response("Not Found", { status: 404 });
}
}Jakmile AssetBinding se exportuje, je pak dostupné na ctx.exports v loader Workeru fetch() handler. ctx je třetí parametr handleru, za request a env. Takto jej v dalším kroku předáte Dynamic Workeru.
Předání asset bindingu Dynamic Workeru
Když zavoláte get() k vytvoření Dynamic Workeru zahrňte AssetBinding v env objekt, aby ho Dynamic Worker mohl použít k obsluze statických souborů. Pro odkaz na AssetBinding třídu, kterou jste definovali v předchozím kroku, použijte ctx.exports.AssetBinding() a předejte projectId jako prop, aby věděl, která assets projektu má obsluhovat. Funguje to stejně jako u vlastních bindingů: props je způsob, jak předat informace do třídy, která je pak čte na this.ctx.props při spuštění.
export default {
async fetch(request, env, ctx) {
const projectId = getProjectIdFromRequest(request);
const worker = env.LOADER.get(projectId, async () => {
const serverCode = await loadServerCode(projectId);
return {
mainModule: "index.js",
modules: {
"index.js": { js: serverCode },
},
compatibilityDate: "2026-08-28",
env: {
ASSETS: ctx.exports.AssetBinding({
props: { projectId },
}),
},
};
});
return await worker.getEntrypoint().fetch(request);
},
};export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const projectId = getProjectIdFromRequest(request);
const worker = env.LOADER.get(projectId, async () => {
const serverCode = await loadServerCode(projectId);
return {
mainModule: "index.js",
modules: {
"index.js": { js: serverCode },
},
compatibilityDate: "$today",
env: {
ASSETS: ctx.exports.AssetBinding({
props: { projectId },
}),
},
};
});
return await worker.getEntrypoint().fetch(request);
},
};Dynamic Worker vidí ASSETS jako binding a může volat env.ASSETS.fetch(request) protože to je metoda, kterou jste definovali na AssetBinding. Když Dynamic Worker tuto metodu zavolá, spustí se v loader Workeru, kde běží vaše AssetBinding třída čte manifest a obsah souborů z KV.
Použijte binding pro aktiva v Dynamic Workeru
Z pohledu Dynamic Workeru, env.ASSETS funguje jako kterýkoli jiný binding. Uživatel napíše serverový kód a zavolá env.ASSETS.fetch() k obsluze statických souborů:
// Inside the Dynamic Worker
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Handle API routes directly
if (url.pathname.startsWith("/api/")) {
return Response.json({ hello: "world" });
}
// Everything else — serve static assets
return env.ASSETS.fetch(request);
},
};// Inside the Dynamic Worker
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Handle API routes directly
if (url.pathname.startsWith("/api/")) {
return Response.json({ hello: "world" });
}
// Everything else — serve static assets
return env.ASSETS.fetch(request);
},
};Když Dynamic Worker volá env.ASSETS.fetch(request), volání proběhne přes RPC do loader Workeru, konkrétně na jeho AssetBinding, která vyhledá soubor v manifestu a přečte jej z KV. Dynamic Worker se o nic z toho nemusí starat, jen zavolá env.ASSETS.fetch(request) a získá zpět soubor se správnými hlavičkami, připravený k odeslání klientovi.