This is a proof-of-concept implementation of deploying Remix apps to Firebase Functions using a bundle hosted on Firebase Hosting.
- Remix App is built
- The server code, along with the request handler, is bundled into a single js file using esbuild
- The server bundle is uploaded to Firebase hosting with all other browser assets
- HTTP-triggered Firebase Function is deployed with Firebase Hosting proxying requests to it.
- The fetches the server bundle from Firebase Hosting that proxied the request
- The bundle is parsed and the resulting Remix request handler is used as usual to serve the request.
To update the Remix app you only need to build, bundle and deploy it to Firebase Hosting. The Hosting deployments are much faster than Function deployments.
Static browser assets are always in sync with the Remix app as they are deployed simultaneously.
Firebase Hosting has preview channels - automatically generated Hosting sites that you can use to deploy and test new versions of the app without affecting the main site, which is especially handy for pull-request previews. Hosted bundle approach automatically supports preview channels, no need to redeploy or deploy multiple functions for each preview channel.
Firebase Hosting supports one-click rollbacks - you can instantly switch to a different version, as long as it's still retained. With hosted bundle approach the Remix app is rolled back with your hosting rollback.
This approach adds additional latency due to bundle fetching and parsing. In a steady state with the majority of requests hitting warm function instances with cached responses the extra latency is 14ms at least (round trip to Google CDN from within the function instance), with cold instances it adds at least 500ms on top of the usual cold start time.
The server bundle is usually quite chunky, so this may add something to your Hosting bill. In a steady-state operation with a trivial caching the cost should be marginal.
It is possible to mitigate the extra latency for fetching/checking the server bundle. Because in a real application 99% of all requests will always result in using the same Remix bundle version, it is possible to cache it based on the origin and use the cached version for optimistic rendering while revalidating the cache. The optimistic result could be served as-is or it could be discarded and re-rendered with the fresh version in case of missmatch.