What is the best way to add user login information to the request pipeline in Express.js?

In my current project, I've been working on a middleware that is responsible for extracting the user model and attaching it to the request pipeline. Although I have successfully implemented a token extractor middleware that attaches the token to the request pipeline without any issues, I encountered a problem when trying to extract the user model.

When testing inside the middleware function, everything works as expected. However, when I attempt to access the user model within my controller, it returns as undefined. Here's what I have attempted so far:

utils/middleware.js

const tokenExtractor = async (request, response, next) => {    
        const authorization = await request.get('authorization');
       if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
         request.token = authorization.substring(7);         
       } else{
         request.token = null;
        }
        next();
};
const userExtractor = async (request, response, next) => {  
  tokenExtractor(request, response, next);
  if(request.token){
    const decodedToken = jwt.verify(request.token, process.env.SECRET);    
    request.user = await User.findById(decodedToken.id);
    console.log(request.user); // Works
    next();
  } else{
    response.status(403).json({ error: 'no token received' });
  }
};

The issue arises within my controllers:

controllers/blogs.js

blogRouter.post("/", async (request, response, next) => {
  if (request.body.title && request.body.url) {
    const token = request.token;    
    if (!token) {
      return response.status(401).json({ error: 'invalid token' });
    }
      
    console.log(request.user); // undefined !
    if(!request.user){
      return response.status(401).json({ error: 'invalid user' });
    }
    
    const user = request.user;    
    
    const blog = new Blog({
      title: request.body.title,
      author: request.body.author,
      url: request.body.url,
      likes: request.body.likes,
      user: user._id,
    });

    
    await blog.save();    
    
    user.blogs = user.blogs.concat(blog._id);
    await user.save();
    
    response.status(201).json(blog);
  }
  response.status(400).end();
});

Both middleware functions are already connected to the express app.

EDIT:

To resolve the issue, I removed the call to tokenExtractor from the userExtractor function and instead chained the middleware to the router. This ensured that the userExtractor was called before the blogRouter, preventing the undefined user model error.

app.js

// app.use(tokenExtractor);
app.use(requestLogger);
app.use(errorHandler);
// app.use(userExtractor);

app.use('/api/login', tokenExtractor, loginRouter);
app.use('/api/users', usersRouter);
app.use('/api/blogs', tokenExtractor, userExtractor, blogRouter); // chaining the extractors

Answer №1

Allowing next() to pass on the (req, res, next) instances seamlessly acts like a smooth flowing pipe. It eliminates the necessity for any workarounds and enables stacking multiple middlewares effortlessly, even permitting the reuse of values between them - provided the sequence of the call stack is reliable.

Answer №2

There is no need to chain it together. Just specify the callback argument for the next middleware function in the following way.

const tokenExtractor = (req, res, next) => {
  const authentication = req.get('authorization')
  if (authentication && authentication.toLowerCase().startsWith('bearer ')) {
    req.token = authentication.substring(7)
    next()
  } else {
    next()
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering a top-level-await issue while utilizing the NextJS API

Currently, I am in the process of creating an API using NextJS and MongoDB. To start off, I have set up some basic code at the beginning of the API file: const { db } = await connectToDatabase(); const scheduled = db.collection('scheduled'); Fol ...

When a user clicks on a specific element's id, the image will rotate accordingly

When the elements in ul are clicked, the image rotates. The default position of the image is already rotated by a certain number of degrees, and on each click, it rotates to the desired value. This was achieved using the following code: $("#objRotates"). ...

An error notification received from the command "jspm install jquery"

As I follow the tutorial on the jspm.io site, everything goes smoothly until I reach step 3. When I try to execute jspm install jquery, an error message pops up. The error reads: warn Error on getOverride for jspm:github, retrying (2). ReferenceError: ui ...

Are there any specific steps I should take to ensure that my server can support jQuery.getJSON when using a bookmarklet?

Currently, I am in the process of creating a bookmarklet that will require some user details to be input. After researching my options for cross domain communication, I have found that my best choices are either using jQuery.getJSON or adding a form and i ...

Angular.js image slider display for viewing photos

Exploring the possibility of incorporating an open source widget to showcase images on a webpage, specifically leveraging angular.js. After conducting a search query on Google for "Angular.js photo carousel" or "angular.js photo viewer," I discovered only ...

Avoiding duplicate messages in client-side communication using Express.js and Socket.io

As a newcomer to Node, I am currently working on implementing chat functionality using socket.io. However, I'm facing an issue where my client (browser) receives two messages whenever an event is triggered. app.js var express = require('express& ...

Unable to retrieve information from localhost site using the expressjs API. I have attempted to use both vue-resource and axios in order to get the data without success

Currently diving into the world of VueJS, I decided to embark on a project. My aim is to retrieve data from an ExpressJS server/API. But unfortunately, both vue-resource and axios have been returning status code 0. It seems like my API might not be handli ...

Cypress - A Guide to Efficiently Waiting for the Outcome of a Javascript Function Import

I am interested in creating a Javascript library to act as a wrapper for 3rd party APIs. I have decided to write the API wrapper as a standalone file rather than using Cypress Custom functions, so that I can share the library with teams who are not using C ...

Preventing Double Click Events on jQuery Spinner

I have been working on an option picker, but now there is a new requirement to make the options configurable. While this shouldn't be too difficult, I am facing some issues with the option picker: Currently, when an item is double-clicked, it will ge ...

Transforming a set of properties into an organized array

Looking to transform an object literal that contains inner objects with a "rank" key holding floating point values into an array of these inner objects, sorted by the "rank" value. Input Object: { 452:{ bla:123, dff:233, rank:2 }, 234:{ ...

Swapping out data points using JQuery

What could be causing line 10 to return null? Click here for the code file The code seems to function properly with line 40, but not with line 10. ...

Invoking a function from a separate JavaScript file and finding out that an object is considered null

The source code for a word game is stored in Main.js file. Currently, I am attempting to introduce another file called Bookmarks.js (included on the web page prior to the Main.js file). This new file will contain an object var bookmarks = {}; that stays s ...

Mastering Typing for Enhanced Order Components using Recompose and TypeScript

I have been working on integrating recompose into my react codebase. As part of this process, I have been experimenting with getting some basic functionality to work. While I have made progress, I am uncertain if I am following the correct approach for usi ...

Enhancing the templateUrl with additional value in AngularJS and PHP

I am having trouble capturing the value of the {fold} and adding it to the templateUrl. The php file show.person.php is returning an error because it is only recognizing 'id' as '{fold}' and not as a number like '54' In my co ...

Customizing material-ui styles within a nested component

I am looking to modify the position of the expandIcon in an ExpansionPanel by changing the right attribute: <ExpansionPanel> <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}> <Typography className={classes.heading}&g ...

Verify if a user possesses administrative privileges

I'm facing an issue where I want to grant router access to users with admin privileges. However, I keep encountering an error stating that isAdmin is not defined, even though it is present in the database. function checkAdmin(req, res, next) { ...

What is the best HTTP method to utilize when deleting a sub-document from a MongoDB database?

When working with MongoDB sub-documents, which HTTP verb is appropriate for removing a specific sub-document? Let's consider the following data example: test: 'some value', rooms: [ { id: '1' colour: 'brown ...

Refreshing the Span Element using Ajax and php

Hello there, Stack Overflow community! I have a basic PHP script (countsomething.php) that retrieves a number and displays it using echo. How can I use AJAX to automatically update a simple span element on my HTML page? I've attempted to trigger th ...

Ensure that react-native-google-places-autocomplete is assigned a specific value rather than relying on the default value

I am currently using a functional <TextInput>: <TextInput placeholder="Location" value={props.locationInput.toString()} onChangeText={location => props.updateLocationInput(location)} /> Initially, the props.locationIn ...

Searching and sorting with MongoDB's full-text capabilities in PHP

My current task involves conducting a search using full text index and the following code is what I am working with: $cursor=$collection->find(array('$text'=>(array('$search'=>$s))), array("score"=> arra ...