Not able to access env variables in Remix, even in a server file

954 Views Asked by At

I was using Shopify's version of Remix and ran into an issue where I tried to access an API key to be used in a newsletter signup form. However, I cannot access my env variables due to the error process is not defined.

app/env/env.server.ts:

import {ZodFormattedError} from 'zod';

const serverSchema = z.object({
  KLAVIYO_API_KEY: z.string(),
  KLAVIYO_GLOBAL_LIST_ID: z.string(),
  KLAVIYO_API_VERSION: z.string(),
})

const _serverEnv = serverSchema.safeParse(process.env);

const formatErrors = (errors: ZodFormattedError<Map<string, string>, string>) =>
  Object.entries(errors)
    .map(([name, value]) => {
      if (value && '_errors' in value)
        return `${name}: ${value._errors.join(', ')}\n`;
    })
    .filter(Boolean);

if (_serverEnv.success === false) {
  // eslint-disable-next-line no-console
  console.error(
    '❌ Invalid environment variables:\n',
    ...formatErrors(_serverEnv.error.format()),
  );

  throw new Error('Invalid environment variables');
}

export const env = {..._serverEnv.data};

routes/newsletter/subscribe.tsx:

import {ActionArgs, json} from '@shopify/remix-oxygen';
import { env } from '../../env/env.server';

export async function action({request}: ActionArgs) {
  const email = (await request.formData()).get('email')?.toString() ?? '';
  try {
    const res = await subscribeToKlaviyoList(email);

    return res.json();
  } catch (error) {
    return json({error: error.message, ok: false});
  }
}



export async function subscribeToKlaviyoList(
  email: string,
  source = 'Home Page Footer',
) {
  const API_KEY = env.KLAVIYO_API_VERSION;
  const API_VERSION = env.KLAVIYO_API_VERSION;
  const LIST_ID = env.KLAVIYO_GLOBAL_LIST_ID;

  return await fetch(
    'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs/',
    {
      method: 'POST',
      headers: {
        accept: 'application/json',
        revision: API_VERSION ?? '2023-01-24',
        'content-type': 'application/json',
        Authorization: `Klaviyo-API-Key ${API_KEY}`,
      },
      body: JSON.stringify({
        data: {
          type: 'profile-subscription-bulk-create-job',
          attributes: {
            list_id: LIST_ID,
            custom_source: source,
            subscriptions: [
              {
                channels: {email: ['MARKETING']},
                email,
              },
            ],
          },
        },
      }),
    },
  );
}

I've followed along with this example from Remix and this GitHub repo, but in both instances, they either aren't using env variables or the API key is hard coded. Any help is greatly appreciated.

1

There are 1 best solutions below

0
On

You must add the env variables to the Env type in the remix.env.d.ts file. So I solved this by doing the following:

interface Env extends z.infer<typeof serverSchema> {
    SESSION_SECRET: string;
    PUBLIC_STOREFRONT_API_TOKEN: string;
    PRIVATE_STOREFRONT_API_TOKEN: string;
    PUBLIC_STOREFRONT_API_VERSION: string;
    PUBLIC_STORE_DOMAIN: string;
    PUBLIC_STOREFRONT_ID: string;
  }

And then calling the env variable as shown:

export async function action({context, request}: ActionArgs) {
  const email = (await request.formData()).get('email')?.toString() ?? '';
  try {
    await subscribeToKlaviyoList(context.env, email);
    return json({error: null, ok: true});
  } catch (error) {
    return json({error: error.message, ok: false});
  }
}

export async function subscribeToKlaviyoList(
  env: Env,
  email: string,
  source = 'Home Page Footer',
) {
  const API_KEY = env.KLAVIYO_API_KEY;
  const API_VERSION = env.KLAVIYO_API_VERSION;
  const LIST_ID = env.KLAVIYO_GLOBAL_LIST_ID;

  return await fetch(
    'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs/',
    {
      method: 'POST',
      headers: {
        accept: 'application/json',
        revision: API_VERSION ?? '2023-01-24',
        'content-type': 'application/json',
        Authorization: `Klaviyo-API-Key ${API_KEY}`,
      },
      body: JSON.stringify({
        data: {
          type: 'profile-subscription-bulk-create-job',
          attributes: {
            list_id: LIST_ID,
            custom_source: source,
            subscriptions: [
              {
                channels: {email: ['MARKETING']},
                email,
              },
            ],
          },
        },
      }),
    },
  );
}

Hopefully, we don't need to use context in the future.