supabase-js icon indicating copy to clipboard operation
supabase-js copied to clipboard

window is not defined error when creating supabase client in nodejs server side

Open Vichet97 opened this issue 1 year ago • 32 comments

Bug report

  • [x ] I confirm this is a bug with Supabase, not with my own application.
  • [ ] I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

I try to create client in nodejs but always get window is not defined error as attached below. Then when i try to debug the source code through stack trace, it turned on that isBrowser() return a True value. But when i manually set it to false, it will work.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. create a fastify​ project
  2. import and createClient(host, key, {db: {schema: abc}​}
  3. run the server with node index.js
  4. Got error as attached

Screenshots

This is the error IMG_20230606_170256_892

Then i debug and got True value when printing isBrowser() which it should be False. I manually change code here then it works.

IMG_20230606_170300_006

System information

  • OS: macOS monetary
  • Version of supabase-js: 2.24.0, I also tried with some older version in Supabase 2. It's the same.
  • Version of Node.js: 14

Vichet97 avatar Jun 06 '23 13:06 Vichet97

I commented on a Discord post for this same thing. Maybe it was yours.

In Discord I made the comment that this checks document now, not window. But I made the mistake of thinking it was gotrue-js code. I just looked in supabase-js and they have the same helper function, but still use window.

This likely can be changed to match gotrue-js code, but I'll let the experts decide that.

j4w8n avatar Jun 07 '23 00:06 j4w8n

Yes, that's mine. But posting here will be more visible to other people who have the same issue. So when will it be fixed ? Else can you show me where to change this, i can make a temporary fork

Vichet97 avatar Jun 07 '23 00:06 Vichet97

I'm not sure when or even if it'll be fixed because the maintainers need to decide if it's something that should be fixed.

Code is https://github.com/supabase/supabase-js/blob/master/src/lib/helpers.ts#L16

j4w8n avatar Jun 07 '23 00:06 j4w8n

Why is there a question whether it should be fixed when it just doesn't work and not usable?

Vichet97 avatar Jun 07 '23 02:06 Vichet97

I've been unable to replicate this issue. I've tried in the same version of Node you are using (which is no longer supported by Node themselves btw) and I still didn't get any error. The code I'm using is below.

const { createClient } = require("@supabase/supabase-js");

const fastify = require("fastify")({ logger: true });

const supabase = createClient(
  "http://localhost:54321",  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0",
  { db: { schema: "abc" } }
);

fastify.get("/", async (request, reply) => {
  const { data } = await supabase.from("countries").select("*");
  return { hello: "world", countries: data };
});

// Run the server!
const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

silentworks avatar Jun 07 '23 10:06 silentworks

During my nice insomnia overnight, I went back to the screenshot and realized this is referencing gotrue-js code, not supabase-js.

j4w8n avatar Jun 07 '23 11:06 j4w8n

@j4w8n but the issue mentions the latest @supabase/supabase-js which is what I tested and this also makes use of the gotrue code underneath. Were you able to replicate this by any chance?

silentworks avatar Jun 07 '23 11:06 silentworks

@j4w8n but the issue mentions the latest @supabase/supabase-js which is what I tested and this also makes use of the gotrue code underneath. Were you able to replicate this by any chance?

I haven't had a chance to try it yet. Might in a few hours.

j4w8n avatar Jun 07 '23 11:06 j4w8n

It's from gotrue-js. But I also have many other dependencies installed like firestore, etc. Or maybe the document variable is just used for other purposes but not in my code. It might have made the document != undefined since gotrue only check that variable to identify browser. It's not the right way to check. But surely, that document is not the document in browser.

Vichet97 avatar Jun 07 '23 14:06 Vichet97

Please provide a minimal reproducible repository for us to test as we aren't able to replicate the issue you are having here. Also you should be testing this as a standalone project and not a mix with other libraries to be sure where the issue is from. Please upgrade your Node version too as 14 is no longer supported and has gone pass its EOL.

silentworks avatar Jun 07 '23 15:06 silentworks

I found the issue. It will show error if you declare const a = { document } = { document: true }​ in another js file then require('other.js')​ in the same js file where you call that Supabase createClient.

However, it doesn't have any problem if you just declare const a = { document } = { document: true }​ in the same file

Vichet97 avatar Jun 08 '23 03:06 Vichet97

I found the issue. It will show error if you declare const a = { document } = { document: true }​ in another js file then require('other.js')​ in the same js file where you call that Supabase createClient.

However, it doesn't have any problem if you just declare const a = { document } = { document: true }​ in the same file

@Vichet97 out of curiosity, what's your use case for this?

j4w8n avatar Jun 08 '23 10:06 j4w8n

It's a property in Firestore and MassiveJS. So I declared some default properties with default values to setup structure in my project. It was not a plain property with only document field. There are other fields as well but it was just an example

Vichet97 avatar Jun 08 '23 12:06 Vichet97

Anyway, that isBrowser function is not significant to detect whether it's a browser.

Vichet97 avatar Jun 08 '23 12:06 Vichet97

What do you think would work better?

j4w8n avatar Jun 08 '23 12:06 j4w8n

Can check typeof window == "object" then verify some of its default properties ?

Vichet97 avatar Jun 08 '23 13:06 Vichet97

There were issues with window, because it exists in React Native and was causing problems; so they changed it to document.

At this point I'm wondering if what happened to you is more of an edge case (I haven't seen/heard of any other tickets with a similar scenario), but maybe something like this? There's a precedent for similar code in the gotrue-js repo here

-export const isBrowser = () => typeof document !== 'undefined'
+export const isBrowser = () => typeof document !== 'undefined' && document.addEventListener

j4w8n avatar Jun 08 '23 14:06 j4w8n

Well, can use document but should do the same as i mentioned above like window

Vichet97 avatar Jun 08 '23 15:06 Vichet97

I am also experiencing this issue. I am trying to use supabase inside an airplane task and I am getting this error: Screenshot 2023-10-04 at 16 53 44

This is the code that I have:

import { createClient } from "@supabase/supabase-js";
import airplane from "airplane";

export default airplane.task(
  {
    slug: "list_users",
    name: "list_users",
  },
  async () => {
    const supabase = createClient(
      "redacted url",
      "redacted service key",
      {
        auth: {
          autoRefreshToken: false,
          persistSession: false,
        },
      },
    );

    const { data } = await supabase.auth.admin.listUsers();
    return data.users;
  },
);

DanielSepulveda avatar Oct 04 '23 22:10 DanielSepulveda

@DanielSepulveda Experiencing a similar issue with an Airplane.dev task and wondering if it's how the process loads - it's interesting that the !isBrowser() doesn't short circuit ahead of the actual window check

schybo avatar Oct 10 '23 10:10 schybo

Also having this issue in react-native with the latest version of expo, following the user management expo supabase tutorial

julian-hecker avatar Oct 21 '23 01:10 julian-hecker

Hey, I'm having a similar issue I think. I'm using expo with supabase and instantiate the client like so:

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: AsyncStorage,
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

On iOS and Android that works fine but on web I get the following error:

ReferenceError: window is not defined
    at http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:192612:9
    at http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:192586:21
    at new Promise (<anonymous>)
    at createPromise (http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:192584:12)
    at Object.getItem (http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:192611:14)
    at http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:42084:33
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:1462:26)
    at _next (http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:1481:11)
    at http://localhost:8081/node_modules/expo-router/node/render.bundle//&platform=web&dev=true&minify=false&resolver.environment=node&transform.environment=node:1486:9

In react native community async storage they even have added web as a feature which looks like it would solve my issue: https://github.com/react-native-async-storage/async-storage/releases/tag/v1.9.0

But react native community async storage is deprecated so Im wondering whether this is expected to not work in the current async storage. Im a beginner with expo and supabase and all that stuff so I would appreciate some input :)

clemwo avatar Nov 03 '23 13:11 clemwo

I found a solution which should probably also work for the issue @julian-hecker is describing. I got help from a colleague and we found out that the issue is caused by localStorage being undefined during bundling with metro.

A workaround is to write a custom class that implements getItem, setItem & removeItem that simply returns null if localStorage is undefined (and returns AsyncStorage on mobile)

class SupabaseStorage {
  async getItem(key: string) {
    if (Platform.OS === "web") {
      if (typeof localStorage === "undefined") {
        return null;
      }
      return localStorage.getItem(key);
    }
    return AsyncStorage.getItem(key);
  }
  async removeItem(key: string) {
    if (Platform.OS === "web") {
      return localStorage.removeItem(key);
    }
    return AsyncStorage.removeItem(key);
  }
  async setItem(key: string, value: string) {
    if (Platform.OS === "web") {
      return localStorage.setItem(key, value);
    }
    return AsyncStorage.setItem(key, value);
  }
}

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: new SupabaseStorage(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

We found a related issue here: https://github.com/expo/expo/issues/23895 And the fix in this commit https://github.com/expo/expo/commit/1277abdd1ecb21aa43b22a48282439c97b1c1798

Note: we haven't tested this for a long time but I just wanted to share this quick fix in case it helps

clemwo avatar Nov 03 '23 15:11 clemwo

Also having this issue in react-native with the latest version of expo, following the user management expo supabase tutorial

I'm running into this same issue

aydahealth avatar Dec 01 '23 02:12 aydahealth

I found a solution which should probably also work for the issue @julian-hecker is describing. I got help from a colleague and we found out that the issue is caused by localStorage being undefined during bundling with metro.

A workaround is to write a custom class that implements getItem, setItem & removeItem that simply returns null if localStorage is undefined (and returns AsyncStorage on mobile)

class SupabaseStorage {
  async getItem(key: string) {
    if (Platform.OS === "web") {
      if (typeof localStorage === "undefined") {
        return null;
      }
      return localStorage.getItem(key);
    }
    return AsyncStorage.getItem(key);
  }
  async removeItem(key: string) {
    if (Platform.OS === "web") {
      return localStorage.removeItem(key);
    }
    return AsyncStorage.removeItem(key);
  }
  async setItem(key: string, value: string) {
    if (Platform.OS === "web") {
      return localStorage.setItem(key, value);
    }
    return AsyncStorage.setItem(key, value);
  }
}

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: new SupabaseStorage(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

We found a related issue here: expo/expo#23895 And the fix in this commit expo/expo@1277abd

Note: we haven't tested this for a long time but I just wanted to share this quick fix in case it helps

This worked for me on web builds. thanks

glanikali avatar Dec 05 '23 23:12 glanikali

I found a solution which should probably also work for the issue @julian-hecker is describing. I got help from a colleague and we found out that the issue is caused by localStorage being undefined during bundling with metro.

....

Worked for me as well

indiyon avatar Dec 09 '23 18:12 indiyon

@clemwo your solution worked for me! Thanks!

PabloVallejo avatar Dec 24 '23 17:12 PabloVallejo

Thanks all. The solution that worked for me is this one:

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
    auth: {
        // https://github.com/supabase/supabase-js/issues/870
        ...(Platform.OS !== 'web' ? { storage: AsyncStorage } : {}),
        autoRefreshToken: true,
        persistSession: true,
        detectSessionInUrl: false,
    },
});

Only set storage if the platform isn't web. It defaults to localstorage if you just don't set it. Have a look at this bug https://github.com/supabase/supabase-js/issues/870

julian-hecker avatar Dec 28 '23 19:12 julian-hecker

All solutions here seem to be framework-specific workarounds but aren't addressing the issue at its core and for all users. isBrowser() should be a more elaborate check than just just typeof document !== 'undefined'.

Maybe even just typeof document !== 'undefined' && typeof window === 'object'?

salmoro avatar Feb 12 '24 16:02 salmoro

I LOVE YOU! Thanks this fixed the issue.

...(Platform.OS !== 'web' ? { storage: AsyncStorage } : {}),

artypineda avatar Apr 26 '24 23:04 artypineda