From 11c5a7ce52ea0409e165fc6b803ac54e2ce0e438 Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando <12949382+reynaldichernando@users.noreply.github.com> Date: Fri, 4 Sep 2026 22:57:11 +0700 Subject: [PATCH] dynamic workers docs (#3747) * dynamic workers docs * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/docs/src/Workers/dynamic.md | 99 +++++++++++++++++++++++++++++++++ src/docs/src/sidebar.js | 8 +++ 2 files changed, 107 insertions(+) create mode 100644 src/docs/src/Workers/dynamic.md diff --git a/src/docs/src/Workers/dynamic.md b/src/docs/src/Workers/dynamic.md new file mode 100644 index 000000000..14c8c276f --- /dev/null +++ b/src/docs/src/Workers/dynamic.md @@ -0,0 +1,99 @@ +--- +title: Dynamic Workers +description: Deploy server-side code from inside a hosted website by dropping a .worker.js file into a __workers folder, with no deploy step and no worker to register. +platforms: [workers] +--- + +If you already [deploy your site to Puter](/deployments/#deploy-to-puter), dynamic workers let you add backend endpoints to it without managing workers separately. + +A dynamic worker is server-side code that lives inside your hosted site and deploys itself on demand. Drop a file at `__workers/api.worker.js` next to your `index.html`, publish the site as you normally would, and `https://your-site.puter.site/__workers/api/...` starts serving it. There is no deploy step of its own, no worker name to register, and nothing to keep in sync between the site and its backend. + +
Dynamic workers are routed on *.puter.site only, and they don't appear in puter.workers.list() or the Developer Center. Read Limitations before you build on them.
+ +## Comparison + +A regular [Serverless Worker](/Workers/) is a separate thing from the site that uses it: you deploy the worker, it gets its own `*.puter.work` subdomain, and from then on you keep the site and the worker in sync by hand — matching versions, updating URLs, remembering which worker belongs to which site. + +With a dynamic worker, the code is just a file in the site, so: + +- the site and its backend version together — same folder, same deploy, same backup; +- the URL is derived from where the file lives, so there is nothing to wire up; +- there is no worker record to create, rename, or clean up. + +The tradeoff is that there are fewer tools for working with them: they don't show up in [`puter.workers.list()`](/Workers/list/) or the Developer Center. See [Limitations](#limitations). + +## File Layout + +Inside the directory your site is hosted from, create a folder named `__workers`. Every file directly inside it whose name ends in `.worker.js` is a dynamic worker. + +``` +my-site/ + index.html + style.css + __workers/ + api.worker.js -> /__workers/api/... + matchmaking.worker.js -> /__workers/matchmaking/... +``` + +Two rules apply to the files: + +- **Top level only.** `__workers/nested/thing.worker.js` is never deployed. The URL has room for one worker name, so there would be no way to point at a file inside a subfolder. +- **Allowed names.** The part before `.worker.js` must match `[a-z0-9_-]+` — lowercase letters, digits, underscore, hyphen. The name is also the URL path segment, so sticking to those characters keeps capitalization and URL encoding from getting in the way. + +Anything else under `__workers/` — a README, a nested folder, a `helpers.js` — is ignored. It is not deployed, and it is not served either. + +## Syntax + +Dynamic workers are written exactly like regular workers, with the same [`router`](/Workers/router/) API and the same globals: + +```js +// __workers/api.worker.js +router.get("/health", async () => { + return { ok: true }; +}); + +router.post("/scores/:game", async ({ request, params }) => { + const body = await request.json(); + await me.puter.kv.set(`score:${params.game}:${body.player}`, body.score); + return { saved: true }; +}); +``` + +Everything in the [`router`](/Workers/router/) documentation applies unchanged: [route parameters](/Workers/router/#route-parameters), [wildcards](/Workers/router/#wildcard-routes), [`me.puter` and `user.puter`](/Workers/router/#integration-with-puter-js), [CORS](/Workers/router/#cors), and returning objects vs. a `Response`. + +## URLs and Path Mapping + +``` +https://.puter.site/__workers// +``` + +The `/__workers/` prefix is stripped before the request reaches your code; the worker sees the remainder: + +| Request | Worker sees | +| --- | --- | +| `/__workers/api` | `/` | +| `/__workers/api/` | `/` | +| `/__workers/api/health` | `/health` | +| `/__workers/api/scores/chess?top=10` | `/scores/chess?top=10` | + +Details worth knowing: + +- The worker segment is read from the **URL-decoded** path and lowercased, so `/__workers/API/x` and `/__workers/%61pi/x` both reach `api`. +- The rest of the path keeps its original encoding — an encoded slash in your path stays encoded rather than becoming a real separator. +- Query strings are preserved. Fragments never leave the browser. + +## Response Codes + +| Code | Meaning | +| --- | --- | +| `404` | No such worker file under the site's `__workers/`, or the file is in a subfolder or misnamed. | +| `503` | The file exists but the worker could not be started. Worth retrying — it never means the worker isn't there. | +| Your own | Anything your handler returns. | + +## Limitations + +**`puter.site` only.** Dynamic workers are routed on the primary hosting domain. The alternate hosting domain and `puter.app` (private apps) are not routed yet, and custom domains aren't supported. + +**Not listed by the Workers API.** [`puter.workers.list()`](/Workers/list/) and the Developer Center's Workers view do not show dynamic workers. They have no worker record by design: that's what saves you from keeping one in sync, and it's also why there's nothing to list. + +**One sandbox per site.** All of a site's workers share the same KV and AppData namespace, so they can read and write each other's data. That's intentional — it's how two workers in one site cooperate — but it means you can't keep one worker's data private from another in the same site. diff --git a/src/docs/src/sidebar.js b/src/docs/src/sidebar.js index 4020d302d..550027868 100755 --- a/src/docs/src/sidebar.js +++ b/src/docs/src/sidebar.js @@ -487,6 +487,14 @@ let sidebar = [ source: '/Workers/types.md', path: '/Workers/types', }, + { + title: 'Dynamic Workers', + page_title: 'Dynamic Workers', + title_tag: 'Dynamic Workers', + icon: '/assets/img/object.svg', + source: '/Workers/dynamic.md', + path: '/Workers/dynamic', + }, { title: 'create()', page_title: 'puter.workers.create()',