Search code examples
node.jsexpressmiddlewareendpoint

Correct way to utilize middleware functions in Express


Hi im creating a proxy endpoint using express with node js, i have it working correctly for one get request, but im wanting to setup multiple get/post request endpoints and wondering if what i have done below is the correct way to carry on to implement the other endpoints, im pretty new with NodeJS and express.

what ive tried is reusing

app.use(validateFirebaseIdToken);

and then app.use(new functionname) before i call the new get Endpoint, i need to reuse the decodedIDToken variable in each of my bigQuery methods, bear in mind there are going to be alot of different bigQuery query methods i will be creating to get data for the end user

var express = require('express')`
var app = express()

const validateFirebaseIdToken = async (req, res, next) => {`
console.log('Check if request is authorized with Firebase ID token');

if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&!(req.cookies && req.cookies.__session)) {
    console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
    'Make sure you authorize your request by providing the following HTTP header:',
    'Authorization: Bearer <Firebase ID Token>',
    'or by passing a "__session" cookie.');
    res.status(403).send('Unauthorized');
    return;
}

let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
    console.log('Found "Authorization" header');
    // Read the ID Token from the Authorization header.
    idToken = req.headers.authorization.split('Bearer ')[1];
} else {
// No cookie
   res.status(403).send('Unauthorized');
   return;
}

try {
   const decodedIdToken = await admin.auth().verifyIdToken(idToken);
   console.log('ID Token correctly decoded', decodedIdToken);
   req.user = decodedIdToken;
   next();
   return;
} catch (error) {
   console.error('Error while verifying Firebase ID token:', error);
   res.status(403).send('Unauthorized');
   return;
} 
};

const runDailyCategorybigQuery = async (req, res, next) => {

const query = `select count(string_field_3) as Categories, Month(date_field_2) as Month from test.testing_api group by Month`;

const options = {
   query: query,
   // Location must match that of the dataset(s) referenced in the query.
   location: 'US',
   useLegacySql: true,
};

// Run the query as a job
const [job] = await bigquery.createQueryJob(options);
console.log(`Job ${job.id} started.`);
console.log(`ID ${req.user.user_id}`);

// Wait for the query to finish
const [rows] = await job.getQueryResults();
res.query = rows;
console.log('Rows:');
rows.forEach(row => console.log(row));
next();

};

const runCategoryMonthsbigQuery = async (req, res, next) => {

const query = `select count(string_field_3) as Categories, Month(date_field_2) as Month from test.testing_api group by Month`;

const options = {
   query: query,
   // Location must match that of the dataset(s) referenced in the query.
   location: 'US',
   useLegacySql: true,
};

// Run the query as a job
const [job] = await bigquery.createQueryJob(options);
console.log(`Job ${job.id} started.`);
console.log(`ID ${req.user.user_id}`);

// Wait for the query to finish
const [rows] = await job.getQueryResults();
res.query = rows;
console.log('Rows:');
rows.forEach(row => console.log(row));
next();

};
app.use(validateFirebaseIdToken);
app.use(runDailyCategorybigQuery);
app.get('/getCategories', (req, res) => {
res.json(res.query);
});

//what im thinking of doing
app.use(validateFirebaseIdToken);
app.use(runCategoryMonthsbigQuery);
app.get('/getCategoriesMonth', (req, res) => {
res.json(res.query);
});

Solution

  • What you listed as "what I am thinking of doing" will add the same middleware twice (for validateFirebaseIdToken). This is not necessary as middleware will be called on every request, so you should only add it once.

    The second issue is that you are treating your request handlers like middleware. Since each request will be seen by all middleware (as long as they call next) then you will be running both queries and the second one will overwrite the first.

    Move your two query middleware functions into request handlers instead. For example:

    const runDailyCategorybigQuery = async (req, res) => {
      ...
      res.json(res.query);
    }
    
    app.get('/getCategories', runDailyCategorybigQuery);