DEV Community

vavilov2212
vavilov2212

Posted on • Edited on

Sentry Dynamic Environment at Runtime on Client with Next.js

This article explores how to configure the Sentry SDK with environment-specific information in a Next.js application.

Exposition

Sentry understands and reads the following environment variables, so you don’t have to explicitly set them in your Sentry SDK init payload (refer to Sentry Docs - Common Config Options):

  • SENTRY_DSN
  • SENTRY_ENVIRONMENT
  • SENTRY_RELEASE

The Challenge with .env variables:

With Next.js, this only works for server side, because the framework DOES NOT expose .env variables on client for security reasons (refer to Next.js Docs - Bundling Environment Variables for the Browser).

Sentry even states this in the environment section of the configuration docs.

In order to distinguish Sentry traces between staging environments on client we need to explicitly set the environment key in the Sentry SDK init payload to a desired value.

Remember, NEXT_PUBLIC variables are hardcoded into JS bundle at build time

// sentry.client.config.js
...

Sentry.init({
  ...

  /*
   * The current environment of your application (e.g. "production").
   * Must be explicitly set, as SENTRY_ENVIRONMENT .env variable is not exposed on client
   */
  environment: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,

  ...
});
Enter fullscreen mode Exit fullscreen mode

As we deploy different staging environments - we need to assign actual names of those environments to our Sentry SDK environment key.

Possible Solutions

Currently there’s no way to natively achieve this in Next.js and people have been struggling with this (check out Github issue - Init with environment config not working)

The official recommended way by Next.js is stated in their documentation Next.js docs - Bundling Environment Variables for the Browser.

If you need access to runtime environment values, you'll have to setup your own API to provide them to the client (either on demand or during initialization).

However, this approach doesn’t work for the Sentry Browser SDK as it's initiated during build time and runs before first components mount (as far as i understand).

But there are multiple other ways to achieve this:

  • Have multiple builds for each environment - While technically possible, having separate builds for each environment is cumbersome and inefficient and is simply a bad practice (read Build Once, Deploy Many)
  • Init Client Sentry SDK in Layout Component
  • Point the tunnel option of Sentry SDK to a proxy and set init values there (found in comments of the discussion from previous entry, but yet to be explored).
  • Replace “inlined” NEXT_PUBLIC variables values before stating the application - this is what i ended up utilising in commercial production

Recommended Method (Workaround)

The best practice so far is to actually run through JS bundle files right before starting the application and manually replacing values of NEXT_PUBLIC variables “inlined” in the code like described in multiple sources already:

Implementation

Prerequisites

Set NEXT_PUBLIC_SENTRY_ENVIRONMENT .env variable to any value you like as placeholder for future replacement. Make sure it’s unique, since that’s whats going to be searched and replaced.

# .env

NEXT_PUBLIC_SENTRY_ENVIRONMENT=NEXT_PUBLIC_SENTRY_ENVIRONMENT
Enter fullscreen mode Exit fullscreen mode

Search and Replace

Add shell script file to the root of your sources, it executes when running the container. It replaces the placeholder value you've set in previous step with the name of your actual staging environment before starting the application.

# replace_public_env_vars_and_start_server.sh

#!/bin/sh

`find . -type f  -exec sed -i "s/NEXT_PUBLIC_SENTRY_ENVIRONMENT/$SENTRY_ENVIRONMENT/g" {} +`

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
# CMD ["node", "server.js"]
node server.js
Enter fullscreen mode Exit fullscreen mode

Be aware, that in order to successfully run, the script requires write permissions to files and the directory they are in - it must be executed under appropriate role on wherever you're running it.

Build and Run

After building the image - simply point to shell script from the previous step.

I use the official Next.js "with-docker" example file to build and run my applications - the following snippet is the bottom portion of this file with some changes.

# Dockerfile
...

# Make sure it can be executed
RUN ["chmod", "+x", "/app/replace_public_env_vars_and_start_server.sh"]

ENTRYPOINT [ "/app/replace_public_env_vars_and_start_server.sh" ]
Enter fullscreen mode Exit fullscreen mode

Result

In the browser sentry environment is set to different value on each staging server (in this instance - tst)
Image description

Conclusion:

The "replace inlined variables" method offers a practical workaround for achieving environment differentiation in your application, but remember, that:

  • This method involves additional build complexity.
  • It's a workaround, not an officially supported solution by Next.js.
  • It may not work if your deploy environments have limitations on executing scripts or assigning roles under which files are written and/or executed.

Please, let me know in the comments if you're using some other solution to this or have any insight on how to make this one better! 😊 Thanks for reading!

Top comments (0)