Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Cloudflare Workers ES Modules Support #764

Closed
GregBrimble opened this issue Nov 28, 2021 · 14 comments
Closed

[Feature]: Cloudflare Workers ES Modules Support #764

GregBrimble opened this issue Nov 28, 2021 · 14 comments
Assignees

Comments

@GregBrimble
Copy link
Contributor

GregBrimble commented Nov 28, 2021

What is the new or updated feature that you are suggesting?

Cloudflare Workers has recently GA'd support for ES modules and now uses this format as the default in templates.

ES modules brings three changes that affect Remix:

  1. The entry point syntax is different

    Where before, you set up a Worker using the service workers syntax, you now export a fetch handler:

    // Previous service worker syntax
    
    addEventListener('fetch', (event) => {
      event.respondWith(async () => {
        return new Response("Hello, world!")
      })
    })
    
    // New ES modules syntax
    
    export default {
      async fetch(request, env, context) {
        return new Response("Hello, world!")
      }
    }
  2. Bindings are no longer global

    Previously, any bindings of your Worker were available in the global scope. This pollution was part of problem with the service worker syntax, since there was no 'neat' way to pass them to a handler. Now, these bindings are available on the env (environment) object.

    // Previous service worker syntax
    
    addEventListener('fetch', (event) => {
      event.respondWith(async () => {
        const value = await MY_KV_NAMESPACE.get("key")
        return new Response("Value retrieved from KV: " + value)
      })
    })
    
    // New ES modules syntax
    
    export default {
      async fetch(request, env, context) {
        const value = await env.MY_KV_NAMESPACE.get("key")
        return new Response("Value retrieved from KV: " + value)
      }
    }
  3. Finally, Durable Objects are available when using ES modules syntax

    Durable Objects are powerful database primitives which I personally think are better suited to session storage than KV. KV is eventually consistent, so competing requests could easily squash values. Durable Objects, on the other hand, act as a point of synchronicity, as only one instance (for a given identifier) can ever be in existence. This controller can act as a gatekeeper, and removes the possibility for mistakes if you race data writes. Durable Objects are automatically created near to the user who requested them, and they can migrate as needed. They can also be restricted to a given jurisdiction, which is really useful (particularly for user data) in the context of GDPR etc.

Why should this feature be included?

  • Durable Objects support would bring a great alternative to KV for SessionStorage.
  • ES modules syntax is the default for Cloudflare Workers going forward. Adopting this would mean less friction for users who want to adapt their Worker, and also means documentation can be more easily understood between Cloudflare and Remix.
  • Full-stack Cloudflare Pages uses ES modules syntax, which would offer Remix users another target to deploy to, which offers a variety of new functionality such as rollbacks, preview URLs and git provider integrations.

I have already written an adapter for ES modules, a Cloudflare Pages project, and Durable Objects-backed SessionStorage. We'd need to work out a couple of things to finalize how that gets integrated (e.g. how a developer could specify a jurisdiction for a Durable Object), but it's pretty much there.

@universse
Copy link

universse commented Nov 29, 2021

Will it be possible to create API endpoints using the same worker? I'm looking at using WebSocket with Durable Objects. Or do I need to create and deploy a separate worker?

@GregBrimble
Copy link
Contributor Author

The Remix Approved™️ way to create API endpoints would be with Resource Routes.

However, with the current Cloudflare Workers integration, Remix leaves you with worker/index.js which actually registers the Remix logic in your service worker. Assuming that stays much the same, you'd be able to do a check for "if incoming request has route /api/my/endpoint, then do this, else, serve Remix stuff".

So the answer is yes. Just depends on how you prefer to separate the work.

@universse
Copy link

Is it correct to say that resource route won't have access to any Cloudflare API (KV, DOs etc), even with the integration? And the only way is to use the worker/index.js file directly.

@GregBrimble
Copy link
Contributor Author

No. With the getLoadContext function, you can pass through KV & DO namespaces to your Remix loaders :)

@universse
Copy link

universse commented Nov 30, 2021

Cool. Thanks for the pointer. And look like the same context is also available in action function too.

@mjackson
Copy link
Member

mjackson commented Dec 4, 2021

Thank you for the work here, @GregBrimble! We are anxious to make this change.

I have already written an adapter for ES modules, a Cloudflare Pages project, and Durable Objects-backed SessionStorage

Did you make a PR with this work somewhere?

Do we need to make any changes to our compiler? Currently we output CommonJS from our server build, but I believe we then webpack it so it runs on CF workers. Maybe that's the only piece that we need to change?

@noga-aviator
Copy link

any update on this ?

@ekosz
Copy link

ekosz commented Dec 23, 2021

I took at crack at this today. I think it may work now that Remix can generate ESM output.

@DenaliDeMots
Copy link

I've been interested in using Durable Objects with Remix, so I took a stab at this. Using Cloudflare's Typescript/Rollup durable object template as a starting point, I adapted the Remix cloudflare-workers template to bundle for Cloudflare's new ES Modules syntax. My example includes passing a durable object into Remix through the loader context.

You can clone my template and start experimenting with Durable Objects. I hope others find this useful. I'd be happy to work on a PR to get this into create-remix if that's helpful to the community.
https://github.com/DenaliDeMots/remix-cloudflare-es-modules

Note: I wasn't able to fully replicate the asset handler function due to this issue with @cloudflare/kv-asset-handler. Until this gets fixed, you won't be able to take advantage of getAssetFromKV's cacheControl options.

@huw
Copy link
Contributor

huw commented Dec 3, 2022

I added an implementation that resolves this issue in #4676 by adding a new adapter (necessary due to the way @cloudflare/kv-asset-handler imports manifest info).

@arjunyel
Copy link
Contributor

@huw hi friend, can you put your library on npm?

@huw
Copy link
Contributor

huw commented Jan 15, 2023

Nah, because I don’t want to be responsible for it. But it’s not difficult to clone a subfolder of a git repo or just patch the existing package directly with my code.

@xanderberkein
Copy link
Contributor

I just published a small adapter for Cloudflare ES Module Workers: https://github.com/xanderberkein/remix-cloudflare-module-workers/

@MichaelDeBoey
Copy link
Member

This was done in #6650 by @pcattori

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Closed
Development

Successfully merging a pull request may close this issue.