I always disliked the .env file. Almost every time I was forced to place it on the top level of my app directory. At some point i started to use the npm config package. This gives the application a consistent configuration interface and there is a formidable way to implement it into the NextJS environment. For people which prefer code over text, feel free to checkout the finished implementation.
Implementation
As reference we usa a NextJS Typescript zero configuration setup.
npx create-next-app@latest --ts
# or
yarn create next-app --typescript
First we have to install the necessary npm package.
npm install config @types/config
Now we create the config directory and configuration files. The default.yaml will be used later as a type template.
config
- default.yaml
- development.yaml
- production.yaml
NextJS has the possibility to define environment variables in the config file. To make full use of it, we will import the config package and set the environment variables to the configuration variables with config.util.toObject(). By default NextJS creates a next.config.js file in the root directory. Change the extension to .mjs and replace the content with the following.
// next.config.mjs
import config from 'config';
const nextConfig = {
reactStrictMode: true,
env: config.util.toObject()
}
At this point we already replaced the .env files. NextJS will load the configuration files from the config folder with the corresponding activ environment.
To use it with typescript we have to do some additional work. First we will need a type declaration file for the config variables. Then we have to add it to the global process environment.
From here on out we will work with an example for better traceability.
Let us put some stuff into the default.yaml file.
firebase:
apiKey: apiKey
appId: appId
projectId: projectId
authDomain: authDomain
databaseURL: databaseURL
storageBucket: storageBucket
messagingSenderId: messagingSenderId
measurementId: measurementId
Next we will have to reflect the configuration variables in typescript. In my native language there is a saying:
Viele Wege führen nach Rom
In short it means there are many solutions for a problem. For example, we could do it manually or use a web based yaml to typescript converter. Unfortunately i could not find any npm package for this problem. But as many of us know: Why do something in 10 minutes when you can automate it in 10 hours? Consequently, let us install an additional package.
npm install -D ymlts
Now we can convert the configuration file to typescript from our console. With the following command we create the necessary type file.
npx ymlts config/default @types/config
This will create a config.d.ts in the @types directory. Its content should look like this.
// @types/config.d.ts
interface Config {
firebase: Firebase;
}
interface Firebase {
apiKey: string;
appId: string;
projectId: string;
authDomain: string;
databaseURL: string;
storageBucket: string;
messagingSenderId: string;
measurementId: string;
}
Last but not least, we have to add the new config type to the process environment. For this we create a global.d.ts file in the @types directory...
@types
- global.d.ts
...and extend the process environment with the config type.
// @types/global.d.ts
namespace NodeJS {
interface ProcessEnv extends Config { }
}
For future use, if you are really lazy: It is possible to add the ymlts command to the package.json and start it with npm run env.
// package.json
"scripts": {
"env": "npx ymlts config/default @types/config",
}
Conclusion
So why? At first glance this looks like unnecessary work, but there is some neat stuff!
Better readability with a humanfriendly YAML file.
Environment variables managed in an organized config folder.
Normaly you have to prefix Environment Variables with NEXT_PUBLIC_ to expose them to the browser. But NextJS replaces Environment Variables at buildtime which should make it obsolete.
By converting the config file to types we get the possibility to use intellisense with process.env.
The YAML configuration variables can be nested. Related to this blog as example it is possible to initialize the Firebase app with
initializeApp(process.env.firebase);
.
Top comments (0)