How can I customize my Service Worker based on environment variables?

It's probably way too late for you now but for others this is how I went on about getting around this. This process still utilizes .env file for your environment related variables.
The idea is to create a new script that loads the .env file which creates a new file populated with the variables from .env file.
After the build process we simply import the newly generated file in sw.js for it to be used.

Here are the steps.
First create a file called swEnvbuild.js which will be your script that runs after webpack

//swEnvBuild.js - script that is separate from webpack
require('dotenv').config(); // make sure you have '.env' file in pwd
const fs = require('fs');

fs.writeFileSync('./dist/public/swenv.js',
`
const process = {
  env: {
    VUE_APP_FCM_SENDER_ID: conf.VUE_APP_FCM_SENDER_ID
  }
}
`);

Secondly, we import the file that was generated from swEnvBuild.js called swenv.js in our sw.js.

// sw.js
importScripts('swenv.js'); // this file should have all the vars declared
console.log(process.env.VUE_APP_FCM_SENDER_ID);

And lastly, for this to work with one command just add the following in your npm scripts (assuming that you're running either Linux/Mac).

scripts: {
  "start": "webpack && node swEnvBuild.js"
}

Hopefully, that should do the trick. I wish there was much cleaner way to do this so I'd be happy to know how other's solution too.


I had this same problem and the key is getting the webpack build process to output the env vars that it uses so they can be imported into the service worker. This saves you from having to duplicate your env var definitions into something else that pre-processes your service worker (which is just messy anyway because that file is in source control).

  1. create a new Webpack plugin

    // <project-root>/vue-config/DumpVueEnvVarsWebpackPlugin.js
    const path = require('path')
    const fs = require('fs')
    
    const pluginName = 'DumpVueEnvVarsWebpackPlugin'
    
    /**
     * We to configure the service-worker to cache calls to both the API and the
     * static content server but these are configurable URLs. We already use the env var
     * system that vue-cli offers so implementing something outside the build
     * process that parses the service-worker file would be messy. This lets us
     * dump the env vars as configured for the rest of the app and import them into
     * the service-worker script to use them.
     *
     * We need to do this as the service-worker script is NOT processed by webpack
     * so we can't put any placeholders in it directly.
     */
    
    module.exports = class DumpVueEnvVarsWebpackPlugin {
      constructor(opts) {
        this.filename = opts.filename || 'env-vars-dump.js'
      }
    
      apply(compiler) {
        const fileContent = Object.keys(process.env)
          .filter(k => k.startsWith('VUE_APP_'))
          .reduce((accum, currKey) => {
            const val = process.env[currKey]
            accum += `const ${currKey} = '${val}'\n`
            return accum
          }, '')
        const outputDir = compiler.options.output.path
        if (!fs.existsSync(outputDir)) {
          // TODO ideally we'd let Webpack create it for us, but not sure how to
          // make this run later in the lifecycle
          fs.mkdirSync(outputDir)
        }
        const fullOutputPath = path.join(outputDir, this.filename)
        console.debug(
          `[DumpVueEnvVarsWebpackPlugin] dumping env vars to file=${fullOutputPath}`,
        )
        fs.writeFileSync(fullOutputPath, fileContent)
      }
    }
    
  2. use the plugin in your vue-cli config (vue.config.js or vue-config/config.default.js if your config is split over a few files)

    // import our plugin (change the path to where you saved the plugin script)
    const DumpVueEnvVarsWebpackPlugin = require('./DumpVueEnvVarsWebpackPlugin.js')
    
    module.exports = {
      // other stuff...
      configureWebpack: {
        plugins: [
          // We add our plugin here
          new DumpVueEnvVarsWebpackPlugin({ filename: 'my-env-vars.js' })
        ],
      },
    }
    
  3. in our service worker script, we can now import the file that we wrote with our Webpack plugin (it'll be there after the build has happened and service workers don't run in dev mode so we should be safe)

    importScripts('./my-env-vars.js') // written by DumpVueEnvVarsWebpackPlugin
    const fcmSenderId = VUE_APP_FCM_SENDER_ID // comes from script imported above
    console.debug(`Using sender ID = ${fcmSenderId}`)
    
    // use the variable
    firebase.initializeApp({
        'messagingSenderId': fcmSenderId
    })
    

It's not perfect, but it certainly gets the job done. It's D-R-Y as you only have to define all your env vars in one spot and the whole app uses the same values. Plus, it doesn't process any files that are in source control. I don't like that the plugin runs too early in the Webpack lifecycle so we have to create the dist dir but hopefully someone else smarter than me will have a fix for that.