supabase-js
supabase-js copied to clipboard
window is not defined error when creating supabase client in nodejs server side
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:
- create a fastify project
- import and createClient(host, key, {db: {schema: abc}}
- run the server with node index.js
- Got error as attached
Screenshots
This is the error
Then i debug and got True value when printing isBrowser() which it should be False. I manually change code here then it works.
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
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.
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
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
Why is there a question whether it should be fixed when it just doesn't work and not usable?
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();
During my nice insomnia overnight, I went back to the screenshot and realized this is referencing gotrue-js code, not supabase-js.
@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?
@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.
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.
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.
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
I found the issue. It will show error if you declare
const a = { document } = { document: true }
in another js file thenrequire('other.js')
in the same js file where you call that SupabasecreateClient
.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?
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
Anyway, that isBrowser
function is not significant to detect whether it's a browser.
What do you think would work better?
Can check typeof window == "object"
then verify some of its default properties ?
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
Well, can use document
but should do the same as i mentioned above like window
I am also experiencing this issue. I am trying to use supabase inside an airplane task and I am getting this error:
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 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
Also having this issue in react-native with the latest version of expo, following the user management expo supabase tutorial
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 :)
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
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
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
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
@clemwo your solution worked for me! Thanks!
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
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'
?
I LOVE YOU! Thanks this fixed the issue.
...(Platform.OS !== 'web' ? { storage: AsyncStorage } : {}),