firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.82k stars 884 forks source link

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

Open hecateball opened 4 years ago

hecateball commented 4 years ago

[REQUIRED] Describe your environment

[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

erikeldridge commented 4 years ago

@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.

hecateball commented 4 years ago

@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.

erikeldridge commented 4 years ago

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.

hecateball commented 4 years ago

@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...

Asherlc commented 4 years ago

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

liveHarshit commented 3 years ago

same @Asherlc

stnmonroe commented 3 years ago

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?

sinetodev commented 2 years ago

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.

eaculb commented 2 years ago

@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().

dodoongtak commented 2 years ago

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:

jlongo-encora commented 2 years ago

Updates here? I think this would be valuable

lmcmz commented 2 years ago

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

berkinanik commented 1 year ago

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.

yoitsro commented 1 year ago

Does this have any pointers?

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

bare-metal-gpu commented 1 year ago

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

bare-metal-gpu commented 1 year ago

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

ciriousjoker commented 1 year ago

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.

dmitryshelomanov commented 1 year ago

not working with ssr

dmitryshelomanov commented 1 year ago

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

On ssr all configs not available

capybarahero commented 1 year ago

Hi all, are there any updates on this?

I believe this issue relates to:

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

spookyuser commented 11 months ago

This is absurd lol

ciriousjoker commented 11 months ago

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