Kotlin JS React accessing/configuring environment variables

I’m curious how I would use .env, .env.local, etc. in my Kotlin React application. I am using gradle, and essentially I want to be able to use different values depending upon my environment. Typically I would just create my .env and access process.env.VAR_NAME and I’m good. So, I thought I would try using something like “external val process: dynamic” and then accessing my variables like so. However, it doesn’t seem to work, possibly because the .env doesn’t get picked up? Anyone solve this?

Thanks for any feedback advice. Also, on a curious note, how would one suggest using ‘create-react-kotlin-app’ in an automated build process? That’s the main reason I choose gradle. I am open to ideas and suggestions.

Thank you!

1 Like

Hi,

I am using following code for my env values:

external val process: Process

external interface Process {
val env: dynamic
}

And then execute

process.env.FUNCTION_NAME.unsafeCast<String>()

I’ve tried to use your approach, but cannot access the values in .env. Do I need to setup anything else? Could you give some more background, please?

Thanks
Harald

@chrisbln Could you please give some infos how to read the variables from .env. I’ve tried to follow your steps, but cannot access the configuration.

Thanks
Harald

How would you expect this to work? When the JavaScript is being executed in a browser there are no environment variables like there would be in a NodeJS or JVM process. You’d need to find a way to make the values accessible in variables in the browser.

I’m doing this using Webpack’s DefinePlugin and different configuration files which are loaded in the Webpack configuration depending on whether the build is in development or production mode. Not exactly elegant but does the job.

@simonjacobs could you provide an example of your setup? much appreciated!

You can execute extra code on your webpack configuration by placing JS files in a webpack.config.d directory (docs). You can put, for example:

//webpack.config.d/additional-config.js
const webpack = require("webpack")

var one, two

if (config.mode === "production") { // the build process makes the config object available
   one = "production_one"
   two = "production_two"
}
else {
   one = "development_one"
   two = "development_two"
}
   
const definePlugin = new webpack.DefinePlugin(
   {
      ENV_VAR_ONE: one,
      ENV_VAR_TWO: two
   }
)

config.plugins.push(definePlugin)

Then back in Kotlin, if you put something like:

// inside Kotlin class
val environmentVariableOne = js("ENV_VAR_ONE")
val environmentVariableTwo = js("ENV_VAR_TWO")

then you should magically find that the variables are set to the values from the config file depending on the mode. The mode can be set in Gradle:

// build.gradle.kts
kotlin { // This is the JS plugin setup. There's an extra js closure for Multiplatform
   target {
      browser {
         webpackTask {
            mode = if (condition) Mode.DEVELOPMENT else Mode.PRODUCTION 
         }
      }
   }
}

So then you have a way to transport variables defined during the build to the code depending on environment type.

1 Like

Thanks for sharing this @simonjacobs,

one issue that we are having is that we can’t test minified builds on the development environment with this solution. Also, we can’t have more than 2 environments (e.g., test, dev, prod).

Do you know how to configure this?

After a bit more thinking, I see you can pass arbitrary values to the Webpack configuration (and so your Kotlin program in turn if desired) from the Gradle build using the Webpack environment variables feature (Webpack v4 docs – note the Kotlin plugin currently uses this version). To do this you also need to adjust the Webpack configuration file so that it exports a function. To illustrate:

//build.gradle.kts
kotlin {
   js { // Kotlin plugin v1.4 syntax
      browser {
         webpackTask {
            args.plusAssign( listOf( "--env.arbitraryValue=fortyTwo" ) )
            webpackConfigApplier {
               export = false // stops default export of config object in Webpack code
            } 
         }
      }
   }
}
//webpack.config.d/additional-config.js
module.exports = env => {
   
  const definePlugin = new webpack.DefinePlugin(
      {
         REPLACE_ME: env.arbitraryValue
      }
   )
   config.plugins.push(definePlugin)

   return config
}
// In the Kotlin program
val theAnswer = js("REPLACE_ME") // sets theAnswer = "fortyTwo"

That should then allow multiple setups in multiple environments.

3 Likes

Thanks @simonjacobs!

This appears to work for me. If anyone else is stuck, one thing that I spent a long time debugging was why this didn’t seem to work for my run configuration using ./gradlew run (or ./gradlew browserDevelopmentRun). The ‘webpack’ in webpackTask specifically refers to ./gradlew browserDevelopmentWebpack or ./gradlew browserProductionWebpack tasks, so if you want this to run with browserDevelopmentRun you need to add a runTask {...} section under browser also.

1 Like

You could always use a .env file, dotenv, and the DefinePlugin in your webpack config stored in webpack.config.d:

  1. Make sure you’ve installed dotenv in gradle:
// build.gralde.kts
dependencies {
   .....
   implementation(npm("dotenv", "16.0.1"))
   ....
}
  1. Create an .env file and place it at the root of your project folder:
//.env

MY_ENV_VAR=some_value
  1. In our webpack configuration file, save the environment variables to a property name of your choice (mine was PROCESS_ENV):
// webpack.config.d/config.js
var webpack = require("webpack");
var path = require('path');
var dotenv = require('dotenv').config({ path: path.resolve(__dirname, '../../../../.env') });

var definePlugin = new webpack.DefinePlugin(
   {
      "PROCESS_ENV": JSON.stringify(dotenv.parsed)
   }
)

config.plugins.push(definePlugin)
  1. In Kotlin kode, you can access whichever process env variables you need:
// ExampleFile.kt
val processEnv = js("PROCESS_ENV")
val someValue = processEnv.MY_ENV_VAR // some_value

The above works well when building the app locally. In my experience, deploying to a service like Heroku/Netlify/Vercel, you’ll need to edit the config.js file to export the process.env object to WebPack without dotenv:

// webpack.config.d/config.js
var webpack = require("webpack");

var definePlugin = new webpack.DefinePlugin(
   {
      "PROCESS_ENV": JSON.stringify(process.env)
   }
)

config.plugins.push(definePlugin)