October 6, 2022

Robotic Notes

All technology News

How to set configuration variables in Next.js | by Eric Burel

6 min read


Illustration inspired by Steve Johnson work, found searching “configuration” on Pexels

Imagine the following scenario: you are a front-end developer at NPM, Inc. (awesome job, congrats!). Your job for today is to display the current version of packages in their landing page.

npmjs page for Next.js. It displays some values ​​based on the project’s package.json, like the version number 10.0.7 and MIT license. Source: https://www.npmjs.com/package/next

This version is located in the package.json file. But, of course, you know you can’t import package.json in front-end code, for security reasons. It could leak some secret information, like the list of your development packages. You want the client code to access only public information like the application version or license.

Solving this issue is surprisingly… not that easy. Let’s explore how you can achieve this goal in a secure manner, in the context of a Next.js application.

TL; DR: there is a summary table at the bottom of this article. Methods 1), 4) and 5) are the most relevant, you can safely skip the others.

I use the package.json version number as an example. The patterns I describe here apply to any kind of global configuration variable: handling API keys for Stripe, setting the URL of your application, implementing “feature flags”, etc.

There are many possible approaches depending on your precise use case. Your variable may be known in advance or computed at build-time, it may be shared with the client or limited to the server. I’ll try my best to be exhaustive in this article.

Let’s start with the simplest pattern.

1) Import the value from a file

Yeah, you don’t have to do everything using Next.js features. Just create a config.ts file somewhere in your app and centralize relevant configurations values ​​there.

If some values ​​are dynamic, simply use JavaScript code to compute them.

Example: export const config = { foo: 42 }; and then import { config } from "config";

You could even generate a JSON file in using a custom script. The script named prebuild in your package.json will always be run before build. That’s a good place to generate your configuration file before Next.js starts building.

The limitation is that the code is ran client-side: you can only store public values ​​there. So it won’t work for our use case, because you can’t import package.json in this configuration file.

2) Data fetching

Just for the record, you always have the ability to make your value available through an API and query it. Like you would do for any other kind of data (blog posts, pictures, list of users ().

2.1) Getting the value in the front end from an API

Just query the value as usual in React, using fetch or whatever, and put the value into React context.

This is a common pattern for getting a configuration value that can be changed from a headless CMS or an administration interface.

2.2) Through Next.js getStaticProps, getInitialProps, getServerRenderProps

If you want to query it during server-side render or static generation, then you can use Next.js built-in methods for data fetching.

Fetch the value in getStaticProps (or getInitialProps ) and pass it as your page or app props. You can then put it in React context if you need to pass it down the React tree.

Documentation: https://nextjs.org/docs/basic-features/data-fetching

Data fetching is cool, but in most scenarios you certainly don’t want to setup a whole API just to display your app version. Now, let’s look for simpler approaches specific to configuration variables.

3) Next.js runtime configuration (to be avoided)

You might think Next.js “runtime configuration” is a good place to put configuration stuffs. Sigh, you are going to be disappointed.

Runtime configuration is tricky in Next, and to be honest, I could not figure out a legitimate use case for them.

It’s most probably an historical feature that lost its initial purpose as Next.js added more advanced features for data fetching and configuration.

The problem is the “runtime” part: those configs are not compatible with Automatic Static Optimization (rendering a first version of your page at build time), which you definitely want.

Instead of a runtime configuration, you might want to use a simple config object, see the first pattern of this article.

Documentation: https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration

Let’s look at the pattern recommended by Next: setting variables in .env files.

4) Setting an environment variable in a .env file

This is the recommended way to manage technical environment variables. Next.js is able to handle .env files out-of-the-box, which is pretty neat.

There are 2 kinds of variables: public variables prefixed with NEXT_PUBLIC_ and server-only variables.

Server-side, nothing fancy. Server-only variables behave as normal environment variables. Public variables are also available in the server as normal environment variables.

Client-side, the behavior is more complex. Public variables will be injected in the client bundle at build time. They are basically being replaced in the code by their value: for instanceprocess.env.NEXT_PUBLIC_42 literally becomes 42.

It means if you update the environment variable, you need to rebuild the application to see the change in the front-end code. This is fine for most configuration options, like setting API key or some technical variables.

If your value changes a lot, you might instead want to get it dynamically, using some JavaScript code or an API call, see pattern 1 and 2.

Also, patterns based on environment variables are limited to string values ​​only.

Documentation: https://nextjs.org/docs/basic-features/environment-variables

Since this pattern is limited to static variables known in advance, it doesn’t work with our use case. We can’t get the most recent package.json version automatically. Following pattern will finally solve this issue elegantly.

5) Setting an environment variable in next.config.js env object

The problem with .env files is that they are not JavaScript file: you can’t compute a variable dynamically.

To handle dynamic variables, you need to set the env object in next.config.js. This file is ran at build time, on your build server, so it’s a safe place even to manipulate private data like the content of package.json.

This approach is documented as an “old” pattern, but it is totally legitimate for the very specific use case where you need to compute your variable dynamically at build-time.

And it’s perfect for our use case! You could write your config as follow:

Then! 🎉

Beware: only add public variables, prefixed with NEXT_PUBLIC_, in this object. If you put secret values ​​there, they could leak, because this pattern makes it easy to mistakenly include secret variables in the client build. There is no valid use case for storing secret variables there, so don’t do it. Don’t. Please.

Documentation: https://nextjs.org/docs/api-reference/next.config.js/environment-variables.

It has a few limitations, mainly it can only handle string values. Also, values ​​are injected at build-time only for the client, exactly like for .env files in pattern 4.

The final pattern of this article will be quite similar in terms of usage, but a bit more powerful thanks to some Webpack magic.

6) Inject variables at build-time using Webpack DefinePlugin

This approach is based on code injection: it literally changes your code to inject your values ​​in it, using Webpack DefinePlugin. This is similar to how Next.js handles process.env client-side under the hood.

The advantage compared to normal process.env variables is that you can pass any serializable value: strings, numbers, JSON objects (using JSON.stringify ), etc.

Also, it allows you to set a different value client-side and server-side for the same variable. When Next.js builds your app, it reads next.config.js twice with different options, once to setup the server, and a second time to build the client. Therefore, you can do something like this to set an “IS_SERVER” variable:

Documentation: https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config

As for environment variable patterns, the value is computed at build-time only for the client, and it’s a bit more complex than other patterns because it relies on the Webpack plugin. Use only if you need too.





Source link