Next.js Discord

Discord Forum

Env variables are read by the client and server at the same time

Answered
AM posted this in #help-forum
Open in Discord
AMOP
Hello i do have 2 env variables:

NEXT_PUBLIC_API_BASE_URL=baseUrlApi

NEXTAUTH_SECRET=next_secret


and i can see that in my client component NEXTAUTH_SECRET is logged for some reason, however it comes as null and env validations are throwing error, what could be the reason for that. Is it possible because in my client component i'm using service that internally is using getSession like this:

export const updatePasswordApi = async (data: UpdatePasswordRequestType) => {
  const session = await getSession();

  const response = await fetch(
    `${PUBLIC_API_BASE_URL}/${API_ENDPOINTS.UPDATE_PASSWORD}`,
    {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${session?.user.accessToken}`
      }
    }
  );

  if (!response.ok) {
    throw new Error(API_ERRORS.UPDATE_PASSWORD);
  }

  return response.json();
};


really not sure what is happening and how to handle it
Answered by fuma
that's simple - when you import this from a client component, your whole file will be loaded by client too
View full answer

22 Replies

NEXTAUTH_SECRET shouldn't be passed to the client side otherwise people can easily steal the next auth secret of your app. This is unsafe.
Next.js won't include environment variables without NEXT_PUBLIC_ as a prefix. That's why NEXT_PUBLIC_API_BASE_URL can be accessed on client side while NEXTAUTH_SECRET can't
AMOP
i'm not passing it actually more likely i do have this:
'use client';

const SignUp = () => { 
  ...code...
  const onSubmit = async (values: RegisterDataType) =>      {
      const res = await registerApi(values);
      console.log('res', res);
    };
  ...code...
}


and in my service:

export const registerApi = async (data: RegisterRequestType) => {
  const response = await fetch(
    `${PUBLIC_API_BASE_URL}/${API_ENDPOINTS.REGISTER}`,
    {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json'
      }
    }
  );

  if (!response.ok) {
    throw new Error(API_ERRORS.REGISTER);
  }

  return response.json();
};


actually not passing it, i dont understand why is failing is not used anywhere NEXTAUTH_SECRET secret tho
in the scenario above does this PUBLIC_API_BASE_URL needs to be prefixed as public, i do have standalone service that is used in client component?
interesting thing is that i can see errors start coming when i import my registerApi in my SignUp client component
and my registerApi is located here

index.ts of auth folder
any idea what possible could fail how this chain is happening
importing api from index.ts is start consuming NEXTAUTH_SECRET for some reason
AMOP
ok further more i think i have isolated even more the problem
i do have this import in my services:

import { PUBLIC_API_BASE_URL } from '@/app/lib;

lib/index.ts:

export * from './env';
export * from './utils';


and then in env.ts i do have this:

import { nonNull } from './utils';


export const PUBLIC_API_BASE_URL = nonNull<string>(
  process.env.NEXT_PUBLIC_API_BASE_URL,
  `env of "NEXT_PUBLIC_API_BASE_URL" is not defined. Did you forget a definition in the ".env.local"?`
);

export const NEXTAUTH_SECRET = nonNull<string>(
  process.env.NEXTAUTH_SECRET,
  `env of "NEXTAUTH_SECRET" is not defined. Did you forget a definition in the ".env.local"?`
);
somehow when i import the public api base url to my service next auth is coming too
is it somehow because of the barrel imports hmm
that's simple - when you import this from a client component, your whole file will be loaded by client too
Answer
Then nonNull throw an error, causing the problem you mentioned
create another file for it, like env-server for example
AMOP
ok but from the file i'm loading just import { PUBLIC_API_BASE_URL } from '@/app/lib

why is messing with the NEXTAUTH_SECRET
is true like intelisense gives me to import NEXTAUTH_SECRET so the whole file is loaded true
Because it is same as:
nonNull(process.env.NEXT_PUBLIC)
nonNull(process.env.SECRET)


It is executed when script is loaded
you can throw errors at top-level
AMOP
axx i see
and for the components that are client side i will need to have process.env.NEXT_PUBLIC_API_BASE_URL along with process.env.API_BASE_URL

where process.env.NEXT_PUBLIC_API_BASE_URL goes in env-client.ts and other one goes into env-server.ts and based on the component ( in most cases server but right now submitting form ) they will be imported from env-server.ts
You can store shared envs in env-shared IMO
AMOP
but isn't this like the scenario right now where i do have env.ts and contains both server and client and when client env is imported into client component server ones are triggered as well