Redirecting to static webpacker Content With Rails

With the release of Rails 6, webpacker will be the new default compilation pipeline for Javascript assets. This is a great thing once you wrap your head around how to use webpack and webpacker.

One of the benefits of webpacker, is like sprockets, the automatic compilation and fingerprinting of assets for production builds. Fingerprinting is very handy as it will automatically break any browser caches when new versions of that asset are compiled.

What if you have a Javascript snippet that you want others to use dynamically, such as a widget or html snippet? The fingerprinting of assets then makes it very difficult, as the filename will keep changing over and over again.

Fortunately with a combination of a Rails route and diving into the webpacker gem, this can be accomplished fairly easily using redirects.

Rails Routing

Let’s say you have a Javascript widget that you want your customers to embed on their site. You want something easy to remember, such as https://www.example.com/external/widget.js.

Let’s first setup our routes:

# config/routes.rb
get :widget, to: "widgets#show", path: "external/widget"

Our controller needs to then respond to the request, find the webpacker asset, and then respond with a redirect to the fingerprinted asset. Let’s look at the code:

class WidgetsController < ApplicationController
    include ActionView::Helpers::AssetUrlHelper
    include Webpacker::Helper

    protect_from_forgery except: :show

    def show
        respond_to do |format|
            format.js { redirect_to widget_javascript_source }
        end
    end

    private

        def widget_javascript_source
            asset_pack_url("widget.js")
        end

end

Most of this is fairly straight forward. The first thing we want to do is tell Rails not to worry about protecting from anti-forgery requests. Since this will be used from external applications, it’s not something we’re worried about.

The real magic comes in our widget_javascript_source method, which refers to the asset_pack_url that is included in the Webpacker::Helper module.

What does webpacker actually do?

To figure out what asset_pack_url does, it’s good to know what webpacker actually does for us behind the scenes.

The webpacker gem is a wrapper around webpack, and hides away a lot of the complexity of webpack. For our use case, the most important thing is compiling our assets. Instead of using sprockets, we can now rely on webpack to do all of our assets, and with that, we can hook into the broader webpack ecosystem. Part of that includes asset compilation if we’re using Typescript, asset transpiling using a tool such as babel, and minification and fingerprinting to optimize the assets we are serving.

One thing that webpacker does for us when it compiles all of these assets for us is it creates a manifest.json file with a list of entrypoints and a mapping of the compiled asset name. This is what the new javascript_pack_tag ends up doing for us. We pass in the friendly name, and it uses that manifest file to find the fingerprinted asset name.

If we look at the manifest.json file in the public/packs folder, we’ll see an example

{
    "application.js": "/packs/application-feff836d0a539cc07796.js",
    "application.js.map": "/packs/application-feff836d0a539cc07796.js.map",
    "widget.js": "/packs/widget-feff836d0a539cc07796.js",
    "widget.js.map": "/packs/widget-feff836d0a539cc07796.js.map",
    "entrypoints": {
        "application": {
            "js": [
                "/packs/application-feff836d0a539cc07796.js"
            ],
            "js.map": [
                "/packs/application-feff836d0a539cc07796.js.map"
            ]
        },
        "widget": {
            "js": [
                "/packs/widget-feff836d0a539cc07796.js"
            ],
            "js.map": [
                "/packs/widget-feff836d0a539cc07796.js.map"
            ]
        }
    }
}

What webpacker is doing is reading the content of our manifest file, looking up the friendly name of our asset (widget.js), and then returns to us the properly compiled, minified and fingerprinted asset.

So, all we need to ask webpacker for us the name of our compiled widget.js and then redirect from there!


After looking through some of the internals of webpacker gem, we can see just how easy it is to hook into the compiled assets from our Rails controllers. This allows us to continue to modify and enhance our external widget.js file, and even work on it in a language such as Typescript, while still providing a single, easy to use snippet for others to embed in their own websites.

comments powered by Disqus