fresh icon indicating copy to clipboard operation
fresh copied to clipboard

Unable to deploy Fresh to Cloudflare Workers

Open mzl980425 opened this issue 3 months ago • 9 comments

Hi, I'm trying to deploy a Fresh project to Cloudflare Workers, but I've been unsuccessful.

Here are the steps I followed:

  1. I created a new project using the following command:

    deno run -Ar jsr:@fresh/init
    
  2. I followed the Cloudflare Workers | Fresh docs and added the required code to vite.config.ts file.

  3. I added a wrangler.jsonc file with the following content:

    {
      "$schema": "node_modules/wrangler/config-schema.json",
      "name": "fresh2-example",
      "main": "./main.ts",
      "compatibility_date": "2025-04-01",
      "compatibility_flags": ["nodejs_compat"],
      "observability": {
        "enabled": true
      },
      "upload_source_maps": true
    }
    
  4. I added the following code to the end of my main.ts file:

    export default {
      fetch: app.handler(),
    };
    
  5. I pushed the project to a GitHub repository, which you can find here: mzl980425/fresh2-example. Then, I created a new Worker on Cloudflare and connected it to this repository. The worker's build configuration is as follows:

    • Build command: npx deno task build
    • Deploy command: npx wrangler deploy
  6. After the deployment was complete, I found that static assets are served correctly (e.g., https://xxx.workers.dev/favicon.ico, https://xxx.workers.dev/logo.svg). However, all other page and API routes return a "NOT FOUND" error, including the root page (https://xxx.workers.dev/) and API routes like (https://xxx.workers.dev/api2/world).

Is there something wrong with my code or configuration? Any help would be greatly appreciated.

Thanks

mzl980425 avatar Sep 12 '25 07:09 mzl980425

Thanks for sharing the detailed steps of what you tried. It looks like step 4 is wrong. The proper entry point is autogenerated by fresh and is available under _fresh/server.js after having run deno task build. Directly importing from main.ts will not work because it's missing the autogenerated code that sets up the route manifest and much more.

marvinhagemeister avatar Sep 12 '25 08:09 marvinhagemeister

I reverted step 4, and updated my wrangler.jsonc file, changing the main entry to "./_fresh/server.js".

Then I run deno task build, the following error is in the console:

Image

mzl980425 avatar Sep 12 '25 08:09 mzl980425

I can confirm that just putting in wrangler.jsonc:

  "main": "./_fresh/server.js"

Will produce this error:

Task deploy deno task build && wrangler deploy
Task build vite build
vite v7.1.5 building for production...
✓ 25 modules transformed.
_fresh/client/.vite/manifest.json                  0.84 kB │ gzip: 0.28 kB
_fresh/client/assets/client-entry-BD_nbrQW.css     7.08 kB │ gzip: 2.31 kB
_fresh/client/assets/Counter-CcJtgLXz.js           0.82 kB │ gzip: 0.51 kB
_fresh/client/assets/client-snapshot-DkFxL7r5.js   1.33 kB │ gzip: 0.78 kB
_fresh/client/assets/preact.module-BC68DR7w.js    10.88 kB │ gzip: 4.62 kB
_fresh/client/assets/client-entry-Dj0PfJKP.js     24.60 kB │ gzip: 9.11 kB
✓ built in 627ms
vite v7.1.5 building SSR bundle for production...
vite v7.1.5 building SSR bundle for production... (x2)
✓ 1 modules transformed.
✗ Build failed in 1.15s
error during build:
[vite:define] Transform failed with 1 error:
/cloudflare-test/_fresh/server/server-entry.mjs:220:11943: ERROR: Multiple exports with the same name "default"
file: /cloudflare-test/_fresh/server/server-entry.mjs
    at failureErrorWithLog (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:1467:15)
    at /cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:736:50
    at responseCallbacks.<computed> (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:603:9)
    at handleIncomingPacket (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:658:12)
    at Readable.readFromStdout (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:581:7)
    at Readable.emit (ext:deno_node/_events.mjs:436:20)
    at addChunk (node:_stream_readable:452:12)
    at readableAddChunkPushByteMode (node:_stream_readable:413:3)
    at Readable.push (node:_stream_readable:308:48)
    at ext:deno_node/internal/webstreams/adapters.js:74:22
    at eventLoopTick (ext:core/01_core.js:179:7)

fry69 avatar Sep 12 '25 08:09 fry69

Bare bones reproduction repo -> https://github.com/fry69/fresh-vite-cloudflare-repro

(I even tested it with _fresh/server.js instead of ./_fresh/server.js, same error)

fry69 avatar Sep 12 '25 08:09 fry69

I'm not able to reproduce that Multiple exports with the same name "default" error. The build passes just fine. Had to point it to a different entrypoint though because it seems like the cloudflare vite plugin throws an error now when a file doesn't exist when you start the build, see https://github.com/denoland/fresh/pull/3405

Looking at the _fresh/server-entry.mjs file everything is correct too. It sounds very much like an issue in wrangler.

marvinhagemeister avatar Sep 12 '25 09:09 marvinhagemeister

If I try the approach with the server.js file, I get this error:

Task build vite build
vite v7.1.5 building for production...
✓ 25 modules transformed.
_fresh/client/.vite/manifest.json                  0.84 kB │ gzip: 0.28 kB
_fresh/client/assets/client-entry-D1ibAoFr.css     6.92 kB │ gzip: 2.27 kB
_fresh/client/assets/Counter-CcJtgLXz.js           0.82 kB │ gzip: 0.51 kB
_fresh/client/assets/client-snapshot-DkFxL7r5.js   1.33 kB │ gzip: 0.78 kB
_fresh/client/assets/preact.module-BC68DR7w.js    10.88 kB │ gzip: 4.62 kB
_fresh/client/assets/client-entry-D8t_PZsc.js     24.60 kB │ gzip: 9.11 kB
✓ built in 627ms
vite v7.1.5 building SSR bundle for production...
vite v7.1.5 building SSR bundle for production... (x2)
✓ 1 modules transformed.
✗ Build failed in 681ms
error during build:
[deno] Could not load /cloudflare-test/_fresh/server.js (imported by server.js): Import 'file:///cloudflare-test/_fresh/server.js' failed, not found.
    at __wbindgen_error_new (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:1073:15)
    at wasm://wasm/01438d62:wasm-function[11086]:0x404151
    at wasm://wasm/01438d62:wasm-function[4736]:0x39979c
    at wasm://wasm/01438d62:wasm-function[118]:0x3e24d
    at wasm://wasm/01438d62:wasm-function[1983]:0x3011cb
    at wasm://wasm/01438d62:wasm-function[10418]:0x3fb858
    at wasm://wasm/01438d62:wasm-function[11766]:0x4081aa
    at __wbg_adapter_54 (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:252:8)
    at real (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:156:14)
    at ext:core/01_core.js:295:9
    at eventLoopTick (ext:core/01_core.js:179:7)
$ wrangler --version

 ⛅️ wrangler 4.35.0
───────────────────

I assume the trick with the server.js file only works if a prior build already has generated artifacts in _fresh, but not with a clean slate.

fry69 avatar Sep 12 '25 09:09 fry69

Gosh that sounds bad. I successfully deployed Fresh to CF workers when working on the vite plugin a while ago, but it seems like they did a bunch of changes that broke it.

marvinhagemeister avatar Sep 12 '25 10:09 marvinhagemeister

Curiously enough, having the _fresh/server.js already there seems to fix the build issue. Maybe the @fresh/plugin-vite should create that file earlier.

As a temporary fix, I found out that adding a _fresh/server.js before starting the build with the contents: "export default {}" seems to fix it. I suggest even comitting it to the repo so it stays there. I will try to look into it more.

m4rocks avatar Oct 01 '25 18:10 m4rocks

@marvinhagemeister Is this issue currently assigned to someone? It seems the Cloudflare solution is not compatible with clean pipeline builds

athulanilthomas avatar Nov 05 '25 14:11 athulanilthomas

Curiously enough, having the _fresh/server.js already there seems to fix the build issue. Maybe the @fresh/plugin-vite should create that file earlier.

As a temporary fix, I found out that adding a _fresh/server.js before starting the build with the contents: "export default {}" seems to fix it. I suggest even comitting it to the repo so it stays there. I will try to look into it more.

This issue is still a problem for me. Thanks to this suggestion I was able to work around it by creating a temporary server.js. However this was not enough to get build and deploy pipelines working correctly for my project. I found that deno task build twice in a row allowed the "real" server.js to get picked up on the second time.

Of course once you figure out what the content of the server.js should be, you can just echo the correct content into the file and forget the second build, but that could be a pain to maintain and builds are fast.

This was the only way I could get GitHub Actions to work. Hopefully this is of some use for people trying to use workers in an automated way until a fix is found.

connorgolden avatar Dec 07 '25 02:12 connorgolden