mongodb / stitch-js-sdk

MongoDB Stitch JavaScript SDK
Apache License 2.0
113 stars 67 forks source link

window is not defined #186

Open xxyyzz2050 opened 5 years ago

xxyyzz2050 commented 5 years ago

I'm using babel transpiler

function StitchAuthImpl(requestClient, browserAuthRoutes, authStorage, appInfo, jsdomWindow) {
        if (jsdomWindow === void 0) { jsdomWindow = window; }
        ...
}

//error: window is not defined

jsflax commented 5 years ago

Hi @xxyyzz2050 ,

Could you give us a bit more information? What step in the transpiration process are you at? What is the full stack trace?

Thanks.

xxyyzz2050 commented 5 years ago

ok, I use Nuxt framework & latest version of Node.js and npm

maredudd commented 5 years ago

@jsflax I imagine that this might be due to using the SDK for SSR. I'm having the same issue with Next.js.

If fails on this line: client.auth.loginWithCredential(new AnonymousCredential()):

ReferenceError: window is not defined
    at new StitchAuthImpl (/xxx/node_modules/mongodb-stitch-browser-core/dist/cjs/core/auth/internal/StitchAuthImpl.js:28:39)
    at new StitchAppClientImpl (/xxx/node_modules/mongodb-stitch-browser-core/dist/cjs/core/internal/StitchAppClientImpl.js:15:21)
    at Function.Stitch.initializeAppClient (/xxx/node_modules/mongodb-stitch-browser-core/dist/cjs/core/Stitch.js:71:22)

This would be expected since window is undefined on the server side. I tried using the server SDK to retrieve the data on the server side but that fails since fs doesn't exist This results in the following code:

import {
  Stitch,
  RemoteMongoClient,
  AnonymousCredential
} from 'mongodb-stitch-server-sdk'

showing an error due to the missing dependency:


* fs in ./node_modules/fs-extra/lib/index.js, ./node_modules/fs-extra/lib/empty/index.js and 2 others

Are the SDKs expected to support SSR? Is there a workaround for this or is Stitch not an option with SSR?

Thanks.

agstover commented 5 years ago

I've been trying to figure out how Stitch can be used with SSR as well, particularly NextJS. Would one have to instantiate both a Stitch-Server SDK instance and a Stitch-Browser SDK instance, use the server instance if !window and use the browser instance if there is?

This also presents an issue with setting up sessions so that initial requests for logged in users can be server rendered. As an example of an alternative Auth flow, the official NextJS + Firebase Auth example works like this: 1) Login occurs client-side, 2) Request an Auth Token from Firebase client-side, 3) Pass Auth token to /login.js route, 4) Use Firebase to decode token, 5) Set HTTP Only cookie session with user info. Every subsequent request is immediately authenticated.

I'm not sure how this can be done with Stitch. Auth Token gets stored in local storage, but is there a way to get it? And if you could deliver it to the server, could you use it with Stitch-Server SDK to authenticate user server side and create a session?

Ruegen commented 5 years ago

I'm getting the same issue with Next.js client.auth.loginWithCredential(new AnonymousCredential()) I'm guessing it needs access to the window object which won't be available since it's pre-rendered?

Ruegen commented 5 years ago

I've been trying to figure out how Stitch can be used with SSR as well, particularly NextJS. Would one have to instantiate both a Stitch-Server SDK instance and a Stitch-Browser SDK instance, use the server instance if !window and use the browser instance if there is?

This also presents an issue with setting up sessions so that initial requests for logged in users can be server rendered. As an example of an alternative Auth flow, the official NextJS + Firebase Auth example works like this: 1) Login occurs client-side, 2) Request an Auth Token from Firebase client-side, 3) Pass Auth token to /login.js route, 4) Use Firebase to decode token, 5) Set HTTP Only cookie session with user info. Every subsequent request is immediately authenticated.

I'm not sure how this can be done with Stitch. Auth Token gets stored in local storage, but is there a way to get it? And if you could deliver it to the server, could you use it with Stitch-Server SDK to authenticate user server side and create a session?

I used a stateful component that waits until the mount lifecycle (I'm using react hooks, useEffect) which only loads once the component mounts. This solves the problem for me in Next.js.. won't help with anything server side rendering...

jsflax commented 5 years ago

In the future, we hope to have a universal JS SDK. However, in the interim, @agstover is right. A solution could be to make your own isomorphic multiplexer file, i.e., an isomorphic-stitch.js file that does if node return nodesdk else browser sdk.

xxyyzz2050 commented 5 years ago

Thank you @maredudd , yes the issue is due to SSR mode, thanks to you I now knew that there is a server version for SSR and I will try it

@jsflax good idea

phillipmalboeuf commented 5 years ago

+1 for SSR support

Ruegen commented 5 years ago

Trying to use Next.js with Stitch requires SSR support. Even one of the fetch dependancies isn't written for SSR so an alternative to whatwg-fetch would be better?

adamchel commented 5 years ago

Hi @phillipmalboeuf and @Ruegen , have you tried using mongodb-stitch-server-sdk? It's almost the same as the browser SDK, but it uses node-fetch instead of whatwg-fetch, but excludes some redirect auth functionality.

Let us know if that would be sufficient. Otherwise, we can look into offering a more general solution.

The problem with bundling a polymorphic fetch library in the browser SDK is the fact that some JS frameworks (such as NativeScript) will not let you bundle JavaScript libraries that contain NodeJS-based dependencies. See https://github.com/mongodb/stitch-js-sdk/issues/207

phillipmalboeuf commented 5 years ago

Thanks @adamchel, using mongodb-stitch-server-sdk is not the solution, it's difficult to have to juggle between two librairies. Even having something like this gives me a ReferenceError: self is not defined error:

export const client = process.env.production
  ? require('mongodb-stitch-server-core').Stitch.initializeDefaultAppClient('***')
  : require('mongodb-stitch-browser-core').Stitch.initializeDefaultAppClient('***')
export const db = client.getServiceClient(process.env.production
  ? require('mongodb-stitch-server-services-mongodb-remote').RemoteMongoClient.factory
  : require('mongodb-stitch-browser-services-mongodb-remote').RemoteMongoClient.factory, '***').db('***')

The current system setup isn't SSR capable, and it's not just a matter of the fetch library. I believe the credentials storage method would need to be polymorphic as well. I'll see if I can open a PR sometime this week and do deeper dive:)

Update Would axios be fine as a polymorphic fetch library? NativeScript seems fine with it: https://www.nativescript.org/blog/make-http-requests-to-remote-web-services-in-a-nativescript-vue-app

Ruegen commented 5 years ago

Hi @adamchel, yes I’ve tried it however like Phillip I got the same errors. I got around it by only loading the package in on the client side - which involves a fair bit of trickery (not pretty), the server side can’t use the client side and vice versa. It would be better to have it load for SSR which would resolve a great deal of issues.

@phillipmalboeuf I believe the client side uses localstorage to save the credentials automatically.

adamchel commented 5 years ago

Thanks for the feedback everyone!

So, it's hard for us to provide truly isomorphic networking and storage because new javascript libraries with new requirements and nuances pop up all the time, and it's difficult for us to keep up. In the past we actually switched to an isomorphic fetch library to support more JS libraries, but then it caused problems with other JS libraries. This is just the state of the JS ecosystem 🤷‍♂️. That's why we've generally opted to provide the most basic storage and networking capabilities.

If you'd like to go on a deeper dive, it might be useful for you to know that we have abstracted the storage and network layer of the SDK into simple Transport and Storage protocols.

See:

If you know the limitations/requirements of the framework you're working with, you can implement a Transport or Storage that works for your environment, and specify it when configuring your Stitch client.

import { Stitch, StitchAppClientConfiguration } from "mongodb-stitch-browser-sdk";

// these currently do not exist
import { IsomorphicNetworkTransport } from "my-stitch-isomorphic-network-transport";
import { IsomorphicStorage } from "my-stitch-isomorphic-storage";

let client = Stitch.initializeAppClient(
    "your-client-app-id", 
    new StitchAppClientConfiguration.Builder()
        .withTransport(new IsomorphicNetworkTransport())
        .withStorage(new IsomorphicStorage())
        .build()
);

You can look at our existing Storage and Transport implementations as a reference, and we'd be happy to answer any questions you may have or any issues that come up:

image

image

Although this is something we'd personally like to work more on, we are juggling many other priorities. Building your own Transport and Storage (and opening a PR if you'd like) will probably be the fastest way to get the SDK working in some of these third party libraries where isomorphic libraries are necessary.

If you'd like to have a say in Stitch's product roadmap and the features that we should work on, please add a new issue or vote on existing issues at https://mongodb.canny.io/mongodb-stitch !

phillipmalboeuf commented 5 years ago

Oh interesting @adamchel, didn't realize the withTransport/withStorage configurations existed! Will take a look and keep you updated.

phillipmalboeuf commented 5 years ago

@adamchel The problem is that even importing the packages mongodb-stitch-server-core or mongodb-stitch-browser-core causes problems with SSR. And without them, we can't call the initializeDefaultAppClient function no matter the Storage or Transport implementation.

With that said I've tried building a client still with new CoreStitchAppClient(), but it looks like there is no implementation of StitchAuthRequestClient in the core package, so hit a wall with that and not sure how to proceed:(

phillipmalboeuf commented 5 years ago

Is there a way to use StitchAppClientConfiguration without having to use mongodb-stitch-server-core or mongodb-stitch-browser-core?

adamchel commented 5 years ago

What are the SSR issues when just using mongodb-stitch-server-core or mongodb-stitch-browser-core? Is it the dependencies?

phillipmalboeuf commented 5 years ago

@adamchel the dependencies are a good place to start. So I wrote this, might be the best way to test SSR compability (a .tsx/.jsx file):

import { Stitch } from 'mongodb-stitch-browser-sdk'
import React from 'react'
import ReactDOM from 'react-dom/server'

const client = Stitch.initializeDefaultAppClient('****')

const html = ReactDOM.renderToString(<div>
  {client.auth.isLoggedIn.toString()}
</div>)

console.log(html)

Running this in node returns a ReferenceError: self is not defined error from the whatwg-fetch package but I suspect it might not be the only culprit here.

phillipmalboeuf commented 5 years ago

Hey @adamchel, @Ruegen, @maredudd, @agstover, so I got to a weird solution for this. It needs a couple of steps (note that I'm writing TypeScript and bundling with Parcel):

First, change all your references to the stitch sdk to 'mongodb-stitch-server-sdk'. Stick with me here:p

Second, in your package.json add an alias to point the sdk back to the browser:

{...,
  "alias": {
    "mongodb-stitch-server-sdk": "mongodb-stitch-browser-sdk"
  },
  {
    ...,
    "mongodb-stitch-browser-sdk": "^4.3.2",
    "mongodb-stitch-server-sdk": "^4.3.2",
    ...
  },
  ...
}

Third, if you're on TS add to your tsconfig.json an alias path:

{
  "compilerOptions": {
    ...,
    "baseUrl": ".",
    "paths": {
      "mongodb-stitch-server-sdk": ["node_modules/mongodb-stitch-browser-sdk"]
    }
  }
}

And VOILÀ, bundling this for the browser and bundling this for Node and running those works just fine! This works because unlike bundling for the browser where the packages are all merged in, Node keeps the packages in a node_modules folder, and retrieves them from there. So Node looks for mongodb-stitch-server-sdk while the browser gets the aliased mongodb-stitch-browser-sdk packaged in!

I haven't tried this with Next.js but I imagine it might work, let me know.

phillipmalboeuf commented 5 years ago

And for those on next.js, you can do it the same way but you'll need to add a webpack alias resolve to your next.config.js file, like so! :

module.exports = {
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    if (!isServer) { config.resolve.alias = {
      ...config.resolve.alias,
      'mongodb-stitch-server-sdk': 'mongodb-stitch-browser-sdk'
    } }
    return config;
  }
};

Let me know!

adamchel commented 5 years ago

Hey @phillipmalboeuf thanks for taking a look and finding a workaround for this! I'm sure this will be very helpful for a lot of users.

We still would like to eventually have a more clean and robust solution for SSR, so I'll leave this issue open until we get a chance to investigate this further.

gierloff commented 5 years ago

We are using VueJS/NuxtJS for our project. We have a rest api with MongoDB/Mongoose, but we would really like to skip the server part and just use Nuxt with MongoDB Stitch. But we are getting the same error with “self” not defined. “Couldn’t you just use Firebase?” - rather not, because Firebase has annoying data validation and doesn’t accept data types like undefined or class objects. MongoDB stitch does which is really awesome! So we are hoping for a solution to the SSR incompatibility!

Mattertorian commented 5 years ago

I can verify that the workaround @phillipmalboeuf discovered also work with Nuxt js with the following small modification. In the nuxt.config.js file: build: { extend(config, ctx) { if (!ctx.isServer) { config.resolve.alias = { ...config.resolve.alias, 'mongodb-stitch-server-sdk': 'mongodb-stitch-browser-sdk' } } return config; } } Thus the only difference is that the webpack extension is different from Next js. You will still need to replace all import of 'mongodb-stitch-browser-sdk' with 'mongodb-stitch-server-sdk' as well as add the alias in package.json.

Looking forward to the day where the SDK supports SSR.

pnadon commented 5 years ago

I can verify that the workaround @phillipmalboeuf discovered also work with Nuxt js with the following small modification. In the nuxt.config.js file: build: { extend(config, ctx) { if (!ctx.isServer) { config.resolve.alias = { ...config.resolve.alias, 'mongodb-stitch-server-sdk': 'mongodb-stitch-browser-sdk' } } return config; } } Thus the only difference is that the webpack extension is different from Next js. You will still need to replace all import of 'mongodb-stitch-browser-sdk' with 'mongodb-stitch-server-sdk' as well as add the alias in package.json.

Looking forward to the day where the SDK supports SSR.

@Mattertorian I'm curious to see how integrating Stitch into your NuxtJS app worked out for you. I'm also looking to use Stitch with NuxtJS and the Google maps API, so I can send directions to the serve and it can return relevant information from the database.

neonguru commented 5 years ago

I didn't read this whole thread, but I have stitch working with SSR (angular universal), by switching the following package dependencies:

"mongodb-stitch-browser-sdk": "^4.3.2",

To:

"mongodb-stitch-browser-core": "~4.2.0",
"mongodb-stitch-browser-sdk": "~4.2.0",
"mongodb-stitch-browser-services-mongodb-remote": "~4.2.0",
"mongodb-stitch-core-sdk": "~4.2.0",
"mongodb-stitch-core-services-mongodb-remote": "~4.2.0",

4.4.0 still has the problem. Angular Universal uses the same source on client and server, but I filter out outside calls like stitch on the server, and defer them till it gets to the client. The problem with the newer versions appears to be the that stitch devs added a dependency:

"whatwg-fetch": "^3.0.0"

to mongodb-stitch-browser-core. If they can get by with the ^2.0.4 version, then they can circumvent that libraries improper access to the self var, when just loading the library.

For a fetch issue reference see: https://github.com/github/fetch/issues/657

api-pushstart commented 4 years ago

I am trying to do this with next.js . Where should I initialize the stitch app? Currently, I am getting error -

default app can only be set once; currently set to 'my app id'

ivanjeremic commented 4 years ago

I think the way stitch currently works needs to be changed, It needs to be 100% API and no SDK, So Login/Registration and so on needs to have a REST endpoints out of the box, (If GraphQL activated Mutations out of the box for Login, Register etc...)

daveteu commented 4 years ago

I am trying to do this with next.js . Where should I initialize the stitch app? Currently, I am getting error -

default app can only be set once; currently set to 'my app id'

Need to understand if you use any of the build in authentication methods in Stitch, you can conclude it's easier to do it client side, and load the sdk using nextjs dynamic loading. Stitch maintains the session in order to provide you with all the authenticated goodies like customised access to the functions and attached clusters.

If you really need SSR option, the easiest way for you is to do custom authentication, and send to Stitch for custom JWT authentication. Don't waste development time trying for workarounds.

Stitch is serverless, I ditched SSR to go full serverless. Response time for Client > Stitch > Atlas > Stitch > Client is much faster than Client > NodeJs > Stitch > Atlas > Stitch > NodeJS > Client.

ivanjeremic commented 4 years ago

I have not tested it yet but hitting the Stitch Client API directly should solve many problems in Nextjs, https://github.com/mongodb/stitch-specifications/blob/master/source/sdks/sdks.rst#userpasswordauthproviderclient.

We could create Nextjs API routes in the /api folder, for example, nextapp.com/api/login

In this route, we send a request to https://stitch.mongodb.com/api/client/v2.0/app//auth/providers//login

the response will be access_token, refresh_token, user_id, device_id that we can handle then, has anyone an idea what the best method would be at this point to store or handle it?

mattjennings commented 4 years ago

@ivanjeremic I've been looking everywhere for all of the auth API endpoints, thank you! These should be in the official documentation.

As for where to store it, I've been storing them in cookies. I store the user data, access_token and expiry date in one cookie, and the refresh_token in an http-only cookie. Then, I setup a /refresh-token endpoint that my client hits whenever it expires to get the new access_token (and updates the cookie).

You can refresh the token by sending a post request to https://stitch.mongodb.com/api/client/v2.0/auth/session with an Authorization: "Bearer <refresh token>" header.

ivanjeremic commented 4 years ago

@ivanjeremic I've been looking everywhere for all of the auth API endpoints, thank you! These should be in the official documentation.

As for where to store it, I've been storing them in cookies. I store the user data, access_token and expiry date in one cookie, and the refresh_token in an http-only cookie. Then, I setup a /refresh-token endpoint that my client hits whenever it expires to get the new access_token (and updates the cookie).

You can refresh the token by sending a post request to https://stitch.mongodb.com/api/client/v2.0/auth/session with an Authorization: "Bearer <refresh token>" header.

The documentation is really horrible in Stitch, no idea how they want to compete with Strapi or Hasura with no community chat & bad documentation, if after rebranding to Realm nothing gets better I don't see it becoming a thing. All they have is a Forum where you need the approval of your posts, they need a chat on Discord or Slack.