dasJulian
dasJulian

Reputation: 577

Express: Route is not created inside async function

This is the api.js module which creates a test route:

'use strict';

module.exports = function (app) {
  
  console.log("before route creation");
  app.get("/api/test", (req, res) => {
    res.send("it worked");
  });
  console.log("routes created");
};

In the server.js file, I am importing this module as apiRoutes. Then, I am calling it inside an async function.

const databaseConnection = async (apiRoutes, app) => {
  try {
    await mongoose.connect(`mongodb+srv://replitUser:${process.env.DB_PW}@issuetracker.pbbm6.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log("db connection successful");

    //Routing for API 
    console.log("apiRoutes called");
    apiRoutes(app);  

  } catch (err) {
    console.log("an err occurred", err);
  }
}
databaseConnection(apiRoutes, app);
// apiRoutes(app);

The strings "before route creation" and "routes created" are logged to the console. However, the route does not seem to work, although no errors are occurring.

If I call apiRoutes outside of the async function, like here:

const databaseConnection = async (apiRoutes, app) => {
  try {
    await mongoose.connect(`mongodb+srv://replitUser:${process.env.DB_PW}@issuetracker.pbbm6.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log("db connection successful");

    //Routing for API 
    // console.log("apiRoutes called");
    // apiRoutes(app);  

  } catch (err) {
    console.log("an err occurred", err);
  }
}
databaseConnection(apiRoutes, app);
apiRoutes(app);

...it will create the test route successfully.

I've tried to create the route directly inside of the async function and not in a new module, and it changed nothing, the route is still not created.

const databaseConnection = async (apiRoutes, app) => {
  try {
    await mongoose.connect(`mongodb+srv://replitUser:${process.env.DB_PW}@issuetracker.pbbm6.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log("db connection successful");

    //Routing for API 
    // console.log("apiRoutes called");
    // apiRoutes(app);  
    app.get("/api/test", (req, res) => {
      res.send("it worked");
    });
  } catch (err) {
    console.log("an err occurred", err);
  }
}
databaseConnection(apiRoutes, app);

Why can't I create routes inside of an async function?

Here is a link to the project on replit - Feel free to fork

Upvotes: 0

Views: 416

Answers (2)

dasJulian
dasJulian

Reputation: 577

At first, I thought that using express.Router() was an easy fix. Here is what this would have looked like:

Create a router and mount it to the /api path:

const router = express.Router();
app.use("/api", router);

Then, create the route with router.get() instead of app.get().

const databaseConnection = async (router) => {
  try {
    await mongoose.connect(`mongodb+srv://replitUser:${process.env.DB_PW}@issuetracker.pbbm6.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
    console.log("db connection successful");

    //Routing for API 
    router.get("/test", (req, res) => {
      res.send("it worked");
    });
  } catch (err) {
    console.log("an err occurred", err);
  }
}
databaseConnection(router);

Here is a link to the complete version of this

But as user @num8er mentioned, this structure is bad practice and unnecessary.

I didn't understand that there is no need to create the routes after the database connection. Mongoose lets you use models before the database is connected because mongoose buffers these database calls internally. The server just needs to listen after the database connection.

So now I've implemented @num8er's thoughts and updated the project!

I've got a db.js module, which exports a connect function that returns a promise:

const mongoose = require("mongoose");

module.exports = {
  connect: function() {
    const dsn = `mongodb+srv://replitUser:${process.env.DB_PW}@cluster0.m31tz.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`;
    return mongoose.connect(dsn);
  },
}

In the server.js file, I create the routes just by executing the api.js module:

const apiRoutes = require("./routes/api.js");
const router = express.Router();
app.use("/", router);
apiRoutes(router);

And then I start listening after I'm connected to the database:

(async () => {
  const db = require("./db");
  try {
    await db.connect();
    console.log("connected to db");
  } catch (err) {
    console.log("an error occurred while connecting to db", err);
  }

  //Start our server and tests!
  const listener = app.listen(process.env.PORT || 3000, function () {
    console.log("Your app is listening on port " + listener.address().port);
    if (process.env.NODE_ENV === "test") {
      console.log("Running Tests...");
      setTimeout(function () {
        try {
          runner.run();
        } catch (e) {
          console.log("Tests are not valid:");
          console.error(e);
        }
      }, 5000);
    }
  });
})();

Here is a link to the complete fixed project on replit

Attention: There is a problem with the chai tests on replit, as explained here. However, if you download the files and run the server locally, everything works fine.

Upvotes: 0

num8er
num8er

Reputation: 19372

I know it's not direct answer to Your question.

But the problem is that You cannot structure Your code correctly.

So in below You can see app structure with separate db and app module where app starts listening after db connection.

server.js

const http = require('http');

const db = require('./db');
const app = require('./app');

const server = http.createServer(app);
const PORT = process.env.PORT || 8000;

(async () => {
  await db.connect();
  console.log('connected to db');

  server.listen(PORT, () => {
    console.log(`app listening at port: ${PORT}`);
  });      
})();

db.js

const mongoose = require('mongoose');

const UserSchema = require('./schemas/User');
mongoose.model('User', UserSchema);

module.exports = {
  connect: function() {
    const dsn = `mongodb+srv://replitUser:${process.env.DB_PW}@issuetracker.pbbm6.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`;
    return mongoose.connect(dsn);
  },
  model: function(name) {
    return mongoose.model(name);
  },
};

app.js

const express = require('express');
const app = express();

const routes = require('./routes');
app.use(routes);

module.exports = app;

routes/index.js

const express = require('express');
const router = express.Router();

const users = require('./users');
router.use('/api/users', users);

module.exports = router;

routes/users.js

const express = require('express');
const router = express.Router();

const db = require('../db');
const User = db.model('User');

router.get('/', async (req, res) => {
  const users = await User.find({}).lean();
  res.status(200).send({users});
});

router.get('/:id', async (req, res) => {
  const user = await User.findById(req.params.id).lean();
  if (!user) {
    return res.status(404).end();
  }
  res.status(200).send(user);
});

module.exports = router;

schemas/User.js

const mongoose = require('mongoose');
const {Schema} = mongoose;

const UserSchema = new Schema({
  username: Schema.Types.String,
  password: Schema.Types.String,
  name: Schema.Types.String,
});

module.exports = UserSchema;

Upvotes: 1

Related Questions