Open xxyyzz2050 opened 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.
ok, I use Nuxt framework & latest version of Node.js and npm
@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.
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'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?
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...
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
.
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
+1 for SSR support
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?
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
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
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.
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:
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 !
Oh interesting @adamchel, didn't realize the withTransport
/withStorage
configurations existed! Will take a look and keep you updated.
@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:(
Is there a way to use StitchAppClientConfiguration
without having to use mongodb-stitch-server-core
or mongodb-stitch-browser-core
?
What are the SSR issues when just using mongodb-stitch-server-core
or mongodb-stitch-browser-core
? Is it the dependencies?
@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.
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.
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!
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.
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!
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.
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.
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
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'
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...)
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.
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/
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?
@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 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 anAuthorization: "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.
I'm using babel transpiler
//error: window is not defined