Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jihchi/bbc718366fbb6342e0a48045f18c82e5 to your computer and use it in GitHub Desktop.
Save jihchi/bbc718366fbb6342e0a48045f18c82e5 to your computer and use it in GitHub Desktop.

Pros

  • Usage is simple, you could directly access the config via process.env.NEXT_PUBLIC_*, for example:
// call-site, agents.js
const restaurantApi = axios.createConfig({
  baseUrl: process.env.NEXT_PUBLIC_RESTAURANT_API_URI,
});

Cons

  • When you change any NEXT_PUBLIC_* variables, you'd have to (re)build the application and/or deploy the new bundle.
  • Managing environment variables may be cumbersome when you're following The Twelve Factors.
    • How would you manage the environment variables when you have different environment variables in deploys (development, staging, production, etc)?

Pros

  • De-couple with build-time environment variables, just build once for production bundle.
  • You could use the same production bundle with different public runtime configurations in deploys.
  • Usage is simple, you could access the config via a helper from next/config, for example:
// set up in next.config.js
module.exports = {
  publicRuntimeConfig: {
    restaurantApi: process.env.RUNTIME_RESTAURANT_API_URI, // Pass through env variables
  }, 
}

// call-site, agents.js
import getConfig from 'next/config';

const restaurantApi = axios.createConfig({
  baseUrl: getConfig().publicRuntimeConfig.restaurantApi,
});

Cons

  • Runtime configuration won't be available to any page (or component in a page) without getInitialProps.
    • It won't work for Static and SSG pages.
  • There is a bug where the publicRuntimeConfig is undefined in SSR pages.

Self-hosted public runtime configuration

  1. On server-side, the config can be accessed just like ordinary environment variables.
  2. On client-side, first to retreive the config from the server and then store it in window.
    • Ensure that the config is injected and stored before its being used.

Implementation

First, let's add a new API route called /api/config:

// src/pages/api/config.js
const config = {
  restaurantApi: process.env.RESTAURANT_API_URI,
};

// properly access public runtime configuration on both client-side and server-side
export const getPublicConfig = (name) =>
  typeof window === 'undefined' ? config[name] : window.PUBLIC_CONFIG[name];

export default function handler(_req, res) {
  res.status(200).send(`window.PUBLIC_CONFIG = ${JSON.stringify(config)}`);
}

Then, add a script tag in the head, you could add it to either src/pages/_app.tsx or src/pages/_document.tsx, if you don't use any of them, you could add it to every pages (src/pages/*.tsx):

<Head>
  <script src="/api/config" defer />
</Head>

Now, you can directly access the config via window.PUBLIC_CONFIG.*:

import { getPublicConfig } from 'pages/api/config';

// call-site, agents.js
const restaurantApi = axios.createConfig({
  baseUrl: getPublicConfig('restaurantApi'),
});

Pros

  • De-couple with build-time environment variables, just build once for production bundle.
  • You could use the same production bundle with different public runtime configurations in deploys.
  • It works for Server, Static and SSG pages as long as the config is present.
  • Usage is simple, you could access the config via window.PUBLIC_CONFIG.*.

Cons

@ankit-sapkota555
Copy link

Is there anything that I need to add in app router?

I am unable to access the variables in browser console.

@jihchi
Copy link
Author

jihchi commented Apr 15, 2024

Please be aware that the context of this document is the Pages Router.

@ankit-sapkota555
Copy link

Thank you for the response @jihchi, I was able to achieve this in app router as well with the following changes.

In root /layout.tsx:

// Added Script from next/script
 <Script src="/api/config" defer />

And changing the content-type of the api response to be application/javascript.

@ankit-sapkota555
Copy link

Hey @jihchi, I ran into a issue when I have to pass the env var to RTK Query initialization, although it works for those to used later after initialization.

Is there a way to make this script to load before RTK initialization in NextJS app router.

@jihchi
Copy link
Author

jihchi commented Apr 19, 2024

@ankit-sapkota555 It would be helpful if you can provide a minimum reproduction repository to demonstrate the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment