firebase-js-sdk icon indicating copy to clipboard operation
firebase-js-sdk copied to clipboard

FR: allow import 'firebase/remote-config' for server side rendering

Open hecateball opened this issue 6 years ago • 23 comments

[REQUIRED] Describe your environment

  • Operating System version: (not related)
  • Browser version: (not related)
  • Firebase SDK version: 7.1.0
  • Firebase Product: Remote Config

[REQUIRED] Describe the problem

Importing 'firebase/remote-config' where the Window object is undefined throws error, thus we have to avoid import 'firebase/remote-config' at server side rendering.

(Specifically, this is quite troublesome for universal web apps built with Nuxt.js. Since this issue occurs when import files using 'firebase/remote-config', it cannot be used in any Vue components, or must use dynamic import and treat Promise somehow...)

It would be highly appreciated if firebase/remote-config run without caching on server side. At least, make it possible to import without the Window object.

Steps to reproduce:

  1. import 'firebase/remote-config' in any javascript code.
  2. run that code wherever the Window object is undefined.

Relevant Code:

https://github.com/firebase/firebase-js-sdk/blob/master/packages/remote-config/index.ts#L48-L51

hecateball avatar Oct 08 '19 03:10 hecateball

@hecateball Thx for the feedback!

Is the goal to fetch config server-side, or just build in the Node env? I ask because the former has product considerations, but the latter might be more straightforward.

If anyone else would benefit from the same, plz upvote using a reaction on the issue.

erikeldridge avatar Oct 24 '19 22:10 erikeldridge

@erikeldridge Sorry for the late reply... My goal is to fetch config server-side (run the same code both server-side and client-side).

Currently, I'm bypassing fetch config server-side (using default config) so the application generates different page for the same URL depending on which environment that page is rendered (server-side/client-side).

I'd like to generate same page both in server-side and in client-side by single code.

hecateball avatar Oct 31 '19 02:10 hecateball

Thx for clarifying!

Unfortunately, fetching server-side isn't a quick change :( For example, the percent condition requires a stable identifier, the location condition considers the caller's IP address, etc. Although it's still JS, it's almost another platform.

I'll leave this issue open so folks can up-vote, which helps us prioritize.

erikeldridge avatar Nov 01 '19 00:11 erikeldridge

@erikeldridge Is it possible to change when an error occurs? It would be good enough if we could import remote-config module server-side without any errors. Instead, fetching config server-side may cause error but we can handle it.

Our biggest problem is that we have to use dynamic import everywhere we use remote-config module and it makes application code messy...

hecateball avatar Nov 03 '19 15:11 hecateball

Any update on this? This is currently a blocker for us using remote config at all

Asherlc avatar May 11 '20 23:05 Asherlc

same @Asherlc

liveHarshit avatar May 11 '21 12:05 liveHarshit

Thx for clarifying!

Unfortunately, fetching server-side isn't a quick change :( For example, the percent condition requires a stable identifier, the location condition considers the caller's IP address, etc. Although it's still JS, it's almost another platform.

I'll leave this issue open so folks can up-vote, which helps us prioritize.

Could the stable identifier be stored in a cookie so it can be used server side and passed into the remote config function?

stnmonroe avatar Jul 13 '21 19:07 stnmonroe

Any update here?

I'm having a similar issue with Next.js. I haven't been able to work around it yet. Locally, I do not have any issue at all, but every time I try to build my application on AWS Amplify the build breaks with the message:

Error [FirebaseError]: Remote Config: Undefined window object. This SDK only supports usage in a browser environment. (remoteconfig/registration-window).

Can anyone help?

Thanks in advance.

sinetodev avatar Jan 13 '22 01:01 sinetodev

@sinetodev here is a client-side Next.js workaround for creating a RemoteConfig Context. It falls back on a local default when not in a client context:

import { createContext, useContext, useState, useEffect } from 'react';
import firebase from '@/lib/firebase';

// This will tighten the requirements on what can be fetched from the config
export interface ConfigValues {
  example: string;
  // define types for your config values here
}

const CONFIG_DEFAULTS: ConfigValues = {
  example: 'local fallback',
  // etc
};

type ConfigKey = keyof ConfigValues;

export interface ConfigContextValue {
  get: (key: ConfigKey) => string | number | boolean;
}

export const ConfigContext = createContext<ConfigContextValue | null>(null);

export function ConfigProvider({ children }) {
  const value = useProvideRemoteConfig();
  return <ConfigContext.Provider value={value}>{children}</ConfigContext.Provider>;
}

export const useRemoteConfig = () => {
  return useContext(ConfigContext);
};

export function useProvideRemoteConfig(): ConfigContextValue {
  const [configClient, setConfigClient] = useState<firebase.remoteConfig.RemoteConfig | null>(null);

  async function initRemoteConfig() {
    const remoteConfig = firebase.remoteConfig();

    // The spread here is required for the more specific type to be compatible with the general dictionary
    remoteConfig.defaultConfig = { ...CONFIG_DEFAULTS };
    await remoteConfig.fetchAndActivate();
    setConfigClient(remoteConfig);
  }

  useEffect(() => {
    if (!configClient) {
      initRemoteConfig();
    }
  }, [configClient]);

  const get = ( key: keyof ConfigValues): string | number | boolean => {
    if (!process.browser) {
      console.warn(
        'Cannot call configContext.get() outside of a browser context. Using local default'
      );
      return CONFIG_DEFAULTS[key];
    }

    // Only needed for type check
    const defaultValue = CONFIG_DEFAULTS[key];
    // This will return the value as the type we want rather than interpreting it as a string
    if (typeof defaultValue === 'boolean') {
      return configClient.getBoolean(key);
    } else if (typeof defaultValue === 'number') {
      return configClient.getNumber(key);
    } else {
      return configClient.getString(key);
    }
  };

  return { get };
}

However this doesn't solve the main issue of server-side rendering -- it would be really great to be able to fetch config state inside getServerSideProps().

eaculb avatar Jan 14 '22 15:01 eaculb

I've used isSupported function provided from firebase/remote-config to get client-side NextJS workaround.

Snippets:

import {
  getRemoteConfig,
  RemoteConfig,
  isSupported as isRemoteConfigSupproted,
} from "firebase/remote-config";

let remoteConfig: RemoteConfig;

function initializeRemoteConfig() {
  isRemoteConfigSupproted().then(supported => {
    if (supported) {
      remoteConfig = getRemoteConfig(firebaseApp);
    }
  });

  return remoteConfig;
}

initializeRemoteConfig()

See Also:

  • https://firebase.google.com/docs/reference/js/v8/firebase.remoteconfig#issupported

dodoongtak avatar Feb 04 '22 08:02 dodoongtak

Updates here? I think this would be valuable

jlongo-encora avatar Mar 28 '22 22:03 jlongo-encora

Same here, since we upgrade our chrome extension to Manifest V3. The remote config won't work anymore.

Not sure why it still marks as compatible in here. :/ https://firebase.google.com/docs/web/environments-js-sdk

lmcmz avatar Apr 23 '22 05:04 lmcmz

Hope there are some updates and plans for making remote-config able to work in node.

This month, firebase itself introduced framework-aware hosting targeting next.js, react is almost going server-first together with next.js with the features and plans announced.

Having SSR frontend applications and being able to use remote-config in server-side, thus being able to serve SSR UI while a/b testing would be great.

berkinanik avatar Oct 29 '22 02:10 berkinanik

Does this have any pointers?

https://firebase.google.com/docs/hosting/nextjs#pre-render_dynamic_content

yoitsro avatar Nov 04 '22 18:11 yoitsro

lol this is a reason for people to use launch darkly or unleash and its so simple to prevent

bare-metal-gpu avatar Nov 26 '22 04:11 bare-metal-gpu

but currently what i do is have a cloud firebase functions trigger onConfigUpdate and just fetch the template via admin sdk and place it in a memory store for me to access

bare-metal-gpu avatar Nov 26 '22 04:11 bare-metal-gpu

In our case, we just need to fetch a property from node and we want to make A/B tests work based on a UID.

How can we replicate the stable percentage in a way that is consistent with what Firebase A/B testing expects?

Obviously everything would fall apart if the server intends for the given uid to get variant a but they actually get variant b.

ciriousjoker avatar Jan 19 '23 23:01 ciriousjoker

not working with ssr

dmitryshelomanov avatar Jan 25 '23 20:01 dmitryshelomanov

For fix it, I added FB controller only on client side

On ssr all configs not available

dmitryshelomanov avatar Jan 26 '23 08:01 dmitryshelomanov

Hi all, are there any updates on this?

I believe this issue relates to:

  • https://github.com/firebase/firebase-js-sdk/issues/6911

Sharing a repo to reproduce the problem on Chrome Extensions using MV3:

  • https://github.com/capybarahero/temp-remote-config-chrome-extension/blob/main/src/pages/background/index.ts

capybarahero avatar Aug 18 '23 14:08 capybarahero

This is absurd lol

spookyuser avatar Sep 21 '23 08:09 spookyuser

There's a related official feature request here, perhaps upvote it: https://firebase.uservoice.com/forums/948424-general/suggestions/47033704-remote-config-for-server-side

ciriousjoker avatar Sep 26 '23 14:09 ciriousjoker

Somehow forgot that this was a thing despite commenting last year but as far as I can tell there's no actual usage of window in the package anyway, except for one place where it seems unnecessary https://github.com/firebase/firebase-js-sdk/blob/aa6db78ebc51da10943e9766a551887e85841fbf/packages/remote-config/src/client/rest_client.ts#L76

spookyuser avatar Oct 08 '24 12:10 spookyuser

This seems to be fixed now, yay https://github.com/firebase/firebase-js-sdk/pull/8699

spookyuser avatar Feb 12 '25 12:02 spookyuser

@spookyuser This is incredible news.

Do you have any knowledge or understanding about which version this should be released in?

And will there be docs updated accordingly?

chrisspiegl avatar Feb 12 '25 12:02 chrisspiegl

I know right, besides that it somehow happened at the exact time I made that pr, I don't know much but I suspect there's nothing to change in the docs really. Without the If(!window){throw} it should just work

spookyuser avatar Feb 12 '25 13:02 spookyuser

#8699 is cool!

It only took 5 years

lesmo avatar Feb 12 '25 22:02 lesmo

Next release is tentatively scheduled for Feb 27, 2025, subject to change.

hsubox76 avatar Feb 14 '25 16:02 hsubox76