iaincollins / nextjs-starter

A starter project for Next.js with authentication
https://nextjs-starter.now.sh
ISC License
1.37k stars 423 forks source link

How to get user object on the server when send ajax request via getInitialProps? #83

Closed michael-land closed 6 years ago

michael-land commented 6 years ago

Hello,

I am trying to fetch user bookmarks from the server.

when send request on the client side, we can access user object by req.user. Is there any way to get access to the user object when send request via getInitialProps.

Thanks.

// model/user.js

const UserSchema = new mongoose.Schema({
  _id: mongoose.Schema.Types.ObjectId,
  bookmarkIds: [{ref:'Bookmark', ...}]
  ......
});
//pages/index.js

export default class extends Page {
  static async getInitialProps({req}) {
    const props = await super.getInitialProps({ req });

    if (isServer) {
      fetch('http://localhost:3000/api/test', { method: 'GET', credentials: 'include' })
        .then((res) => res.json())
        .then(console.log);
    }
    return props
  }
}
routes/test.js

module.exports = (expressApp) => {
  expressApp.get('/api/test', (req, res) => {

    console.log(req.user)        // undefined

    const result = {}            // fetch user bookmarks...

    res.send(result);
  });
};
iaincollins commented 6 years ago

Hi there,

Hmm off the top of my head I would expect that to work - you have credentials: "include" so that looks fine and req.user should be populated.

You might want to check you are adding the routes to express in the right place (e.g. after NextAuth routes have been setup in your apps main file (e.g. index.js).

It may help to look at routes/admin.js as it does the same thing - checking req.user to see if the user is logged in. This page is called from pages/admin.js and is normally only available if an 'admin' property is set to true on a User account.

michael-land commented 6 years ago

Thanks. I checked out the links you provide (pages/admin), I noticed that you call this.update() inside the componentDidMount hook.

Can it works within getInitialProps when a page is loaded server side.

static async getInitialProps({req}) {
    const props = await super.getInitialProps({ req });

    if (isServer) {
    // Session is not available on the server
      fetch('http://localhost:3000/api/test', { method: 'GET', credentials: 'include' })
        .then((res) => res.json())
        .then(console.log);
    }
    return props
  }
iaincollins commented 6 years ago

You should be able to inspect req directly (e.g. req.user or req.session) in getInitialProps() when running server side.

That is actually what the NextAuth package does - and is how it works both server and client side.

For reference, the only reason to to also call a method in componentDidMount() is because getInitialProps() blocks page rendering. While we have to block to render pages server side - if we are running client side we avoid blocking page rendering and just use an Ajax call to lazy load data.

Note: The admin demo should really run both server and client side (and so have code in getInitialProps()), but I was a bit lazy with it so it only works client side as getting the BootstrapTable code to work with server side rendering seemed a hassle for a feature that isn't really finished. I'll try and improve it at some point.

I hope this helps. If not please do feel free to ask! Good luck, am interested to know how you get on :-)

michael-land commented 6 years ago

I think I went the wrong direction. I was trying to access req.user in my server side request handler. The face is that the req in callback and req in getInitialProps() are two different instances.

I Got It, Thank You

iaincollins commented 6 years ago

Oh good, I'm glad it's working! Thanks for raising and for following up.

It sounds like it might be helpful to add an example of this to the project.

That the req object in an Express route handler and req object in Next.js are similar but not exactly the same is sure to catch people off guard. What the req object looks like also matters if you declare it before or after including auth routes (e.g. NextAuth, Passport, etc).

ghost commented 6 years ago

Can't we access req.user in the server-side request handler somehow?

iaincollins commented 6 years ago

Yes!

req.user and req.session should both be populated in getInitialProps() on React Pages when running on the server, if a user is logged in (this is what next-auth does).

The NextAuth.init() method is passed the req param in components/pages.js on every page load. Of course it's null when doing server side rendering, and that tells NextAuth it's running on the client and to do an AJAX call to load the session details instead (if it they are not already cached).

You can access req.user in Express route handlers too, just be sure to define your own Express routes after initialising NextAuth.

MaffooBristol commented 5 years ago

I'm getting an undefined req.user when calling my custom express route from the server, although it works fine when coming from the client. My routes are definitely defined in the right place within the nextapp bootstrapping code and so should be populated. I assume that the credentials or useCredentials flags only apply for the browser?

Even funnier is that req.session does exist. However it cycles a new one every time it's called, so basically not picking up any authentication.

It all seems to look fine inside the req object in getInitialProps(), but the express routes, no dice.

Any ideas? You say that in certain instances it can be null... but completely undefined?

(Ps, thanks in advance!)